为什么“[”比“子集”好?

Why is `[` better than `subset`?

提问人:flodel 提问时间:3/25/2012 最后编辑:Communityflodel 更新时间:11/15/2016 访问量:108589

问:

当我需要过滤 data.frame 时,即提取满足特定条件的行,我更喜欢使用以下函数:subset

subset(airquality, Month == 8 & Temp > 90)

而不是功能:[

airquality[airquality$Month == 8 & airquality$Temp > 90, ]

我偏爱有两个主要原因:

  1. 我发现代码从左到右读起来更好。即使是对 R 一无所知的人也能看出上面的陈述在做什么。subset

  2. 由于列可以在表达式中称为变量,因此我可以保存一些击键。在上面的示例中,我只需要用 键入一次,但用 键入三次。selectairqualitysubset[

所以我过得很开心,到处都在使用,因为它更短,读起来更好,甚至向我的 R 程序员同事宣传它的美感。但昨天我的世界分崩离析了。在阅读文档时,我注意到这一部分:subsetsubset

警告

这是一项旨在以交互方式使用的便利功能。对于编程,最好使用像 [ 这样的标准子集函数,特别是对参数子集的非标准计算可能会产生意想不到的后果。

有人可以帮忙澄清作者的意思吗?

首先,“交互式使用”是什么意思?我知道什么是交互式会话,而不是在 BATCH 模式下运行的脚本,但我看不出它应该有什么区别。

那么,您能否解释一下“参数子集的非标准评估”以及为什么它是危险的,也许可以举个例子?

筛选器 子集 R-FAQ

评论

16赞 Tyler Rinker 3/25/2012
它略少(但坚果少于子集)与,with(airquality, airquality[Month == 8 & Temp > 90, ])
10赞 Patrick Burns 3/26/2012
您可能还查看了“The R Inferno”的 Cirlces 8.2.31 和 8.2.32 burns-stat.com/pages/Tutor/R_inferno.pdf
13赞 Stian Håklev 9/28/2013
尝试 data.table,默认语法类似于 airquality[Month == 8 & Temp > 90,] - 非常可读,而且速度更快。
3赞 userJT 2/12/2015
好的,所以如果子集不好用 - [ vs. dplyr::filter() 呢?
6赞 CoderGuy123 1/28/2017
对于那些想知道的人,也有同样的问题。也就是说,如果环境碰巧有一个具有该名称的变量,它将使用它而不是数据框中的变量。使调试混乱!dplyr::filter

答:

265赞 4 revs, 3 users 91%joran #1

这个问题在@James的评论中得到了很好的回答,指出了哈德利·威克姆(Hadley Wickham)对[这里]的危险(以及类似的功能)的出色解释。去读吧!subset

这是一本有点长的书,所以在这里记录一下哈德利使用的例子可能会有所帮助,这个例子最直接地解决了“什么会出错?”的问题:

Hadley 建议以下示例:假设我们想要使用以下函数对数据框进行子集,然后重新排序:

scramble <- function(x) x[sample(nrow(x)), ]

subscramble <- function(x, condition) {
  scramble(subset(x, condition))
}

subscramble(mtcars, cyl == 4)

这将返回错误:

eval(expr, envir, enclos) 中的错误:找不到对象“cyl”

因为 R 不再“知道”在哪里可以找到名为“cyl”的对象。他还指出,如果偶然在全球环境中存在一个叫做“cyl”的物体,就会发生真正奇怪的事情:

cyl <- 4
subscramble(mtcars, cyl == 4)

cyl <- sample(10, 100, rep = T)
subscramble(mtcars, cyl == 4)

(运行它们,亲眼看看,这太疯狂了。

评论

4赞 Heisenberg 10/29/2013
我可以有一些新手问题需要澄清吗?当我们编写(在顶层)时,R 在哪里寻找 cyl?如果它查看传递给 的对象,那么即使它位于另一个函数中,它不应该能够找到,因为仍在传递给它?如果我的问题没有意义,你可以更详细地解释为什么 R 再也找不到了。谢谢!subset(mtcars, cyl == 4)mtcarssubset()cylscramblemtcarscyl
4赞 joran 10/29/2013
@Anh 在内部,我们当时试图评估的东西只是 .这在 中是不存在的。因此,用于确保被正确评估为 .但是,我们又弹出到封闭框,现在当 R 查找它时,它不再查看 .如果我们不使用 ,类似的东西根本不起作用。subset.data.frameconditionmtcarssubset.data.frameenclos = parent.frame()conditioncyl == 4cylmtcarsenclossubset(mtcars,cyl == a)
1赞 joran 10/3/2017
@MikePalmice确实如此。的最后一行是 。问题在于如何从不加引号的参数到可以有效传递给 .subset.data.framex[r, vars, drop = drop]subsetselect[.data.frame
1赞 tjebo 6/21/2018
这是一个如此古老的问题/答案,有如此多的赞成票 - 很明显我忽略了一些东西??对我来说,您的示例代码本身不起作用。Hadley 的示例包含另一个名为“subset2”的函数的预创建......和 之间的重要区别在于这个函数......[subset()
1赞 tjebo 6/21/2018
感谢您的检查。我可能误解了你代码的意图。但是用子集替换它 ,这会导致与您的代码 using 相同的“奇怪”结果 - 至少在这里 :/也清理 R 3.4.3[subset
36赞 bartektartanus 4/5/2014 #2

也更快:[

require(microbenchmark)        
microbenchmark(subset(airquality, Month == 8 & Temp > 90),airquality[airquality$Month == 8 & airquality$Temp > 90,])
    Unit: microseconds
                                                           expr     min       lq   median       uq     max neval
                     subset(airquality, Month == 8 & Temp > 90) 301.994 312.1565 317.3600 349.4170 500.903   100
     airquality[airquality$Month == 8 & airquality$Temp > 90, ] 234.807 239.3125 244.2715 271.7885 340.058   100

评论

42赞 flodel 4/6/2014
是的,也不是。我认为你所看到的时差是由于两件事造成的。1) 开销很小(< 100 微秒),并且 2) 不同 删除筛选器计算结果为 的行。这样做,你会发现它们在“公平”比较时都一样快:subset[NAx <- do.call(rbind, rep(list(airquality), 100)); microbenchmark(subset(x, Month == 8 & Temp > 90),{ i <- x$Month == 8 & x$Temp > 90; x[!is.na(i) & i ,] })