【问题标题】:Efficient comparison of POSIXct in data.tabledata.table 中 POSIXct 的高效比较
【发布时间】:2013-03-27 15:45:47
【问题描述】:

您好,我正在寻找一种从data.table 中选择POSIXct 行的有效方法,这样一天中的时间就少于12:00:00(注意不需要毫秒,所以我们可以使用ITime例如)

set.seed(1); N = 1e7;
DT = data.table(dts = .POSIXct(1e5*rnorm(N), tz="GMT"))
DT
                               dts
#       1: 1969-12-31 06:35:54.618925
#       2: 1970-01-01 05:06:04.332422
#     ---                           
# 9999999: 1970-01-03 00:37:00.035565
#10000000: 1969-12-30 08:30:23.624506

一个解决方案(这里的问题是,如果 N 很大,演员表可能会很昂贵)

f <- function(t, st, et) {time <- as.ITime(t); return(time>=as.ITime(st) & time<=as.ITime(et))}
P <- function(t, s) { #geekTrader solution
    ep <- .parseISO8601(s) 
    if(grepl('T[0-9]{2}:[0-9]{2}:[0-9]{2}/T[0-9]{2}:[0-9]{2}:[0-9]{2}', s)){
        first.time <- as.double(ep$first.time)
        last.time <- as.double(ep$last.time)-31449600
        SecOfDay <- as.double(t) %% 86400
        return(SecOfDay >= first.time & SecOfDay <= last.time )
    } else {
        return(t >= ep$first.time & t <= ep$last.time)    
    }
}

快速了解性能

system.time(resf <- DT[f(dts,'00:00:00','11:59:59')])
   user  system elapsed 
   1.01    0.28    1.29
system.time(resP <- DT[P(dts,'T00:00:00/T11:59:59')])
   user  system elapsed 
   0.64    0.13    0.76 

identical(resf,resP)
[1] TRUE

【问题讨论】:

  • 您是否乐意创建一个itime 列并以此为关键字?
  • @mnel: 是的,所以我们进行二分搜索...
  • 你真的不应该在你的问题中编辑别人的答案
  • 我明白了……这是为什么呢?看起来更适合我理解......
  • @statquant :只是好奇为什么里奇的答案会获得赏金?

标签: r time-series data.table posixct subset


【解决方案1】:
 P <- function(t, s) {
  ep <- .parseISO8601(s)

  if(grepl('T[0-9]{2}:[0-9]{2}:[0-9]{2}/T[0-9]{2}:[0-9]{2}:[0-9]{2}', s)){
    first.time <- as.double(ep$first.time)
    last.time <- as.double(ep$last.time)-31449600
    SecOfDay <- as.double(t) %% 86400
    return(SecOfDay >= first.time & SecOfDay <= last.time )

  } else {
    return(t >= ep$first.time & t <= ep$last.time)    
  }

}

F <- function(t, st, et) {
  time <- as.ITime(t) 
  return(time>=as.ITime(st) & time<=as.ITime(et))
}


 Sys.setenv(TZ='GMT')
 N = 1e7;
 set.seed(1);

 DT <- data.table(dts = .POSIXct(1e5*rnorm(N), tz="GMT"))


 system.time(resP <- DT[P(dts, 'T00:00:00/T12:00:00'), ])
##   user  system elapsed 
##   1.11    0.11    1.22 
 system.time(resF <- DT[F(dts,'00:00:00','12:00:00')])
##   user  system elapsed 
##   2.22    0.29    2.51 

 resP
##                         dts
##      1: 1969-12-31 06:35:54
##      2: 1970-01-01 05:06:04
##      3: 1969-12-31 00:47:17
##      4: 1970-01-01 09:09:10
##      5: 1969-12-31 01:12:33
##     ---                    
##5000672: 1970-01-01 06:08:15
##5000673: 1970-01-01 05:02:27
##5000674: 1969-12-31 02:25:24
##5000675: 1970-01-03 00:37:00
##5000676: 1969-12-30 08:30:23
 resF
##                         dts
##      1: 1969-12-31 06:35:54
##      2: 1970-01-01 05:06:04
##      3: 1969-12-31 00:47:17
##      4: 1970-01-01 09:09:10
##      5: 1969-12-31 01:12:33
##     ---                    
##5000672: 1970-01-01 06:08:15
##5000673: 1970-01-01 05:02:27
##5000674: 1969-12-31 02:25:24
##5000675: 1970-01-03 00:37:00
##5000676: 1969-12-30 08:30:23

 #Check the correctness
 resP[,list(mindts=max(dts)),by=list(as.Date(dts))]
