我有两个data.tables:一个实验数据表x
和一个类别查找表dict
。
library(data.table)
set.seed(123)
x = data.table(samp=c(1,1,2,3,3,3,4,5,5,5,6,7,7,7,8,9,9,10,10), y=rnorm(19))
x
samp y
#1: 1 -0.56047565
#2: 1 -0.23017749
#3: 2 1.55870831
#4: 3 0.07050839
#5: 3 0.12928774
#6: 3 1.71506499
#7: 4 0.46091621
#8: 5 -1.26506123
#9: 5 -0.68685285
#10: 5 -0.44566197
#11: 6 1.22408180
#12: 7 0.35981383
#13: 7 0.40077145
#14: 7 0.11068272
#15: 8 -0.55584113
#16: 9 1.78691314
#17: 9 0.49785048
#18: 10 -1.96661716
#19: 10 0.70135590
dict = data.table(samp=c(1:5, 4:8, 7:10), cat=c(rep(1,length(1:5)), rep(2,length(4:8)), rep(3,length(7:10))))
dict
# samp cat
# 1: 1 1
# 2: 2 1
# 3: 3 1
# 4: 4 1
# 5: 5 1
# 6: 4 2
# 7: 5 2
# 8: 6 2
# 9: 7 2
# 10: 8 2
# 11: 7 3
# 12: 8 3
# 13: 9 3
# 14: 10 3
对于每个samp
,我需要先计算与之关联的所有y
的产品。然后,我需要计算dict$cat
中指定的每个样本类别的这些产品的总和。请注意,每个samp
都会映射到多个dict$cat
。
执行此操作的一种方法是立即合并x
和dict
,允许行重复(allow.cartesian=T
):
setkey(dict, samp)
setkey(x, samp)
step0 = dict[x, allow.cartesian=T]
setkey(step0, samp, cat)
step1 = step0[, list(prodY=prod(y)[1], cat=cat[1]), by=c("samp", "cat")]
resMet1 = step1[, sum(prodY), by="cat"]
我想知道这个加入步骤是否可以避免。这有几个原因 - 例如,如果x
是巨大的,重复将使用额外的内存(我是对的吗?)。此外,这些具有重复行的汇总表非常混乱,使得分析更容易出错。
所以我考虑在每个dict$cat
中使用样本进行x
中的二进制搜索。我知道如何为一个类别做这个,所以对所有这些做一个丑陋的方式就是循环:
setkey(x, samp)
setkey(dict,samp)
pool = vector("list")
for(n in unique(dict$cat)){
thisCat = x[J(dict[cat==n])]
setkey(thisCat, samp)
step1 = thisCat[, list(prodY=prod(y)[1], cat=cat[1]), by="samp"]
pool[[n]] = step1[, sum(prodY), by="cat"]
}
resMet2 = rbindlist(pool)
但当然要避免这种循环。所以我想知道是否有办法让data.table
迭代J()
内的键值?
答案 0 :(得分:2)
您最好先将x
折叠到samp
级别。
xprod = x[, .(py = prod(y)), by=samp]
<强>合并强>
res2 <- xprod[dict, on = "samp"][, sum(py), by=cat]
identical(res2, resMet2) # test passed
或子集
如果samp
是xprod
中的行号(如此处所示),则可以进行子集而不是合并:
res3 <- xprod[(dict$samp), sum(py), by=.(cat=dict$cat)]
identical(res3, resMet2) # test passed
重新标记样本ID非常简单,这是真的。
答案 1 :(得分:2)
IIUC,我会按照以下方式制定您的问题:为每个dict$cat
,我希望prod(y)
与sample
对应cat
},然后sum
他们全部。
让我们一步一步地构建这个:
对于每个dict$cat
- 听起来需要按cat
分组:
dict[, ,by=cat]
剩下的就是正确填写j
。
对于此群组的每个样本,您需要从prod(y)
获取x
:
x[samp %in% .SD$samp, prod(y), by=samp]
从对应此论坛 x
的{{1}}中提取这些行(使用代表数据子集的samp
)和在.SD
上按prod(y)
计算samp
。太好了!
我们仍需要总结它们。
sum(x[samp %in% .SD$samp, prod(y), by=samp]$V1)
我们有完整的j
表达式。让我们把它全部插入:
dict[, sum(x[samp %in% .SD$samp, prod(y), by=samp]$V1), by=cat]
# cat V1
# 1: 1 1.7770272
# 2: 2 0.7578771
# 3: 3 -1.0295633
希望这有帮助。
注1:这里有prod(y)
的冗余计算,但好处是我们没有实现太多的中间数据。所以它的内存效率很高。如果你的群组太多,这可能会变慢..你可能想要在另一个变量中计算prod(y)
,如下所示:
x_p = x[, .(p = prod(y)), by=samp]
有了这个,我们可以简化j
如下:
dict[, x_p[samp %in% .SD$samp, sum(p)], by=cat]
注2: %in%
表达式在x's
samp
列的第一次运行时创建自动索引,以使用二进制搜索从那时起基于子集。因此,由于矢量扫描,不需要担心性能。