如何在dplyr :: filter中使用变量?

nac*_*cab 18 r dplyr

我有一个与数据框中的列同名的变量:

df <- data.frame(a=c(1,2,3), b=c(4,5,6))
b <- 5
Run Code Online (Sandbox Code Playgroud)

我想把行df$b == b放到哪里,但是dplyr将其解释为df$b == df$b:

df %>% filter(b == b) # interpreted as df$b == df$b
#   a b
# 1 1 4
# 2 2 5
# 3 3 6
Run Code Online (Sandbox Code Playgroud)

如果我更改变量名称,它可以工作:

B <- 5
df %>% filter(b == B) # interpreted as df$b == B
#   a b
# 1 2 5
Run Code Online (Sandbox Code Playgroud)

我想知道是否有更好的方法来filter指出b外部变量.

小智 20

最近我发现这是解决这个问题的一个优雅的解决方案,尽管我只是开始关注它是如何工作的.

df %>% filter(b == !!b)

这是语法糖

df %>% filter(b == UQ(b))

我对此的高级认识是UQ(un-quote)操作导致在筛选操作之前评估其内容,因此不在data.frame中对其进行求值.

本文在"准引用"下对此进行了描述.我要注意的是,本文还包括一些与NSE相关的类似问题的解决方案.

对上述内容的一个警告 - 我倾向于将!!表达式的结尾放在上面.如果你把它放在开头:

df %>% filter(!!b == b)

那么它将被评估为 df %>% filter(!!(b == b))

这相当于写作df %>% filter(5 == 5):

> quo(filter(df, !!b == b))
<quosure: global>
~filter(df, TRUE) 
Run Code Online (Sandbox Code Playgroud)

UQ()上面的选项没有这个问题.


nis*_*ist 13

您可以使用该get函数从环境中获取变量的值.

df %>% filter(b == get("b")) # Note the "" around b
Run Code Online (Sandbox Code Playgroud)

  • 这不再适用于“dplyr”0.7.8。它返回所有行。 (5认同)
  • 使用 `df %&gt;% filter(b == get("b", envir = .env))` 指定环境仍然有效 (dplyr 0.8.3) (3认同)

LMc*_*LMc 9

rlang,它是用 , 导入的dplyr,具有.env.data代词,正好适合这种情况,因为数据屏蔽而需要明确。要显式引用数据框中的列,.data请使用并显式引用您的环境使用.env

library(dplyr)
df %>% 
  filter(.data$b == .env$b) # b == .env$b works the same here

  a b
1 2 5
Run Code Online (Sandbox Code Playgroud)

从文档中:

请注意,.data 只是一个代词,它不是真正的数据框。这意味着您无法获取其名称或在 .data 的内容上映射函数。同样,.env 也不是实际的 R 环境。

您不一定需要在此处使用,因为评估首先.data$b在数据框中搜索具有该名称的列(正如您所发现的)。


Axe*_*man 8

作为一般解决方案,您可以使用SE(标准评估)版本filter,即filter_.在这种情况下,事情变得有点混乱,因为你在一个表达式中混合了一个变量和一个"外部"常量.以下是使用该interp功能的方法:

library(lazyeval)
df %>% filter_(interp(~ b == x, x = b))
Run Code Online (Sandbox Code Playgroud)

如果您想使用更多值,b可以写:

df %>% filter_(interp(~ b == x, .values = list(x = b)))
Run Code Online (Sandbox Code Playgroud)

  • `filter_` 现已弃用。 (5认同)