##       as.Date              mindts
## 1: 1969-12-31 1969-12-31 12:00:00
## 2: 1970-01-01 1970-01-01 12:00:00
## 3: 1969-12-29 1969-12-29 12:00:00
## 4: 1970-01-02 1970-01-02 12:00:00
## 5: 1969-12-30 1969-12-30 12:00:00
## 6: 1970-01-03 1970-01-03 12:00:00
## 7: 1970-01-04 1970-01-04 11:59:59
## 8: 1970-01-05 1970-01-05 11:59:45
## 9: 1969-12-28 1969-12-28 12:00:00
##10: 1969-12-27 1969-12-27 11:59:21
##11: 1970-01-06 1970-01-06 10:53:21
##12: 1969-12-26 1969-12-26 10:15:03
##13: 1970-01-07 1970-01-07 08:21:55
 resF[,list(mindts=max(dts)),by=list(as.Date(dts))]
##       as.Date              mindts
## 1: 1969-12-31 1969-12-31 12:00:00
## 2: 1970-01-01 1970-01-01 12:00:00
## 3: 1969-12-29 1969-12-29 12:00:00
## 4: 1970-01-02 1970-01-02 12:00:00
## 5: 1969-12-30 1969-12-30 12:00:00
## 6: 1970-01-03 1970-01-03 12:00:00
## 7: 1970-01-04 1970-01-04 11:59:59
## 8: 1970-01-05 1970-01-05 11:59:45
## 9: 1969-12-28 1969-12-28 12:00:00
##10: 1969-12-27 1969-12-27 11:59:21
##11: 1970-01-06 1970-01-06 10:53:21
##12: 1969-12-26 1969-12-26 10:15:03
##13: 1970-01-07 1970-01-07 08:21:55

现在是一些不错的 xts 样式子集的演示

 DT[P(dts, '1970')]
##                         dts
##      1: 1970-01-01 05:06:04
##      2: 1970-01-02 20:18:48
##      3: 1970-01-01 09:09:10
##      4: 1970-01-01 13:32:22
##      5: 1970-01-01 20:30:32
##     ---                    
##5001741: 1970-01-02 15:51:12
##5001742: 1970-01-03 01:41:31
##5001743: 1970-01-01 06:08:15
##5001744: 1970-01-01 05:02:27
##5001745: 1970-01-03 00:37:00
 DT[P(dts, '197001')]
##                         dts
##      1: 1970-01-01 05:06:04
##      2: 1970-01-02 20:18:48
##      3: 1970-01-01 09:09:10
##      4: 1970-01-01 13:32:22
##      5: 1970-01-01 20:30:32
##     ---                    
##5001741: 1970-01-02 15:51:12
##5001742: 1970-01-03 01:41:31
##5001743: 1970-01-01 06:08:15
##5001744: 1970-01-01 05:02:27
##5001745: 1970-01-03 00:37:00
 DT[P(dts, '19700102')]
##                         dts
##      1: 1970-01-02 20:18:48
##      2: 1970-01-02 17:59:38
##      3: 1970-01-02 07:14:53
##      4: 1970-01-02 02:13:03
##      5: 1970-01-02 01:31:37
##     ---                    
##1519426: 1970-01-02 11:25:24
##1519427: 1970-01-02 10:00:21
##1519428: 1970-01-02 05:21:25
##1519429: 1970-01-02 05:11:26
##1519430: 1970-01-02 15:51:12
 DT[P(dts, '19700102 00:00:00/19700103 12:00:00')]
##                         dts
##      1: 1970-01-02 20:18:48
##      2: 1970-01-02 17:59:38
##      3: 1970-01-02 07:14:53
##      4: 1970-01-02 02:13:03
##      5: 1970-01-02 01:31:37
##     ---                    
##1785762: 1970-01-02 05:21:25
##1785763: 1970-01-02 05:11:26
##1785764: 1970-01-02 15:51:12
##1785765: 1970-01-03 01:41:31
##1785766: 1970-01-03 00:37:00

 #Check the correctness again
 DT[P(dts, '19700102 00:00:00/19700103 12:00:00'), max(dts)]
##[1] "1970-01-03 12:00:00 GMT"
 DT[P(dts, '19700102 00:00:00/19700103 12:00:00'), min(dts)]
