【问题标题】:Using data.table with replacement functions in r在 r 中使用带有替换函数的 data.table
【发布时间】:2015-07-13 19:51:57
【问题描述】:

我今天遇到了以下问题,我想知道是否有更好的方法来完成我想做的事情。

假设我有以下data.table(只是一个小时的时间戳):

library(data.table)
tdt <- data.table(Timestamp = seq(as.POSIXct("1980-01-01 00:00:00"), as.POSIXct("2015-01-01 00:00:00"), '1 hour'))

> tdt
                  Timestamp
     1: 1980-01-01 00:00:00
     2: 1980-01-01 01:00:00
     3: 1980-01-01 02:00:00
     4: 1980-01-01 03:00:00
     5: 1980-01-01 04:00:00
    ---                    
306813: 2014-12-31 20:00:00
306814: 2014-12-31 21:00:00
306815: 2014-12-31 22:00:00
306816: 2014-12-31 23:00:00
306817: 2015-01-01 00:00:00

我的目标是将时间戳的分钟更改为 10 分钟。

我知道我可以使用:

library(lubridate)
minute(tdt$Timestamp) <- 10

但这并没有利用数据表的超快速度(我需要)。在我的笔记本电脑上,这是:

> system.time(minute(tdt$Timestamp) <- 10)
   user  system elapsed 
  11.29    0.16   11.45 

所以,我的问题是:我们能否以某种方式在数据表语法中使用替换函数,以便使用 data.table 的速度完成我想要的操作?如果答案是否定的,那么任何其他data.table 解决方案都可以快速执行此操作。

如果您想知道我尝试过的其中一件事是:

tdt[, Timestamp2 := minute(Timestamp) <- 10]

这不起作用。

预期输出(但带有数据表语法):

> tdt
                  Timestamp
     1: 1980-01-01 00:10:00
     2: 1980-01-01 01:10:00
     3: 1980-01-01 02:10:00
     4: 1980-01-01 03:10:00
     5: 1980-01-01 04:10:00
    ---                    
306813: 2014-12-31 20:10:00
306814: 2014-12-31 21:10:00
306815: 2014-12-31 22:10:00
306816: 2014-12-31 23:10:00
306817: 2015-01-01 00:10:00

【问题讨论】:

  • 认为这可能会为您指明正确的方向:stackoverflow.com/questions/10449366/…。但至少截至去年data.table 并不能很好地支持日期/时间格式。现在可能已经有所改善。
  • tdt[, Timestamp := Timestamp + 600L] 怎么样?
  • @AriB.Friedman 感谢您提供此链接。我已经看到了这个问题,并且我知道替换功能是如何工作的,但是我仍然无法以某种方式使用它...
  • @DavidArenburg 这是伟大的戴夫,非常感谢。老实说,我明天将使用此解决方案。出于好奇,我想看看是否有办法将通用替换函数与data.table 语法结合使用。随意发布它作为答案。如果没有其他人提供补充函数 + data.table 语法的解决方案,我会接受。
  • 您是否对真实数据进行了基准测试,或者您明天才能做到?

标签: r data.table


【解决方案1】:

POSIXct 对象只是具有某些属性的双精度对象

storage.mode(as.POSIXct("1980-01-01 00:00:00"))
## [1] "double"

因此,为了有效地操作它,您可以将其视为一个,例如

tdt[, Timestamp := Timestamp + 600L]

每行将增加 600 秒(10 分钟)引用


一些基准测试

tdt <- data.table(Timestamp = seq(as.POSIXct("1600-01-01 00:00:00"), 
                                  as.POSIXct("2015-01-01 00:00:00"), 
                                  '1 hour'))
system.time(minute(tdt$Timestamp) <- 10)
# user  system elapsed 
# 124.86    1.95  127.68 
system.time(set(tdt, j = 1L, value = `minute<-`(tdt$Timestamp, 10)))
# user  system elapsed 
# 124.99    1.83  128.25 
system.time(tdt[, Timestamp := Timestamp + dminutes(10)])
# user  system elapsed 
# 0.39    0.04    0.42 
system.time(tdt[, Timestamp := Timestamp + 600L])
# user  system elapsed 
# 0.01    0.00    0.01 

【讨论】:

  • 非常感谢戴夫的回答。我刚刚修改了我的函数的核心(根据这个答案)并将它应用到这个示例时间戳上,它可以在不到一秒的时间内工作。它非常快速和准确。非常感谢您为我节省了很多等待运行的时间。我希望我能接受这两个答案。一个向我展示了一种使用替换功能的方法(虽然看起来效果不佳,但它直接回答了我的问题),另一个帮助我解决了我的实际问题。我很困惑:S。堆栈溢出需要为这种情况做某事!
  • 没关系,阿里提供了一个很好的答案,值得打勾。
  • 非常感谢戴夫,你让我感觉好多了 :)。感谢您的所有帮助和实际解决方案。
  • @LyzanderR 也可能要记住IDateTime(或类似的IDate/ITime)类,它们的工作方式类似于Date/POSIXct,但为了提高效率而存储为整数。跨度>
