假设我有一个长度为 D 的列表,其中包含 data.table 对象。每个 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 循环的性能不会很好。似乎应该有一些优雅的方法来做到这一点,但我还没有偶然发现它。是否还有另一种高效且不那么神秘的方法?
(如果您想知道,数组中的每个表代表在特定治疗方案下对受试者的模拟反事实观察,我想以不同的概率从这些表中进行采样,以测试具有不同比率的不同回归方法的行为观察到的制度。)
最佳答案
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
结论
- 我的第一种方法是迄今为止最快的,速度快了 x15K 倍 比你原来的。它也返回相同的结果
- 我的第二种方法仍然比您原来的方法快x1.5K倍,但避免了循环(由于某种原因您不喜欢循环)。虽然结果是按表格外观排序的,但顺序与您的结果并不相同。
关于r - 通过从表数组中选择每一行来构建 R data.table,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31826893/