##[1] "1970-01-02 00:00:00 GMT"

【讨论】:

  • 无论对错,我倾向于使用 HHMMSS 存储为纯整数;即没有时代,没有阶级。
  • 不错。将时间存储为秒和%% 86400 似乎是一个不错的直接解决方案,而且很可能很快。
  • @statquant 在我们将as.integer 替换为as.double 后似乎工作正常
  • 干得好,一模一样。但这在闰年有效吗?因为那些年有超过 86400 秒...
  • 它也适用于闰年。 POSIXct 忽略闰秒。在文档中的某处阅读
【解决方案2】:

执行此操作的规范方法是转换为 POSIXlt 并提取小时分量。

hour(as.POSIXlt(DT$dts, "GMT")) < 12

这似乎在性能上与所讨论的其他技术相当(并且更容易理解)。

【讨论】:

  • +1 这肯定是最快的方式吗?如果不是,这对我来说肯定是最容易理解的。它在约 1 秒内将 1e7 行减少到约 5e6 行(我所期望的 rnorm)。并且只需要基本 R。
  • data.table::hour 已经在使用as.POSIXlt,所以你可以写DT[, morning:= (hour(DT$dts) &lt; 12)] 之类的东西。
  • 你们尝试过基准测试吗?我最初的基准测试显示as.POSIXlt 并没有真正提高性能..
【解决方案3】:

这是一种使用 xts 中的一些功能来完成您想要的功能的方法。这不是一个很好的解决方案,因为xts 对象必须按时间排序,但data.table 对象不必如此。此外,它可能不会非常快,因为xtsdata.table 正在完成一些冗余工作。尽管如此,我认为这可能很有趣。

library(data.table)
library(xts)
set.seed(1); N = 1e5;
# I tweaked the following line to make this reproducible in other timezones.
DT = data.table(dts = .POSIXct(1e5*rnorm(N), tz="GMT"))
setkey(DT, dts)  # must sort on time first so that the `xts` object we're about 
                 # to create has the same order
DT[, XTS:=xts(rep(NA, .N), dts)]  # add a dummy xts object as a column
DT[XTS["T00:00:00/T11:59:59.999999", which=TRUE]][, list(dts)] 
                       dts
    1: 1969-12-27 00:28:41
    2: 1969-12-27 00:34:00
    3: 1969-12-27 03:11:21
    4: 1969-12-27 04:20:27
    5: 1969-12-28 00:00:21
   ---                    
49825: 1970-01-05 08:05:22
49826: 1970-01-05 09:35:32
49827: 1970-01-05 09:49:49
49828: 1970-01-05 09:50:27
49829: 1970-01-05 11:07:32

上面使用xts风格的子集字符串来获取每天时间在00:00:00到12:00:00之间的行。使用 which=TRUE 返回行号而不是该行的数据,以便我们可以通过这些行子集 data.table

您可以使用“1970-01-01”之类的字符串来获取当天的所有数据,或使用“1970-01”来获取 1970 年 1 月的所有数据,或者使用“1970-01-01/1970-01- 02" 以获取这两天的所有行。

【讨论】:

  • 是的,正如您所指出的,在较大的表 (1e7) 上排序(setkey 操作)似乎很昂贵。并且由于某种原因,1e7 行(种子 = 1)的测试结果在您​​的解决方案中提供了大约 60 行(可能与毫秒有关?)
  • @Arun "T00:00:00/T12:00:00" 还将包括不包括但不包括 12:00:01 的时间。我已经编辑使用更精确的子集字符串,它提供与 OP 相同的行数。
  • 感谢 Gsee,虽然我发现这个 x2 时间比基于方法(函数 f)的效率低,而且没有排序
【解决方案4】:

一个迟到的条目,但我认为as.POSIXlt 解决方案将创建一个命名的向量列表,您只需要一个小时

我会按ITime 列作为关键字,然后使用二分搜索在下午 12 点之前对这些时间进行子集化

在下午 12 点之前有 60*60 *12 - 1 秒,所以 seq_len(43199) 将返回所有内容直到(但不包括)下午 12 点

# create IDate and ITime columns and key by time
setkey(DT[, c('Date','Time') := IDateTime(dts)],Time)

# subset times before 12pm
DT[.(seq_len(43199))]

【讨论】:

  • 其实很好,让我测试一下闰年和夏令时的东西,它总是比看起来更棘手。谢谢
猜你喜欢
  • 2012-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
  • 2020-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多