【问题标题】:Why is the [[]] operator subsetting faster than the [,] operator in R?为什么 [[]] 运算符的子集化速度比 R 中的 [,] 运算符快?
【发布时间】:2016-07-08 07:51:46
【问题描述】:

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

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

【问题讨论】:

  • 如果您在小数据集上进行基准测试,它不会给出正确的输出
  • 您可以在基准测试中再添加两种方法:df[df == -99] &lt;- NAis.na(df) &lt;- df == -99
  • @Arun 感谢您的提示。但据我所知,$ 运算符是 [["x", exact = FALSE]] 的缩写。因此,与 [,] 运算符相比,它并没有真正的帮助,或者?
  • 在您的问题中,您比较[ VS [[。你可以比较[.data.frame[[.data.frame,但实际上你也在比较[&lt;-.data.frame[[&lt;-.data.frame。您可以浏览这些函数并根据参数的数量等找到可能(如果有的话)增加计算时间的内容。

标签: r dataframe subset


【解决方案1】:

一种更快的方法是使用data.table中的set

 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 的值,所以一次又一次地运行它没有意义。

【讨论】:

  • OP 询问为什么 meth1 比 meth2 更快,因为样本太小而无法得出结论。您的答案是“meth3 更快”,并在一个不错的样本中对 meth3 进行了基准测试。我没有发现这回答了 OP 问题。这是我的看法,你可能不同意
  • 版主说明:停止争论投票;如果您想讨论投票行为,请将其带到 Meta 或聊天。
  • 感谢您提供更快的解决方案 akrun。我会记住的。为什么您认为 [ 确实会产生 data.frame 的开销? Drop = True,因此结果 df[, i]df[[i]] 都是向量
  • @Tobi_R [[ 用于子集单个列或单个列表元素。通过使用[,它可以用于对多个列进行子集化,而使用,,行也可以出现。虽然,我们在, 的左侧将其设为空白,但我猜它仍会检查行部分。
  • [[,同样,调度它的“data.frame”方法,它也接受行参数mtcars[2, 6]mtcars[[2, 6]]
【解决方案2】:

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

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

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

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

【讨论】:

  • 请注意,在您的问题中,您不仅在 extracting 值,而且还在 assigning 值给“data.frame”
  • 嗯,你是绝对正确的。我没有将 assigning 部分视为我陈述的问题的一部分。感谢您指出这一点。
猜你喜欢
  • 1970-01-01
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-29
  • 2021-10-28
相关资源
最近更新 更多