8

我有一个数据框列表,我确信它们都包含至少一行(实际上,有些只包含一行,而另一些包含给定数量的行),并且它们都具有相同的列(名称和类型)。万一这很重要,我也确定行中的任何地方都没有 NA。

情况可以这样模拟:

#create one row
onerowdfr<-do.call(data.frame, c(list(), rnorm(100) , lapply(sample(letters[1:2], 100, replace=TRUE), function(x){factor(x, levels=letters[1:2])})))
colnames(onerowdfr)<-c(paste("cnt", 1:100, sep=""), paste("cat", 1:100, sep=""))
#reuse it in a list
someParts<-lapply(rbinom(200, 1, 14/200)*6+1, function(reps){onerowdfr[rep(1, reps),]})

我已经设置了(随机化的)参数,以便它们接近我的真实情况。

现在,我想将所有这些数据帧合并到一个数据帧中。我认为使用 rbind 可以解决问题,如下所示:

system.time(
result<-do.call(rbind, someParts)
)

现在,在我的系统上(不是特别慢),使用上面的设置,这是 system.time 的输出:

   user  system elapsed 
   5.61    0.00    5.62

rbind-ing 254(在我的情况下)行 200 个变量将近 6 秒?当然必须有一种方法来提高这里的性能?在我的代码中,我必须经常做类似的事情(它是多重插补的一个),所以我需要尽可能快。

4

5 回答 5

15

您可以仅使用数字变量构建矩阵并在最后转换为因子吗? rbind在数字矩阵上要快得多。

在我的系统上,使用数据框:

> system.time(result<-do.call(rbind, someParts))
   user  system elapsed 
  2.628   0.000   2.636 

用所有数字矩阵构建列表:

onerowdfr2 <- matrix(as.numeric(onerowdfr), nrow=1)
someParts2<-lapply(rbinom(200, 1, 14/200)*6+1, 
                   function(reps){onerowdfr2[rep(1, reps),]})

结果快了很多rbind

> system.time(result2<-do.call(rbind, someParts2))
   user  system elapsed 
  0.001   0.000   0.001

编辑:这是另一种可能性;它只是依次组合每一列。

> system.time({
+   n <- 1:ncol(someParts[[1]])
+   names(n) <- names(someParts[[1]])
+   result <- as.data.frame(lapply(n, function(i) 
+                           unlist(lapply(someParts, `[[`, i))))
+ })
   user  system elapsed 
  0.810   0.000   0.813  

尽管如此,仍然不如使用矩阵快。

编辑2:

如果您只有数字和因子,那么将所有内容都转换为数字,rbind然后将必要的列转换回因子并不难。这假设所有因素都具有完全相同的水平。从整数转换为因子也比从数字更快,所以我先强制转换为整数。

someParts2 <- lapply(someParts, function(x)
                     matrix(unlist(x), ncol=ncol(x)))
result<-as.data.frame(do.call(rbind, someParts2))
a <- someParts[[1]]
f <- which(sapply(a, class)=="factor")
for(i in f) {
  lev <- levels(a[[i]])
  result[[i]] <- factor(as.integer(result[[i]]), levels=seq_along(lev), labels=lev)
}

我的系统上的时间是:

   user  system elapsed 
   0.090    0.00    0.091 
于 2011-05-12T15:40:11.090 回答
5

不是一个巨大的提升,但从包中交换会rbind减少大约 10% 的运行时间(在我的机器上使用示例数据集)。rbind.fillplyr

于 2011-05-12T16:38:38.460 回答
4

如果你真的想data.frame更快地操作你的 s,我建议使用 packagedata.table和 function rbindlist()。我没有进行广泛的测试,但我的数据集(3000 个数据帧,1000 行 x 40 列)rbindlist()只需要 20 秒。

于 2013-07-10T11:35:43.347 回答
3

This is ~25% faster, but there has to be a better way...

system.time({
  N <- do.call(sum, lapply(someParts, nrow))
  SP <- as.data.frame(lapply(someParts[[1]], function(x) rep(x,N)))
  k <- 0
  for(i in 1:length(someParts)) {
    j <- k+1
    k <- k + nrow(someParts[[i]])
    SP[j:k,] <- someParts[[i]]
  }
})
于 2011-05-12T17:00:28.977 回答
1

确保您将数据框绑定到数据框。将列表绑定到数据框时,性能大幅下降。

于 2016-06-24T18:05:06.350 回答