如何在大型数据帧中快速转换不同的时间格式?

时间:2019-06-25 12:19:18

标签: r performance datetime-format

我想计算不同时间维度的长度,但是我在处理数据帧列中的两种略有不同的时间格式时遇到问题。

原始数据帧列大约有一百万行,两种格式(如示例代码所示)混合在一起。

示例代码:

Sorted Input

格式time <- c("2018-07-29T15:02:05Z", "2018-07-29T14:46:57Z", "2018-10-04T12:13:41.333Z", "2018-10-04T12:13:45.479Z") length <- c(15.8, 132.1, 12.5, 33.2) df <- data.frame(time, length) df$time <- format(as.POSIXlt(strptime(df$time,"%Y-%m-%dT%H:%M:%SZ", tz=""))) df "2018-10-04T12:13:41.333Z"产生"2018-10-04T12:13:45.479Z"

是否存在一种适用于将两种格式混合使用的大数据框的解决方案?

3 个答案:

答案 0 :(得分:3)

我们可以使用%OS代替%S来计算秒的小数。

help("strptime")
  

特定于R的是%OSn,对于输出,该秒数将被截断为   0 <= n <= 6个小数位(如果%OS后没有数字,则它   使用getOption(“ digits.secs”)的设置,或者如果未设置,则n =   0)。

as.POSIXct(time, format="%Y-%m-%dT%H:%M:%OSZ")
# [1] "2018-07-29 15:02:05 CEST" "2018-07-29 14:46:57 CEST"
# [3] "2018-10-04 12:13:41 CEST" "2018-10-04 12:13:45 CEST"

此基本R代码比软件包解决方案快 ,您可以自己尝试。

更新1

time2 <- c("2018-09-01T12:42:37.000+02:00", "2018-10-01T11:42:37.000+03:00")

这个比较棘手。 ?strptime说我们应该使用%z来抵消UTC的偏移量,但是在某种程度上,它不适用于as.POSIXct。相反,我们可以这样做,

as.POSIXct(substr(time2, 1, 23), format="%Y-%m-%dT%H:%M:%OS") + 
  {os <- as.numeric(el(strsplit(substring(time2, 24), "\\:")))
  (os[1]*60 + os[2])*60}
# [1] "2018-09-01 14:42:37 CEST" "2018-10-01 13:42:37 CEST"

从字符串中剪切出不可读的部分,将其转换为秒,然后将其添加到"POSIXct"对象中。

如果像time2中只有 小时,我们也可以说:

as.POSIXct(substr(time2, 1, 23), format="%Y-%m-%dT%H:%M:%OS") + 
  as.numeric(substr(time2, 24, 26))*3600
# [1] "2018-09-01 14:42:37 CEST" "2018-10-01 13:42:37 CEST"

现在的代码稍长一点,不会掩盖它实际上与答案顶部的运行速度一样快的事实。

更新2

您可以将当前的三个变体包装到具有if (nchar(x) == 29) ... else结构的函数中,例如这样一个:

fixDateTime <- function(x) {
  s <- split(x, nchar(x))
  if ("20" %in% names(s))
    s$`20` <- as.POSIXct(s$`20` , format="%Y-%m-%dT%H:%M:%SZ")
  else if ("24" %in% names(s))
    s$`24` <- as.POSIXct(s$`24`, format="%Y-%m-%dT%H:%M:%OSZ")
  else if ("29" %in% names(s))
    s$`29` <- as.POSIXct(substr(s$`29`, 1, 23), format="%Y-%m-%dT%H:%M:%OS") + 
      {os <- as.numeric(el(strsplit(substring(s[[3]], 24), "\\:")))
      (os[1]*60 + os[2])*60}
  return(unsplit(s, nchar(x)))
}

res <- fixDateTime(time3)
res
# [1] "2018-07-29 15:02:05 CEST" "2018-10-04 00:00:00 CEST" "2018-10-01 00:00:00 CEST"
str(res)
# POSIXct[1:3], format: "2018-07-29 15:02:05" "2018-10-04 00:00:00" "2018-10-01 00:00:00"

与软件包相比,只有fixDateTime可以处理所有三种定义的日期时间类型。根据最终基准,该功能仍然非常快。

注意: :如果不同的日期格式具有相同的nchar,则该函数在逻辑上会失败,因此应在这种情况下进行自定义(例如,由另一个{{ 1}}条件)!未测试:向split添加秒数时的夏令时行为。

基准

POSIXct

数据

# Unit: milliseconds
#        expr       min        lq      mean    median        uq       max neval  cld
# fixDateTime  35.46387  35.94761  40.07578  36.05923  39.54706  68.46211    10   c 
#  as.POSIXct  20.32820  20.45985  21.00461  20.62237  21.16019  23.56434    10  b   # to compare
#   lubridate  11.59311  11.68956  12.88880  12.01077  13.76151  16.54479    10 a    # produces NAs! 
#     anytime 198.57292 201.06483 203.95131 202.91368 203.62130 212.83272    10    d # produces NAs!

基准代码

time <- c("2018-07-29T15:02:05Z", "2018-07-29T14:46:57Z", "2018-10-04T12:13:41.333Z", 
"2018-10-04T12:13:45.479Z")
time2 <- c("2018-07-29T15:02:05Z", "2018-07-29T15:02:05Z", "2018-07-29T15:02:05Z") 
time3 <- c("2018-07-29T15:02:05Z", "2018-10-04T12:13:41.333Z", 
           "2018-10-01T11:42:37.000+03:00") 

答案 1 :(得分:2)

您可以使用库anytime

    library(anytime)
    time<- c("2018-07-29T15:02:05Z",
             "2018-07-29T14:46:57Z",
             "2018-10-04T12:13:41.333Z",
             "2018-10-04T12:13:45.479Z")
    anytime(time)
#[1] "2018-07-29 15:02:05 CEST" "2018-07-29 14:46:57 CEST" "2018-10-04 12:13:41 CEST" "2018-10-04 12:13:45 CEST"

答案 2 :(得分:2)

或者您也可以使用:

time<- c("2018-07-29T15:02:05Z",
         "2018-07-29T14:46:57Z",
         "2018-10-04T12:13:41.333Z",
         "2018-10-04T12:13:45.479Z")

length<-c(15.8,132.1,12.5,33.2)

df<-data.frame(time,length)
library(lubridate)

# df$time2<-as_datetime(df$time)
df$time2 <-parse_date_time(df$time, "ymd_HMS") 
df