假设我有一个包含data.table对象的长度D列表。每个data.table具有相同的列(X,Y)和相同的行数N.我想构造另一个具有N行的表,其中各个行取自由索引向量指定的表,长度为N.重申,最终表中的每一行都取自数组中的一个且只有一个表,源表的索引由现有向量指定。
N = 100 # rows in each table (actual ~1000000 rows)
D = 4 # number of tables in array (actual ~100 tables)
tableArray = vector("list", D)
for (d in 1:D) {
tableArray[[d]] = data.table(X=rnorm(N), Y=d) # actual ~100 columns
}
tableIndexVector = sample.int(D, N, replace=TRUE) # length N of random 1:D
finalTable = copy(tableArray[[1]]) # just for length and column names
for (n in 1:N) {
finalTable[n] = tableArray[[tableIndexVector[n]]][n]
}
这似乎按我想要的方式工作,但数组表示法中的数组很难理解,我认为for循环的性能不会很好。似乎应该有一些优雅的方式来做到这一点,但我还没有偶然发现它。有没有其他方法可以做到这一点,效率高,不那么神秘?
(如果你想知道,数组中的每个表代表特定治疗方案下受试者的模拟反事实观察,我想用不同概率的样本进行抽样,以测试不同比率的不同回归方法的行为观察到的政权。)
答案 0 :(得分:3)
for
循环可以正常使用data.table
,但我们可以使用以下方法显着改善特定循环的性能(我相信)。
方法#1
set
,因为它可以避免[.data.table
开销1:N
,因为您可以简化循环以仅在tableIndexVector
的唯一值上运行,并一次分配所有相应的值。这应该会将运行时间减少至少 x10K (因为N
的大小为1MM且D
的大小仅为100,而unique(tableIndexVector) <= D
)所以你基本上可以将你的循环转换为以下
for (i in unique(tableIndexVector)) {
indx <- which(tableIndexVector == i)
set(finalTable, i = indx, j = 1:2, value = tableArray[[i]][indx])
}
方法#2
另一种方法是使用rbindlist
并将所有表合并为一个大data.table
,同时添加新的idcol
参数,以便识别大表中的不同表。你需要devel version。这将避免请求循环,但结果将按表外观
temp <- rbindlist(tableArray, idcol = "indx")
indx <- temp[, .I[which(tableIndexVector == indx)], by = indx]$V1
finalTable <- temp[indx]
以下是更大数据集的基准
N = 100000
D = 10
tableArray = vector("list", D)
set.seed(123)
for (d in 1:D) {
tableArray[[d]] = data.table(X=rnorm(N), Y=d)
}
set.seed(123)
tableIndexVector = sample.int(D, N, replace=TRUE)
finalTable = copy(tableArray[[1]])
finalTable2 = copy(tableArray[[1]])
## Your approach
system.time(for (n in 1:N) {
finalTable[n] = tableArray[[tableIndexVector[n]]][n]
})
# user system elapsed
# 154.79 33.14 191.57
## My approach # 1
system.time(for (i in unique(tableIndexVector)) {
indx <- which(tableIndexVector == i)
set(finalTable2, i = indx, j = 1:2, value = tableArray[[i]][indx])
})
# user system elapsed
# 0.01 0.00 0.02
## My approach # 2
system.time({
temp <- rbindlist(tableArray, idcol = "indx")
indx <- temp[, .I[which(tableIndexVector == indx)], by = indx]$V1
finalTable3 <- temp[indx]
})
# user system elapsed
# 0.11 0.00 0.11
identical(finalTable, finalTable2)
## [1] TRUE
identical(setorder(finalTable, X), setorder(finalTable3[, indx := NULL], X))
## [1] TRUE
所以结论