r - 通过从表数组中选择每一行来构建 R data.table

标签 r dataframe data.table

假设我有一个长度为 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

  1. 使用set相反,因为它避免了 [.data.table开销
  2. 不要循环 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/

相关文章:

c - 带有 C/C++ 和 openMP 的 R 包 : how to make "Makevars" file under "mypackage/src/" folder?

r - r中的命令if(0)是什么意思?

python - 将每日 Pandas 数据帧转换为分钟频率

python - 将类型为 'object' 的数据帧列转换为类型列表

在 lapply(.SD,...) 中为 data.table R 保留列名

r - 在 R 中子集或排列数据

r - 使用 knitr 强制执行 PDF 包小插图

r - 合并列,同时忽略重复项和 NA

R:检查一组变量是否形成唯一索引

r - 为每个唯一 ID 创建滞后变量