【发布时间】:2014-10-31 01:40:44
【问题描述】:
我有一些看起来像这样的 R 代码:
library(dplyr)
library(datasets)
iris %.% group_by(Species) %.% filter(rank(Petal.Length, ties.method = 'random')<=2) %.% ungroup()
给予:
Source: local data frame [6 x 5]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 4.3 3.0 1.1 0.1 setosa
2 4.6 3.6 1.0 0.2 setosa
3 5.0 2.3 3.3 1.0 versicolor
4 5.1 2.5 3.0 1.1 versicolor
5 4.9 2.5 4.5 1.7 virginica
6 6.0 3.0 4.8 1.8 virginica
这按物种分组,每个组只保留Petal.Length最短的两个。我的代码中有一些重复,因为我为不同的列和数字做了几次。例如:
iris %.% group_by(Species) %.% filter(rank(Petal.Length, ties.method = 'random')<=2) %.% ungroup()
iris %.% group_by(Species) %.% filter(rank(-Petal.Length, ties.method = 'random')<=2) %.% ungroup()
iris %.% group_by(Species) %.% filter(rank(Petal.Width, ties.method = 'random')<=3) %.% ungroup()
iris %.% group_by(Species) %.% filter(rank(-Petal.Width, ties.method = 'random')<=3) %.% ungroup()
我想把它提取到一个函数中。天真的方法不起作用:
keep_min_n_by_species <- function(expr, n) {
iris %.% group_by(Species) %.% filter(rank(expr, ties.method = 'random') <= n) %.% ungroup()
}
keep_min_n_by_species(Petal.Width, 2)
Error in filter_impl(.data, dots(...), environment()) :
object 'Petal.Width' not found
据我了解,rank(Petal.Length, ties.method = 'random') <= 2 表达式是在不同的上下文中计算的,由 filter 函数引入,它为 Petal.Length 表达式提供了含义。我不能只为 Petal.Length 换一个变量,因为它将在错误的上下文中进行评估。我尝试使用substitute 和eval 的不同组合,并阅读了此页面:Non-standard evaluation。我想不出合适的组合。我认为问题可能是我不只是想将调用者 (Petal.Length) 的表达式传递到 filter 进行评估 - 我想构造一个新的更大的表达式 (rank(Petal.Length, ties.method = 'random') <= 2) 和然后将整个表达式传递给 filter 进行评估。
- 如何将 this 表达式重构为函数?
- 更一般地说,我应该如何将 R 表达式提取到函数中?
- 更一般地说,我是不是抱着错误的心态来处理这个问题?在我熟悉的更主流的语言(例如 Python、C++、C#)中,这是一个相对简单的操作,我一直希望这样做以消除代码中的重复。在 R 中,似乎(至少对我来说)非标准评估可以使它成为一个非常不明显的操作。我应该完全做其他事情吗?
【问题讨论】:
-
我相信 hadley 正在使用lazyeval 包来解决这个问题,它将提供通用框架来在其他包中实现标准版本的 NSE 函数。