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

Ant*_*uve 5 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))
Run Code Online (Sandbox Code Playgroud)

以下时间:

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 
Run Code Online (Sandbox Code Playgroud)

迭代大数据帧的两个命令要慢一些.那些循环只是在内存中写入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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这完全解决了性能问题.

And*_*rie 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 
Run Code Online (Sandbox Code Playgroud)

要解决此问题,您最好的操作是仅修改一次数据框:

untracemem(toto.big)

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

   user  system elapsed 
   0.02    0.00    0.02
Run Code Online (Sandbox Code Playgroud)

在那些在循环(或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 
Run Code Online (Sandbox Code Playgroud)

您可以<-.data.frame通过`<-.data.frame`在控制台中键入来查看代码.