为什么`[`优于`subset`?


400

当我需要过滤data.frame,即提取满足某些条件的行时,我更喜欢使用以下subset功能:

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

而不是[功能:

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

我偏爱的主要原因有两个:

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

  2. 因为列可以在select表达式中称为变量,所以我可以节省一些击键。在上面的示例中,我只需输入airquality一次subset,但只需输入3次[

因此,我过着幸福的生活,subset在任何地方都可以使用它,因为它更短且读起来更好,甚至向我的R编码员倡导它的美。但是昨天我的世界崩溃了。在阅读subset文档时,我注意到以下部分:

警告

这是旨在交互使用的便利功能。对于编程,最好使用标准的子集函数,例如[,尤其是参数子集的非标准评估会产生意想不到的后果。

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

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

然后,请您解释一下“ 论点子集的非标准评估 ”,为什么这样做很危险,也许可以举个例子?


14
它的使用要少一些(但要比子集少一些),with(airquality, airquality[Month == 8 & Temp > 90, ])
Tyler Rinker 2012年

7
您也可以看看'The R Inferno'的Cirlces
Patrick Burns

9
尝试使用data.table,默认语法类似于airquality [Month == 8&Temp> 90,]-可读性强,并且速度更快。
StianHåklev2013年

3
好。所以如果子集不好用-[vs. dplyr :: filter()呢?
userJT

4
对于那些想知道的人,dplyr::filter有同样的问题。即,如果环境恰好具有该名称的变量,它将使用它而不是数据框中的变量。使混乱的调试!
Deleet

Answers:


241

@James的评论很好地回答了这个问题,并指出了Hadley Wickham对[此处]subset(以及类似功能)的危险性的出色解释。去读吧!

读起来有些长,因此在这里记录Hadley使用的最直接解决“什么地方可能出问题?”这个问题的示例可能会有所帮助:

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)

(运行它们并自己查看,这非常疯狂。)


2
我可以问一些新手问题吗?当我们写subset(mtcars, cyl == 4)(顶层)时,R在哪里寻找cyl?如果它查看mtcars传递给的对象subset(),那么cyl即使仍在scramble另一个函数中,它是否也无法找到,因为mtcars它仍在传递给它?如果我的问题没有道理,您可以详细说明为什么R不再可以找到cyl。谢谢!
海森堡

4
@Anh Inside subset.data.frame,我们当时要评估的只是condition。在中不存在mtcars。因此subset.data.frame用于enclos = parent.frame()确保condition正确评估为cyl == 4。但是,然后我们跳回到了封闭的框架,现在当R查找时,cyl它不再在的内部mtcars。如果我们不使用enclos,类似的东西subset(mtcars,cyl == a)将根本无法工作。
joran

有谁知道为什么subset()不能只在幕后实现更快,更安全的[,]方法?
比约克斯排名第一的球迷,

1
@MikePalmice确实如此。的最后一行subset.data.framex[r, vars, drop = drop]。问题是如何从加引号得到subsetselect论证的东西,你可以有效地传递给[.data.frame
joran

@joran知道了,谢谢。您如何考虑是否使用dplyr的过滤器而不是[]
比约克斯排名第一的球迷

30

[更快:

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

36
是的,没有。我认为您看到的时差是由于两件事。1)较小的开销(<100微秒),以及2)subset不像[移除过滤器求值为的行NA。这样做,您将看到它们在“相当”比较时都一样快:x <- 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 ,] })
flodel 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.