我有一些像这样的 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
这个操作按照物种分组,并且在每个分组中只保留两个花瓣长度最短的数据。由于我需要对不同的列和数量进行相同的操作,所以我的代码存在一些重复。例如:
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
,阅读过这个页面:非标准评估。我无法找到适当的组合。我认为问题可能是我不仅想从调用者(Petal.Length
)传递一个表达式给filter
去评估-我想构造一个新的更大的表达式(rank(Petal.Length, ties.method = 'random') <= 2
),然后将整个表达式传递给filter
去评估。
- 如何将此表达式重构为函数?
- 更一般地说,如何将R表达式提取到函数中?
- 更一般地说,我的思路是否有误?在我熟悉的其他主流语言中(e.g. Python, C++, C#),这是一个相对简单的操作,我经常希望通过它来消除代码中的重复。在R中,非标准评估似乎使它成为一个非常不明显的操作(至少对我来说)。我应该完全做其他事情吗?