为什么`[`比`子集更好?

flo*_*del 392 r subset filter r-faq

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

subset(airquality, Month == 8 & Temp > 90)
Run Code Online (Sandbox Code Playgroud)

而不是[功能:

airquality[airquality$Month == 8 & airquality$Temp > 90, ]
Run Code Online (Sandbox Code Playgroud)

我偏好有两个主要原因:

  1. 我发现代码从左到右读得更好.甚至那些对R一无所知的人也可以说出subset上面的陈述是做什么的.

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

所以我过着幸福快乐,subset随处可见,因为它更短,读起来更好,甚至为我的R编码员提倡它的美丽.但昨天我的世界崩溃了.在阅读subset文档时,我注意到这一节:

警告

这是一种便于交互使用的便利功能.对于编程,最好使用像[的标准子集函数,特别是参数子集的非标准评估可能会产生意想不到的后果.

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

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

那么,请你解释一下" 论证子集的非标准评估 "以及为什么它是危险的,或许可以提供一个例子?

jor*_*ran 236

这个问题在@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)
Run Code Online (Sandbox Code Playgroud)

这会返回错误:

eval(expr,envir,enclos)出错:找不到对象'cyl'

因为R不再"知道"在哪里找到名为'cyl'的对象.他还指出,如果在全球环境中有一个名为'cyl'的对象,可能会发生真正奇怪的事情:

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

cyl <- sample(10, 100, rep = T)
subscramble(mtcars, cyl == 4)
Run Code Online (Sandbox Code Playgroud)

(运行它们并亲眼看看,它非常疯狂.)

  • @Anh在`subset.data.frame`里面,我们在那时要评估的东西就是`condition`.那在`mtcars`中不存在.因此`subset.data.frame`使用`enclos = parent.frame()`来确保`condition`被正确评估为`cyl == 4`.但是后来我们重新回到了封闭的框架,现在当R寻找`cyl`时,它不再是在查看`mtcars`.如果我们不使用`enclos`,那么像`subset(mtcars,cyl == a)`这样的东西根本不起作用. (4认同)
  • 我可以提一些新手问题来澄清吗?当我们写`subset(mtcars,cyl == 4)`(在顶层)时,R在哪里寻找cyl?如果它查看传递给`subset()`的`mtcars`对象,那么即使`scramble`在另一个函数中也不应该找到`cyl`,因为`mtcars`仍然被传递给它?如果我的问题没有意义,你可以详细说明为什么R不能再找到`cyl`.谢谢! (2认同)
  • @Tjebo正如你所说,这个问题已经过时了,自从我写这篇文章以来,哈德利的源材料可能已经发生了变化.但这不是"我的"答案,它是一个社区维基,只是总结了其他东西.如果您认为它已过时,请修复它. (2认同)

bar*_*nus 29

[更快:

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

  • 是的,不是.我认为你所看到的时差是由两件事造成的.1)一个小的(<100微秒)开销和2)`子集`不像`[`删除过滤器评估为'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,]}) (34认同)