首选解决方案是:
dt[, opsdiff := c(NA, diff(opscounter)),
by = type][opsdiff < 0, opsdiff := opscounter][]
# type opscounter opsdiff
# 1: a 105609 NA
# 2: a 106316 707
# 3: a 106705 389
# 4: a 489 489
# 5: a 723 234
# 6: a 1250 527
# 7: b 105609 NA
# 8: b 106316 707
# 9: b 106705 389
# 10: b 489 489
# 11: b 723 234
# 12: b 1250 527
请注意,我添加了额外的内容[]
为了即时打印结果并说明您可以添加其中多个结果。
一般来说,最好避免ifelse
(特别是在你有如此大数据集的情况下)它可能会很慢(尽管矢量化),因为它评估两者yes and no cases https://stackoverflow.com/q/16275149/559784。在你的情况下,你发现了另一个“缺陷”,你需要告诉它你想要拉的确切位置opscounter
from,这增加了复杂性(参见@阿伦斯评论 https://stackoverflow.com/questions/26239328/conditional-difference-calculation-in-data-table#comment41158288_26239328以实现可能的覆盖)。
关于您在评论中提出的问题,data.table
表格的操作DT[...]
只是调用函数[.data.table(DT, ...)
。这并没有什么不同data.frame
;有一个类似的功能[.data.frame
.
请注意,一个data.table
也是一个data.frame
. See class(dt)
并且还阅读了?data.table
.
为了更清楚地说明,在 data.table 中,添加[...]
一个接一个地被称为chaining。这是免费的。您也可以在 data.frame 中执行相同的操作(如下所示),但是您可以在 data.frame 上执行的操作data.frame
是有限的,因此链接本身的使用与 data.table 不同。
df <- as.data.frame(dt) # or `setDF(dt)` in 1.9.4+ to do this by reference
df[df$type == "a", ][2:3, ]
# type opscounter
# 2 a 106316
# 3 a 106705
最后,为了说明无效ifelse
,这是一个基准:
set.seed(123)
n <- 1e6
dt <- data.table(type = rep(c("a","b"), each = n),
opscounter = sample(1:1e5, n*2, replace = TRUE))
library(microbenchmark)
microbenchmark(
dt[, opsdiff := c(NA, diff(opscounter)), by = type][opsdiff < 0, opsdiff := opscounter],
dt[, opsdiff := c(NA, ifelse(diff(opscounter) > 0, diff(opscounter), tail(opscounter, -1L))), by=type]
)
# Unit: milliseconds
# expr
# dt[, `:=`(opsdiff, c(NA, diff(opscounter))), by = type][opsdiff < 0, `:=`(opsdiff, opscounter)]
# dt[, `:=`(opsdiff, c(NA, ifelse(diff(opscounter) > 0, diff(opscounter), tail(opscounter, -1L)))), by = type]
# min lq mean median uq max neval
# 228.0445 255.4006 285.8163 281.1388 307.4195 508.3841 100
# 899.1222 990.1478 1085.5492 1048.3704 1095.7179 1740.5704 100
The ifelse
解决方案速度慢约 4 倍。