通过从表数组

时间:2015-08-05 08:11:29

标签: r dataframe data.table

假设我有一个包含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循环的性能不会很好。似乎应该有一些优雅的方式来做到这一点,但我还没有偶然发现它。有没有其他方法可以做到这一点,效率高,不那么神秘?

(如果你想知道,数组中的每个表代表特定治疗方案下受试者的模拟反事实观察,我想用不同概率的样本进行抽样,以测试不同比率的不同回归方法的行为观察到的政权。)

1 个答案:

答案 0 :(得分:3)

for循环可以正常使用data.table,但我们可以使用以下方法显着改善特定循环的性能(我相信)。

方法#1

  1. 请改用set,因为它可以避免[.data.table开销
  2. 不要遍历1:N,因为您可以简化循环以仅在tableIndexVector的唯一值上运行,并一次分配所有相应的值。这应该会将运行时间减少至少 x10K (因为N的大小为1MM且D的大小仅为100,而unique(tableIndexVector) <= D
  3. 所以你基本上可以将你的循环转换为以下

    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 次 比你原来的。它也返回相同的结果
    • 我的第二种方法仍然比原始方法快 0.5K 倍,但避免了循环(由于某些原因你不喜欢)。虽然结果是表格外观的顺序,但顺序与您的结果不一样。