在data.table中采用列式差异

C8H*_*4O2 6 r difference data.table

如何使用data.table语法生成data.table,其中每列包含原始data.table列与下一列之间的差异?

示例:我有一个data.table,其中每一行都是一个组,每一列都是在第0年之后,第1年,第2年之后存活的人口等.例如:

pop <- data.table(group_id = c(1, 2, 3), 
                   N = c(4588L, 4589L, 4589L), 
                   N_surv_1 = c(4213, 4243, 4264), 
                   N_surv_2 = c(3703, 3766, 3820), 
                   N_surv_3 = c(2953, 3054, 3159) )
# group_id    N N_surv_1 N_surv_2 N_surv_3
#        1 4588     4213     3703     2953
#        2 4589     4243     3766     3054
#        3 4589     4264     3820     3159
Run Code Online (Sandbox Code Playgroud)

(数据类型不同,因为N是一个真正的整数计数,N_surv_1等是可能是小数的投影.)

我做了什么:使用基数diff和矩阵换位,我们可以:

diff <- data.table(t(diff(t(as.matrix(pop[,-1,with=FALSE])))))
setnames(diff, paste0("deaths_",1:ncol(diff)))
cbind(group_id = pop[,group_id],diff) 
# produces desired output:
#    group_id deaths_1 deaths_2 deaths_3
#           1     -375     -510     -750
#           2     -346     -477     -712
#           3     -325     -444     -661
Run Code Online (Sandbox Code Playgroud)

我知道我可以diff在由单个列生成的基础上逐个使用melt.data.table,所以这可行但不漂亮:

melt(pop, 
     id.vars = "group_id"
     )[order(group_id)][, setNames(as.list(diff(value)),
                                   paste0("deaths_",1:(ncol(pop)-2)) ),
                          keyby = group_id]
Run Code Online (Sandbox Code Playgroud)

这是最实现这一目标的data.table-riffic方法,还是有办法将其作为data.table中的多列操作?

Fra*_*ank 6

好吧,你可以减去子集:

ncols = grep("^N(_surv_[0-9]+)?", names(pop), value=TRUE)
pop[, Map(
  `-`, 
   utils:::tail.default(.SD, -1), 
   utils:::head.default(.SD, -1)
), .SDcols=ncols]

#    N_surv_1 N_surv_2 N_surv_3
# 1:     -375     -510     -750
# 2:     -346     -477     -712
# 3:     -325     -444     -661
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法将这些值分配给新列:=.我不知道为什么tail并且head没有更容易获得......正如@akrun所指出的,你可以使用with=FALSE,比如pop[, .SD[, -1, with=FALSE] - .SD[, -ncol(.SD), with=FALSE], .SDcols=ncols].

无论如何,与简单重塑相比,这是非常复杂的:

melt(pop, id="group_id")[, tail(value, -1) - head(value, -1), by=group_id]
#    group_id   V1
# 1:        1 -375
# 2:        1 -510
# 3:        1 -750
# 4:        2 -346
# 5:        2 -477
# 6:        2 -712
# 7:        3 -325
# 8:        3 -444
# 9:        3 -661
Run Code Online (Sandbox Code Playgroud)