【解决方案2】:

替换函数分两步运行:

  1. 创建所需输出的函数,
  2. 然后将该输出分配给结果。

您可以运行step 1 without running step 2。然后可以使用该结果设置 data.table 列(此处使用set,但您也可以使用:=)。

library(lubridate)
library(data.table)
tdt <- data.table(Timestamp = seq(as.POSIXct("1980-01-01 00:00:00"), as.POSIXct("2015-01-01 00:00:00"), '1 hour'))
minute(tdt$Timestamp) <- 20
print( `minute<-`(tdt$Timestamp,11) )
set( tdt, j=1L,value=`minute<-`(tdt$Timestamp,11)  )

编辑:小 data.table 与大 data.table 基准测试

library(lubridate)
library(data.table)
library(microbenchmark)

# Config
tms <- 5L

# Sample data, 1 column
tdt <- data.table(Timestamp = seq(as.POSIXct("1980-01-01 00:00:00"), as.POSIXct("2015-01-01 00:00:00"), '1 hour'))
minute(tdt$Timestamp) <- 20

tdf <- as.data.frame( tdt )


# Sample data, lots of columns
bdf <- cbind( tdf, as.data.frame( replicate( 100, runif(nrow(tdt)) ) ) )
bdt <- as.data.table( bdf )

# Benchmark
microbenchmark(
  `minute<-`(tdt$Timestamp,10), # How long does the operation to generate the new vector itself take?
  set( tdt, j=1L,value=`minute<-`(tdt$Timestamp,11)  ), # One column: How long does it take to generate the new vector and replace the contents in the data.table?
  minute( tdf$Timestamp ) <- 12, # One column: How long does it take to do it with a data.frame?
  set( tdt, j=1L,value=`minute<-`(bdt$Timestamp,13)  ), # Many columns: How long does it take to generate the new vector and replace the contents in the data.table?
  minute( bdf$Timestamp ) <- 14, #  Many columns: How long does it take to do it with a data.frame?
  times = tms
)

Unit: seconds
                                                    expr      min       lq     mean   median       uq      max neval
                           `minute<-`(tdt$Timestamp, 10) 1.304388 1.385883 1.417616 1.389316 1.459166 1.549327     5
 set(tdt, j = 1L, value = `minute<-`(tdt$Timestamp, 11)) 1.314495 1.344277 1.376241 1.352124 1.389083 1.481225     5
                             minute(tdf$Timestamp) <- 12 1.342104 1.349231 1.488639 1.378840 1.380659 1.992358     5
 set(tdt, j = 1L, value = `minute<-`(bdt$Timestamp, 13)) 1.337944 1.383429 1.402802 1.418211 1.418922 1.455503     5
                             minute(bdf$Timestamp) <- 14 1.332482 1.333713 1.355331 1.335728 1.342607 1.432127     5

看起来它并没有更快,这掩盖了我对正在发生的事情的理解。奇怪。

【讨论】:

  • 很好,不过我想知道它会如何扩展。从我的基准测试来看,时间完全相同
  • 这太棒了!对于我设置的特定示例,老实说,大卫的回答(我赞成戴夫:P)要快得多,但平心而论,这是我一直在寻找的通用答案。非常感谢阿里。
  • 由于您的示例由单个列组成,我希望替换列与替换整个 data.frame 的速度相似。尝试使用包含数百列的 data.table 对其进行基准测试?
  • 我的荣幸。我也赞成 David 的可爱回答,但想提供这个解决方案,以防您的问题需要更通用的机制。
  • 非常感谢阿里。这是一篇很棒的分析,感谢您花时间和精力提供帮助。我将使用@DavidArenburg 的想法来避免使用我已经测试过并且效果很好的minute。我仍然很好奇为什么在使用替换函数时 data.frame 和 data.table 之间似乎没有区别。也许马修可以在这里阐明一些观点。
【解决方案3】:

我想这应该对你有用:

library(data.table)
library(lubridate)

tdt <- data.table(
  Timestamp = seq(as.POSIXct("1980-01-01 00:00:00")
  , as.POSIXct("2015-01-01 00:00:00")
  , '1 hour'))
tdt[, Timestamp := Timestamp + dminutes(10)]

【讨论】:

  • 感谢您的回答。这实际上等同于 Dave 的答案,并且会稍微慢一些,因为它使用的是外部函数。
  • 是的,确实是等价的。但是我在发布之前没有看到它:)但是由于对正在发生的事情有更清晰的了解,我更喜欢我的。
猜你喜欢
  • 2020-09-13
  • 1970-01-01
  • 2021-08-21
  • 2014-05-14
  • 1970-01-01
  • 2013-01-06
  • 2021-08-09
  • 2016-08-26
  • 1970-01-01
相关资源
最近更新 更多