【问题标题】:using data.table to calculate and format time difference between rows by group使用 data.table 按组计算和格式化行之间的时间差
【发布时间】:2013-12-30 22:02:40
【问题描述】:

我是 data.table“场景”的新手,所以如果我的问题过于简单,我深表歉意。我一直处于必须应用一些分析或子集由唯一 ID 分组的一些数据的位置。通常,每个唯一 ID 大约有 1,000 行,大约有 30 个唯一 ID。因此,我被建议切换到 data.table 而不是试图找出 lapply 或 sapply 或 plyr 包。

这是我的数据类型的示例

    structure(list(ID = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 
3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L), dt = structure(c(1138366975, 
1138370472, 1138374064, 1138377669, 1138381264, 1138384873, 1138388503, 
1138399312, 1138402842, 1138406507, 1138413700, 1138417261, 1138420848, 
1138424444, 1138428071, 1138431695, 1138435287, 1138438938, 1138442428, 
1138446098), class = c("POSIXct", "POSIXt"), tzone = "GMT")), .Names = c("ID", 
"dt"), row.names = c(NA, -20L), class = "data.frame")

我把它转换成data.table

X = data.table(test)

将我的“关键”设置为个人

setkey(X,ID)

那么目标是以 HOURS 为单位计算(目前我希望这很容易)时差。因此,通过 Time2-Time1 来获取每个连续位置之间的小时和分钟(在本例中为 ID)。

X[, diff:=c(NA,diff(dt)),by = ID]

此处的 diff 命令以分钟为单位进行计算,但我想以最有效的方式将其转换/舍入为小时,同时仍将值保留为 POSIX 或时间对象。我知道我可能会创建另一列并将 diff 除以 60。但我希望有某种方法可以键入 "hours""minutes" 或某处。因为我不了解 data.table 如何处理时间。 我已经尝试在data.frame 中使用for 循环使用difftime 命令来执行此操作,但是它非常麻烦,并且将数据链接回原始数据框让我感到困惑,因为我不精通for 循环。

一旦我将数据分成几个小时,我只想选择相隔 0.5 小时、相隔 4 小时、相隔 12 小时的数据。在 data.table 中我还没有弄清楚该怎么做

【问题讨论】:

    标签: r diff data.table


    【解决方案1】:

    这是一种方法,但可能不是最有效的......

    X[ , diff := c( NA_character_ , difftime( tail( dt , -1 ) , head( dt , -1 ) , units = "hours" ) ) , by = ID ]
    #    ID                V1
    # 1:  1                NA
    # 2:  1 0.971388888888889
    # 3:  1 0.997777777777778
    # 4:  1  1.00138888888889
    # 5:  2                NA
    

    【讨论】:

    • 你能解释一下为什么你必须引用'tail(dt,-1)'和head版本吗?我不明白为什么这会起作用。我不清楚为什么你必须提到“最后一个”或尾巴而不是最后一个(-1)?另外, NA_character_ 在说什么?这是命令还是指其他东西?
    • 另外,当我运行这个命令时,它似乎产生了一个“新”表?不确定,但基本上它不会简单地在 data.table 的末尾添加一个附加列?我尝试添加类似X$timediff<-X[ , c( NA_character_ , difftime( tail( dt , -1 ) , head( dt , -1 ) , units = "hours" ) ) , by = ID ] 的内容并收到一条错误消息,新的信息列更像是与 1 个变量关联的 3 个新元素的列表。非常混乱。
    • @Kerry 你混淆了data.framedata.table 的语法。不要做X$timediff <- ...X[ , c( NA_character_ , ... 是正确的。运行它,然后查看X。您将看到一个新列。 NA_character_ is simply the character version of NA` 需要将结果强制转换为 character 向量,否则 data.table 会抱怨数据类型(数字和字符)不匹配。
    • 至于headtail...试试吧!例如x <- 1:5; head( x , -1 ); tail( x , -1 )。第一个返回没有最后一个元素的向量,tail-1 的使用返回一个没有第一个元素的向量,因此我们可以从它之前的时间中减去后续时间。 (试一下tail(x,-1) - head(x,-1),就相当于2:5 - 1:4,所以结果是1 1 1 1
    • 我也进行了更新,因此它将列作为原始 data.table 中的新列返回。我希望现在清楚了。
    【解决方案2】:

    X[, diff := c(NA,round(diff(dt)/60)),by=ID] 不是更简单吗?似乎没有时间惩罚。

    f1 <- function(X){return(X[, diff := {tmp = diff(dt); units(tmp) <- "hours"; c(NA, as.numeric(tmp))}, by=ID][])}
    f2 <- function(X){return(X[, diff := c(NA,round(diff(dt)/60)),by=ID])}
    
    library(microbenchmark)
    microbenchmark(f1(X),f2(X))
    # Unit: milliseconds
    #   expr      min       lq   median       uq      max neval
    #  f1(X) 4.676918 4.772861 5.233032 5.324829 7.387008   100
    #  f2(X) 4.615325 4.854294 5.161371 5.383165 7.147151   100
    

    【讨论】:

    • 我也想过这个,但如果可能的话,想保持它的属性为“时间”对象。所以以防万一下次我想知道这些地点之间发生了多少天或几个月。这种方法确实有效,只是不容易转换为其他计量单位
    猜你喜欢
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    • 2016-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-17
    • 1970-01-01
    相关资源
    最近更新 更多