为什么[[]]运算符子集比R中的[,]运算符更快?

时间:2016-07-08 07:51:46

标签: r dataframe subset

我对一些解决方案进行了基准测试,以替换每列的缺失值。

set.seed(11)
df <- data.frame(replicate(3, sample(c(1:5, -99), 6, rep = TRUE)))
names(df) <- letters[1:3]

fix_na <- function(x) {
  x[x == -99] <- NA
}

microbenchmark(
  for(i in seq_along(df)) df[, i] <- fix_na(df[, i]),
  for(i in seq_along(df)) df[[i]] <- fix_na(df[[i]]),
  df[] <- lapply(df, fix_na)
)

Unit: microseconds
                                                     expr     min       lq     mean      median   uq     max neval
 for (i in seq_along(df)) df[, i] <- fix_na(df[, i]) 179.167 191.9060 206.1650 204.2335 211.630 364.497   100
 for (i in seq_along(df)) df[[i]] <- fix_na(df[[i]])  83.420  92.8715 104.5787  98.0080 109.309 204.645   100
                          df[] <- lapply(df, fix_na) 105.199 113.4175 128.0265 117.9385 126.979 305.734   100

为什么[[]]运算符将数据帧的子集比[,]运算符快2倍?

修改

我从docendo discimus中包含了两个推荐的调用,并增加了数据量。

set.seed(11)
df1 <- data.frame(replicate(2000, sample(c(1:5, -99), 500, rep = TRUE)))
df2 <- df1
df3 <- df1
df4 <- df1
df5 <- df1

结果改变是,但我的问题仍然存在:[[]]的执行速度比[,]

Unit: milliseconds
                                                        expr       min        lq       mean        median      uq 
 for (i in seq_along(df1)) df1[, i] <- fix_na(df1[, i]) 301.06608 356.48011 377.31592 372.05625 392.73450 472.3330
 for (i in seq_along(df2)) df2[[i]] <- fix_na(df2[[i]]) 238.72005 287.55364 301.35651 298.05950 314.04369 386.4288
                           df3[] <- lapply(df3, fix_na) 170.53264 189.83858 198.32358 193.43300 202.43855 284.1164
                                 df4[df4 == -99] <- NA  75.05571  77.64787  85.59757  80.72697  85.16831  363.2223
                              is.na(df5) <- df5 == -99  74.44877  77.81799  84.22055  80.06496  83.01401  347.5798

2 个答案:

答案 0 :(得分:0)

更快的方法是使用set

中的data.table
 library(data.table)
 setDT(df)
 for(j in seq_along(df)){
  set(df, i = which(df[[j]]== -99), j=j, value = NA)
 }

关于OP关于使用[[[进行基准测试的问题,[[会在没有.data.frame开销的情况下提取该列。但是,我会在更大的数据集上进行基准测试,以发现任何差异。另外,当我们在相同数据上分配NA时,当我们再次执行操作时,它不会做任何更改。

基准

set.seed(11)
df1 <- data.frame(replicate(2000, sample(c(1:5, -99), 500, rep = TRUE)))
df2 <- copy(df1)
df3 <- copy(df1)
df4 <- copy(df1)
df5 <- copy(df1)
df6  <- copy(df1)

 f1 <- function() for (i in seq_along(df1)) df1[, i] <- fix_na(df1[, i])
 f2 <- function() for (i in seq_along(df2)) df2[[i]] <- fix_na(df1[[i]])
 f3 <- function()  df3[] <- lapply(df3, fix_na)
 f4 <- function()  df4[df4 == -99] <- NA 
 f5 <- function()   is.na(df5) <- df5 == -99

 f6 <- function() {
   setDT(df6)
   for(j in seq_along(df)){
     set(df, i = which(df[[j]]== -99), j=j, value = NA)
   }  
  }

 t(sapply(paste0("f", 1:6), function(f) system.time(get(f)())))[,1:3]
 #   user.self sys.self elapsed
 #f1      0.29        0    0.30
 #f2      0.22        0    0.22
 #f3      0.11        0    0.11
 #f4      0.31        0    0.31
 #f5      0.31        0    0.32
 #f6      0.00        0    0.00

在这里,我使用system.time作为OP的帖子中的函数已经在第一次运行中替换了NA的值,所以没有必要一次又一次地运行它。

答案 1 :(得分:-1)

在Arun建议的网站上找到一个非常类似的问题的答案:adv-r.had.co.nz/Performance.html

从数据框中提取单个值部分,它说:

  

Blockquote以下微基准测试显示了七种从内置mtcars数据集访问单个值(右下角的数字)的方法。性能的变化令人吃惊:最慢的方法比最快的方法长30倍。 没有必要在性能上有这么大的差异。只是没有人有时间来修复它。

在不同的选择方法中,两个运算符[[和[与我观察到的结果相同)。 [[优于[