用许多行写入数据帧非常慢

时间:2014-07-04 08:06:22

标签: r

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

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

对于那些好奇的人来说,data.table和data.frame的时间安排为R v.3.1和3.0.2(我每次测量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上的相同数字,使用函数&#34; set&#34;而不是&#34; []&#34;操作

      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

这完全解决了性能问题。

1 个答案:

答案 0 :(得分:5)

您的代码很慢,因为每次修改对象时,函数[.<-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)中计算值更方便的情况下,您可以在循环中对向量执行计算,然后在一个向量化分配中分配到数据帧中: / p>

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 

您可以在控制台中输入<-.data.frame来查看`<-.data.frame`的代码。