【问题标题】:Writing to data frame with many lines is very slow写入多行数据帧非常慢
【发布时间】:2014-08-25 12:17:01
【问题描述】:

让我们考虑下面的三个数据框:

toto.small  <- data.frame(col1=rep(1,850), col2=rep(2,850))
toto.medium <- data.frame(col1=rep(1,85000), col2=rep(2,85000))
toto.big    <- data.frame(col1=rep(1,850000), col2=rep(2,850000))

以及下面的时间安排:

system.time(for(i in 1:100) { toto.small[i,2] <- 3 })
user  system elapsed 
0.004   0.000   0.006 

system.time(for(i in 1:100) { toto.medium[i,2] <- 3 })
user  system elapsed 
0.088   0.000   0.087 

system.time(for(i in 1:100) { toto.big[i,2] <- 3 })
user  system elapsed 
2.248   0.000   2.254 

在大数据帧上迭代小数据帧要慢两个数量级。 这些循环只是在内存中写入 100 个预先分配的元素;时间甚至不应该取决于数据帧的总长度。

有人知道这是什么原因吗?

对于数据表以及应用函数,我仍然会得到类似的时差。

编辑 1:R 3.0.2 与 R 3.1

对于那些好奇的人,这里有 R v. 3.1 和 3.0.2 的 data.table 和 data.frame 的时间安排(我每个测量 3 次):

R 3.0.2

      type   size time1 time2 time3
data frame  small 0.005 0.005 0.005
data frame medium 0.074 0.077 0.075
data frame    big 3.184 3.373 3.101
data table  small 0.048 0.048 0.047
data table medium 0.073 0.068 0.066
data table    big 0.615 0.621 0.593

R 3.1

      type   size time1 time2 time3
data frame  small 0.004 0.004 0.004
data frame medium 0.021 0.020 0.022
data frame    big 0.221 0.207 0.243
data table  small 0.055 0.055 0.055
data table medium 0.076 0.076 0.076
data table    big 0.705 0.699 0.663

R 3.1 更快,但我们仍然会慢一些;数据表也一样。

编辑 2:使用函数集

R 3.1.0 上的相同数字,使用函数“set”而不是“[]”运算符

      type   size        time1        time2        time3
data frame  small 0.0249999999 0.0020000000 0.0009999999
data frame medium 0.0010000000 0.0009999999 0.0010000000
data frame    big 0.0010000000 0.0000000000 0.0009999999
data table  small 0.0009999999 0.0209999999 0.0000000000
data table medium 0.0009999999 0.0009999999 0.0010000000
data table    big 0.0000000000 0.0029999999 0.0009999999

这完全解决了性能问题。

【问题讨论】:

  • 你知道 Patrick Burns 写的 R inferno 吗?
  • 感谢您的建议!我确实知道 R inferno(它是我的主要 R 参考资料之一:p),但我无法找到任何准确的相关信息来解决这个问题。
  • 就像一个注释,toto.big[,2][i] 大约快 4 倍。
  • 这里有一个相关的问答:stackoverflow.com/questions/23388893/…
  • 谢谢 BenBarnes,这与我的问题完全相关。很抱歉自己找不到。

标签: r


【解决方案1】:

您的代码很慢,因为函数[.&lt;-data.frame 会在您每次修改对象时复制底层对象。

如果您跟踪内存使用情况,它就会变得清晰:

tracemem(toto.big)
system.time({
  for(i in 1:100) { toto.big[i,2] <- 3 }
})


tracemem[0x000000001d416b58 -> 0x000000001e08e9f8]: system.time 
tracemem[0x000000001e08e9f8 -> 0x000000001e08eb10]: [<-.data.frame [<- system.time 
tracemem[0x000000001e08eb10 -> 0x000000001e08ebb8]: [<-.data.frame [<- system.time 
tracemem[0x000000001e08ebb8 -> 0x000000001e08e7c8]: system.time 
tracemem[0x000000001e08e7c8 -> 0x000000001e08e758]: [<-.data.frame [<- system.time 
tracemem[0x000000001e08e758 -> 0x000000001e08e800]: [<-.data.frame [<- system.time 
....
tracemem[0x000000001e08e790 -> 0x000000001e08e838]: system.time 
tracemem[0x000000001e08e838 -> 0x000000001e08eaa0]: [<-.data.frame [<- system.time 
tracemem[0x000000001e08eaa0 -> 0x000000001e08e790]: [<-.data.frame [<- system.time 
   user  system elapsed 
   4.31    1.01    5.29 

要解决这个问题,最好的做法是只修改一次数据框:

untracemem(toto.big)

system.time({
  toto.big[1:100, 2] <- 5
})

   user  system elapsed 
   0.02    0.00    0.02

如果在循环(或lapply)中计算值更方便,那么您可以在循环中对向量执行计算,然后在一个向量化分配中分配到数据帧中:

system.time({
  newvalues <- numeric(100)
  for(i in 1:100)newvalues[i] <- rnorm(1)
  toto.big[1:100, 2] <- newvalues
})

   user  system elapsed 
   0.02    0.00    0.02 

您可以通过在控制台中输入`&lt;-.data.frame` 来查看&lt;-.data.frame 的代码。

【讨论】:

  • 谢谢。我怀疑类似的事情,但我找不到验证它的方法。让我看看。
  • 顺便说一句,这取决于 R 的版本。正如stackoverflow.com/questions/23388893/… 中提到的,R 3.1.0 的性能更好(我使用的是 R 3.0.2)。
猜你喜欢
  • 2017-05-13
  • 1970-01-01
  • 2015-10-17
  • 1970-01-01
  • 2017-12-14
  • 1970-01-01
  • 2020-04-27
  • 2018-10-12
  • 1970-01-01
相关资源
最近更新 更多