为啥`[`比`subset`好?

Posted

技术标签:

【中文标题】为啥`[`比`subset`好?【英文标题】:Why is `[` better than `subset`?为什么`[`比`subset`好? 【发布时间】:2012-04-09 05:54:27 【问题描述】:

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

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

而不是[函数:

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

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

    我发现代码从左到右读起来更好。即使对 R 一无所知的人也能知道上面的 subset 语句在做什么。

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

所以我过着幸福的生活,到处使用subset,因为它更短且可读性更好,甚至向我的 R 程序员同伴宣传它的美感。但是昨天我的世界分崩离析。在阅读subset 文档时,我注意到这一部分:

警告

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

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

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

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

【问题讨论】:

with(airquality, airquality[Month == 8 & Temp > 90, ])一起使用的数量略少(但比子集少) 你也可以看看 'The R Inferno' burns-stat.com/pages/Tutor/R_inferno.pdf987654321@的 Circlces 8.2.31 和 8.2.32 尝试使用 data.table,默认语法类似于 airquality[Month == 8 & Temp > 90,] - 可读性强,速度更快。 好的。因此,如果子集不好使用- [ vs. dplyr::filter() 怎么样? 对于那些想知道的人,dplyr::filter 也有同样的问题。 IE。如果环境碰巧有一个具有该名称的变量,它将使用它而不是数据框中的变量。使调试混乱! 【参考方案1】:

另外[ 更快:

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

【讨论】:

是和不是。我认为您看到的时差是由于两件事。 1) 一个小的(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 ,] )【参考方案2】:

@James 在 cmets 中很好地回答了这个问题,并指出 Hadley Wickham 对 subset(以及类似的函数) [here] 的危险性做出了很好的解释。去读吧!

读起来有点长,所以在这里记录一下 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)

(运行它们自己看看,这太疯狂了。)

【讨论】:

我有一些新手问题需要澄清吗?当我们写subset(mtcars, cyl == 4)(在顶层)时,R 在哪里寻找 cyl?如果它查看传递给subset()mtcars 对象,那么即使scramble 在另一个函数中,它是否应该能够找到cyl,因为mtcars 仍在传递给它?如果我的问题没有意义,您可以详细说明为什么 R 无法再找到 cyl。谢谢! @Anh Inside subset.data.frame,我们当时试图评估的只是condition。这在mtcars 中不存在。所以subset.data.frame 使用enclos = parent.frame() 来确保condition 被正确评估为cyl == 4。但是后来我们又回到了封闭的框架,现在当 R 寻找cyl 时,它不再寻找mtcars 的内部。如果我们不使用enclossubset(mtcars,cyl == a) 之类的东西根本就不起作用。 @MikePalmice 确实如此。 subset.data.frame 的最后一行是x[r, vars, drop = drop]。问题是如何从未引用的 subsetselect 参数中获取可以有效传递给 [.data.frame 的内容。 这是一个如此古老的问题/答案,有如此多的支持 - 很明显我忽略了一些东西?对我来说,您的示例代码不能单独运行。 Hadley 的示例包含另一个名为 'subset2' 的函数的预创建...[subset() 之间的重要区别在于此函数... 感谢您的检查。我可能误解了您的代码的意图。但是使用[ 将其替换为子集,这会导致与使用subset 的代码相同的“奇怪”结果 - 至少在这里:/ 也清理 R 3.4.3

以上是关于为啥`[`比`subset`好?的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode 78. Subsets (backtracking) 90 subset

90. Subsets II

R语言 subset()函数用法

LeetCode第[78]题(Java):Subsets(求子集)扩展——第[90]题:Subsets 2

78. Subsets

R子集subset