使用data.table按变量对组进行平均差异查找

3
假设我有以下的`data.table`数据表:
library(data.table)
dt <- data.table(x1 = c(1:12), x2=c(21:32))

然后,我使用以下方法按照用户指定的间隔创建箱子:

dt[,intx1:=cut(x1, breaks = c(-Inf, 4, 9, Inf))]

返回,

    x1 x2    intx1
 1:  1 21 (-Inf,4]
 2:  2 22 (-Inf,4]
 3:  3 23 (-Inf,4]
 4:  4 24 (-Inf,4]
 5:  5 25    (4,9]
 6:  6 26    (4,9]
 7:  7 27    (4,9]
 8:  8 28    (4,9]
 9:  9 29    (4,9]
10: 10 30 (9, Inf]
11: 11 31 (9, Inf]
12: 12 32 (9, Inf]

我正在尝试查找bin和变量之间的平均差异:
dt[, mux1_grp:=mean(x1), by = intx1][,mux1_pop:=mean(x1)][,mux1_diff:=mux1_grp-mux1_pop]
dt[,`:=`(intx1=NULL, mux1_grp=NULL, mux1_pop=NULL)]

返回结果如下:

    x1 x2 mux1_diff
 1:  1 21      -4.0
 2:  2 22      -4.0
 3:  3 23      -4.0
 4:  4 24      -4.0
 5:  5 25       0.5
 6:  6 26       0.5
 7:  7 27       0.5
 8:  8 28       0.5
 9:  9 29       0.5
10: 10 30       4.5
11: 11 31       4.5
12: 12 32       4.5

然而,我的原始数据包含多个变量(例如,x1、x2、...、x20)。
因此,我需要按照以下步骤重复x2的相同程序:

dt[,intx2:=cut(x2, breaks = c(-Inf, 25, 28, Inf))]
dt[, mux2_grp:=mean(x2), by = intx2][,mux2_pop:=mean(x2)][,mux2_diff:=mux2_grp-mux2_pop]
dt[,`:=`(intx2=NULL, mux2_grp=NULL, mux2_pop=NULL)]

我的最终输出将是:

    x1 x2 mux1_diff mux2_diff
 1:  1 21      -4.0      -3.5
 2:  2 22      -4.0      -3.5
 3:  3 23      -4.0      -3.5
 4:  4 24      -4.0      -3.5
 5:  5 25       0.5      -3.5
 6:  6 26       0.5       0.5
 7:  7 27       0.5       0.5
 8:  8 28       0.5       0.5
 9:  9 29       0.5       4.0
10: 10 30       4.5       4.0
11: 11 31       4.5       4.0
12: 12 32       4.5       4.0

如何改进这段代码?请注意每个变量都有不同的用户指定间隔。

关于其他变量,您是每次都在每个变量上单独分组对吧?如果是这样的话,您可能需要使用一个for循环,因为分组变量每次都会改变。 - akrun
顺便说一下,dt[,\:=`(int=NULL, mu_grp=NULL, mu_pop=NULL)]可以写成dt[,c("int","mu_grp","mu_pop") := NULL]。关于你实际的问题,我建议使用 melt` 将 x1..x20 放在单个列中,并转换为长格式。这在第五篇文献中有介绍:https://github.com/Rdatatable/data.table/wiki/Getting-started - Frank
1个回答

2
我们可以使用一种简洁的单步选项来完成此操作(尽管与 OP 的方法相比可能不是最优的,这是来自 @Frank 的评论)。
dt[, mu_diff := mean(x) - mean(dt$x), by = .(cut(x, breaks = c(-Inf, 4, 9, Inf)))][]
#    x    mu_diff
#1:  1 -3.8636364
#2:  2 -3.8636364
#3:  3 -3.8636364
#4:  4 -3.8636364
#5:  5  0.3863636
#6:  6  0.3863636
#7:  7  0.3863636
#8:  9  0.3863636
#9: 10  4.6363636
#10:11  4.6363636
#11:12  4.6363636

如果有许多变量(不清楚是否对不同列使用相同的cut中断点 - 假设是相同的),我们可以遍历列(在下面的可重现示例中,显示了两个变量“x1”和“x2”),通过列的数字索引指定.SDcols,按cut分组子集的列,将新列赋值为组内数值的mean与整个列的mean之间的差。

nm1 <- paste0("mu_diff", seq_along(dt1))
for(j in seq_along(dt1)){
  dt1[, (nm1[j]) := mean(.SD[[1L]]) - mean(dt1[[names(dt1)[j]]]), 
      by = .(cut(get(names(dt1)[j]), breaks = c(-Inf, 4, 9, Inf))) ,
              .SDcols = j][]
 }

更新 - 假设对于每一列,cut 变量的 breaks 是不同的,那么将其放在一个 list 中,并在 for 循环中使用索引获取该 list 元素。

brkLst <- list(c(-Inf, 4, 9, Inf), c(-Inf, 10, 14, Inf))
for(j in seq_along(dt1)){
  dt1[, (nm1[j]) := mean(.SD[[1L]]) - mean(dt1[[names(dt1)[j]]]), 
      by = .(cut(get(names(dt1)[j]), breaks = brkLst[[j]])) ,
              .SDcols = j][]
 }

使用OP的新数据(“dt2”)检查输出

brkLst2 <- list(c(-Inf, 4, 9, Inf),  c(-Inf, 25, 28, Inf))
nm1 <- paste0("mu", names(dt2), "_diff")
for(j in seq_along(dt2)){
   dt2[, (nm1[j]) := mean(.SD[[1L]]) - mean(dt2[[names(dt2)[j]]]), 
  by = .(cut(get(names(dt2)[j]), breaks = brkLst2[[j]])) ,
          .SDcols = j][]
}

dt2
#    x1 x2 mux1_diff mux2_diff
# 1:  1 21      -4.0      -3.5
# 2:  2 22      -4.0      -3.5
# 3:  3 23      -4.0      -3.5
# 4:  4 24      -4.0      -3.5
# 5:  5 25       0.5      -3.5
# 6:  6 26       0.5       0.5
# 7:  7 27       0.5       0.5
# 8:  8 28       0.5       0.5
# 9:  9 29       0.5       4.0
#10: 10 30       4.5       4.0
#11: 11 31       4.5       4.0
#12: 12 32       4.5       4.0

数据

dt1 <- data.table(x1 = c(1,2,3,4,5,6,7,9,10,11,12))[, x2 := x1 + 5][]
#OP's changed dataset
dt2 <- data.table(x1 = 1:12, x2=21:32)

1
由于对分组的mean进行了GForce优化,因此OP的方法可能具有更好的性能。您可以通过OP的示例和verbose查看:dt[, mu_grp:=mean(x), by = cut(x, breaks = c(-Inf, 4, 9, Inf)), verbose=TRUE]因此,在避免临时变量时存在权衡。 - Frank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接