data.frames 列表的快速矢量化合并

Posted

技术标签:

【中文标题】data.frames 列表的快速矢量化合并【英文标题】:Fast vectorized merge of list of data.frames by row 【发布时间】:2011-06-19 07:10:47 【问题描述】:

关于在 SO 上的列表中合并 data.frame 的大多数问题与我在这里想要解决的问题不太相关,但请随时证明我错了。

我有一个 data.frames 列表。我想逐行将行“rbind”到另一个data.frame中。本质上,所有第一行形成一个 data.frame,第二行形成第二个 data.frame,依此类推。 结果将是一个与我的原始 data.frame(s) 中的行数相同长度的列表。到目前为止,data.frames 在维度上是相同的。

这里有一些数据可供使用。

sample.list <- list(data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)),
        data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)),
        data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)),
        data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)),
        data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)),
        data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)),
        data.frame(x = sample(1:100, 10), y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)))

这是我想出的优秀 for 循环。

#solution 1
my.list <- vector("list", nrow(sample.list[[1]]))
for (i in 1:nrow(sample.list[[1]])) 
    for (j in 1:length(sample.list)) 
        my.list[[i]] <- rbind(my.list[[i]], sample.list[[j]][i, ])
    


#solution 2 (so far my favorite)
sample.list2 <- do.call("rbind", sample.list)
my.list2 <- vector("list", nrow(sample.list[[1]]))

for (i in 1:nrow(sample.list[[1]])) 
    my.list2[[i]] <- sample.list2[seq(from = i, to = nrow(sample.list2), by = nrow(sample.list[[1]])), ]

可以使用矢量化来改善这一点而不会造成太大的脑损伤吗?当然,正确答案将包含一段 sn-p 代码。 “是”作为答案不算数。

编辑

#solution 3 (a variant of solution 2 above)
ind <- rep(1:nrow(sample.list[[1]]), times = length(sample.list))
my.list3 <- split(x = sample.list2, f = ind)

基准测试

我的列表更大,每个 data.frame 有更多行。我对结果进行了基准测试,结果如下:

#solution 1
system.time(for (i in 1:nrow(sample.list[[1]])) 
    for (j in 1:length(sample.list)) 
        my.list[[i]] <- rbind(my.list[[i]], sample.list[[j]][i, ])
    
)
   user  system elapsed 
 80.989   0.004  81.210 

# solution 2
system.time(for (i in 1:nrow(sample.list[[1]])) 
    my.list2[[i]] <- sample.list2[seq(from = i, to = nrow(sample.list2), by = nrow(sample.list[[1]])), ]
)
   user  system elapsed 
  0.957   0.160   1.126 

# solution 3
system.time(split(x = sample.list2, f = ind))
   user  system elapsed 
  1.104   0.204   1.332 

# solution Gabor
system.time(lapply(1:nr, bind.ith.rows))
   user  system elapsed 
  0.484   0.000   0.485 

# solution ncray
system.time(alply(do.call("cbind",sample.list), 1,
                .fun=matrix, ncol=ncol(sample.list[[1]]), byrow=TRUE,
                dimnames=list(1:length(sample.list),names(sample.list[[1]]))))
   user  system elapsed 
 11.296   0.016  11.365

【问题讨论】:

为什么我忘记了拆分?非常好的解决方案! 很好的演示。这种情况是我仍然倾向于使用 for 循环的少数情况之一,但很清楚为什么这是一个坏主意:) @jonw,我想这取决于你的追求。如果你有中等或小的数据集,循环就可以了。 这个 merged.list = do.call('rbind', sample.list) 怎么样 我读到即使问题已经得到解答,也可以奖励赏金。所以我决定去寻找好的答案。然而,这对我来说有点问题,因为 mnel 的 DT 解决方案似乎比我最初计划授予的公认解决方案更好。 【参考方案1】:

试试这个:

bind.ith.rows <- function(i) do.call(rbind, lapply(sample.list, "[", i, TRUE))
nr <- nrow(sample.list[[1]])
lapply(1:nr, bind.ith.rows)

【讨论】:

TRUE 对应于什么参数? 它给出的sample.list[i, TRUE]sample.list[i, ] 相同。如果我们遗漏了TRUE,我们将得到sample.list[i],它会选择一列,而不是选择一行的期望结果。 这是我见过的最好的 R【参考方案2】:

这是我对 plyr 的尝试,但我喜欢 G. Grothendieck 的方法:

library(plyr)
alply(do.call("cbind",sample.list), 1, .fun=matrix,
        ncol=ncol(sample.list[[1]]), byrow=TRUE,
        dimnames=list(1:length(sample.list),
        names(sample.list[[1]])
      ))

【讨论】:

你能提供一个dplyr解决方案吗?【参考方案3】:

使用data.table 可以加快这一进程的几个解决方案

编辑 - 更大的数据集显示data.table 更加精彩。

# here are some sample data 
sample.list <- replicate(10000, data.frame(x = sample(1:100, 10), 
  y = sample(1:100, 10), capt = sample(0:1, 10, replace = TRUE)), simplify = F)

Gabor 的快速解决方案:

# Solution Gabor
bind.ith.rows <- function(i) do.call(rbind, lapply(sample.list, "[", i, TRUE))
nr <- nrow(sample.list[[1]])
system.time(rowbound <- lapply(1:nr, bind.ith.rows))

##    user  system elapsed 
##   25.87    0.01   25.92 

data.table 函数rbindlist 将使这个甚至更快,即使在使用 data.frames 时)

library(data.table)
fastbind.ith.rows <- function(i) rbindlist(lapply(sample.list, "[", i, TRUE))
system.time(fastbound <- lapply(1:nr, fastbind.ith.rows))

##    user  system elapsed 
##   13.89    0.00   13.89 

data.table 解决方案

这是一个使用 data.tables 的解决方案 - 它是 split 类固醇解决方案。

# data.table solution
system.time(
    # change each element of sample.list to a data.table (and data.frame) this
    # is done instaneously by reference
    invisible(lapply(sample.list, setattr, name = "class", 
               value = c("data.table",  "data.frame")))
    # combine into a big data set
    bigdata <- rbindlist(sample.list)
    # add a row index column (by refere3nce)
    index <- as.character(seq_len(nr))
    bigdata[, `:=`(rowid, index)]
    # set the key for binary searches
    setkey(bigdata, rowid)
    # split on this -
    dt_list <- lapply(index, function(i, j, x) x[i = J(i)], x = bigdata)
    # if you want to drop the `row id` column
    invisible(lapply(dt_list, function(x) set(x, j = "rowid", value = NULL)))
    # if you really don't want them to be data.tables run this line
    # invisible(lapply(dt_list, setattr,name = 'class', value =
    # c('data.frame')))
)
################################
##    user  system elapsed    ##
##    0.08    0.00    0.08    ##
################################

data.table 真棒!

rbindlist警告用户

rbindlist 很快,因为它不执行do.call(rbind,....) 将执行的检查。例如,它假定任何因子列与列表的第一个元素具有相同的级别。

【讨论】:

【参考方案4】:

添加tidyverse解决方案:

library(tidyverse)
bind_rows(sample.list)

【讨论】:

以上是关于data.frames 列表的快速矢量化合并的主要内容,如果未能解决你的问题,请参考以下文章

从列表中合并并平均每15个data.frames

合并大量data.frames [重复]

BIM轻量化平台功能对比

基于pytorch的模型剪枝+模型量化+BN合并+TRT部署(cifar数据)

基于pytorch的模型剪枝+模型量化+BN合并+TRT部署(cifar数据)

基于pytorch的模型剪枝+模型量化+BN合并+TRT部署(cifar数据)