避免在dplyr中向量和变量名之间发生冲突

时间:2014-11-10 14:37:56

标签: r lazy-evaluation dplyr

我在一个以data.frame dplyr为参数的函数中使用df

在某些时候,我想基于我刚刚创建的名为n的向量进行过滤。但是,如果n也是输入data.frame中变量的名称,那么这将无法工作。

library(dplyr)
df <- data.frame(n = c(0L, 0L))
n <- c(1L, 1L)
filter(df, n == 1L)
#> [1] n
#> <0 rows> (or 0-length row.names)

由于该函数适用于任何数据帧,我想避免这种情况。我尝试使用与全局环境关联的公式/惰性对象,但这返回了相同的结果:

a <- ~ n == 1L
filter_(df, a)
#> [1] n
#> <0 rows> (or 0-length row.names)
a <- lazy(n == 1L)
filter_(df, a)
#> [1] n
#> <0 rows> (or 0-length row.names)

有优雅的方式吗?

3 个答案:

答案 0 :(得分:4)

因为n既是变量名也是包含值的对象,使用 lazyeval 中的interp并使用n作为值(而不是变量)似乎做你想做的事。

library(lazyeval)
filter_(df, interp(~n == 1L, n = n))

  n
1 0
2 0

我首先尝试了更复杂的

filter_(df, interp(~n == 1L, .values = list(n = n)))

但更简单的版本似乎也是一样的。

答案 1 :(得分:1)

所有先前的答案都已过时,因为dplyr现在支持rlang的引用和取消引用语义。

您可以简单地使用!! n来防止引用n(并解释为n列)。

library(dplyr)
df <- data.frame(n = c(0L, 0L))
n <- c(1L, 1L)
filter(df, !! n == 1L)

##   n
## 1 0
## 2 0

另一个使用经典mtcars的示例:

gear <- 5

# gear == gear is true for all rows!
# this returns the whole dataset
filter(mtcars, gear == gear)

# this works as intended
filter(mtcars, gear == !! gear)

##   mpg cyl  disp  hp drat    wt qsec vs am gear carb
## 1 26.0   4 120.3  91 4.43 2.140 16.7  0  1    5    2
## 2 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2
## 3 15.8   8 351.0 264 4.22 3.170 14.5  0  1    5    4
## 4 19.7   6 145.0 175 3.62 2.770 15.5  0  1    5    6
## 5 15.0   8 301.0 335 3.54 3.570 14.6  0  1    5    8

答案 2 :(得分:0)

正如你所说,使用NSE和filter_可能有办法做到这一点,但不知道如何做。我希望像df %>% filter_(n == 1L)这样的东西可以工作 - 它确实使用了正确的n,但它只使用了向量的第一个条目。这个替代方案怎么样

df %>% `[`(n == 1L, ,drop = F)
#  n
#1 0
#2 0