dplyr :: filter()的异常行为

时间:2019-04-10 13:57:47

标签: r filter dplyr tidyverse

感谢您的时间。

这可能是我忽略的一个明显问题,但是今天早上我使用dplyr::filter()遇到了一些意外行为。

使用filter()似乎可以工作,除非列名和对象名相等。有关详细信息,请参见下面的示例。

我期望data仅返回data$year匹配yeardata$month匹配month的行,但是它将返回所有值。

我之前已经做过很多次相同的操作,所以我不确定为什么这次会发生这种情况。

month重命名为month_by_a_different_name时,一切正常。有任何想法吗?感谢您的宝贵时间。

library(tidyverse)

# Example data
data <-
  tibble(
    year = c(2019, 2018, 2017),
    month = c("January", "February", "March"),
    value = c(1, 2, 3)
  )


# -----------------------------------------------

# Values to filter by
year <- 2019
month <-  "February"

# Assigning year and month to a different object name
year_by_a_different_name <- year
month_by_a_different_name <- month


# -----------------------------------------------

# Filtering using year and month doesn't work
data %>%
  dplyr::filter(year == year)        # Doesn't work

data %>%
  dplyr::filter(month == month)      # Doesn't work


# -----------------------------------------------

# Filtering using different names works
data %>%
  filter(year == year_by_a_different_name)       # Works

data %>% 
  filter(month == month_by_a_different_name)     # Works


# -----------------------------------------------

# Using str_detect() also doesn't work for month
data %>% 
  dplyr::filter(str_detect(month, month))


# -----------------------------------------------

# Works with base R
data[data$month == month, ]
data[data$year == year, ]


# -----------------------------------------------

# Objects are of same class
class(data$year) == class(year)      # TRUE
class(data$month) == class(month)    # TRUE

2 个答案:

答案 0 :(得分:4)

TLDR:使用filter(year == !!year)

这是由dplyr的非标准评估(NSE)引起的-无论是引用df$year还是外部变量year,这都是不明确的。 NSE使用所谓的“ quosures”来推断,当您在LHS上写year时,是指管道输入列的列。这种引用技巧使您可以引用tidyverse系列软件包中管道输入范围(即数据框列)中定义的名称,并通过(i)避免键入引号来使您的工作更加轻松到处都有标记;(ii)允许Rstudio为您提供自动完成建议。

但是,在您的情况下,RHS上的year旨在引用输入data.frame之外的内容,即使此处也使用了该名称。在这种情况下,!!(“ bangbang”)运算符告诉NSE,您的变量不应加引号,而应按原样进行评估。

您可以在此处找到更多信息:https://dplyr.tidyverse.org/articles/programming.html,尤其是“不同表达式”部分。在上方的小插图中:

  

在dplyr中(通常在tidyeval中)使用!!表示您希望取消对输入的引用,以便对其进行评估而不是引用。这给了我们一个功能,该功能实际上可以实现我们想要的功能。

答案 1 :(得分:3)

要在原始环境中评估表达式,您可以知道以下定义的位置,这一点都不漂亮。

data %>%
  dplyr::filter(year == !!.GlobalEnv$year)

或者您可以使用enquo

data %>%
  dplyr::filter(month == !!enquo(month))

从帮助页面help('enquo')

  

捕获表示形式

     

quo()enquo()与它们的expr类似,但可以捕获   表达式和其环境都在称为quosure的对象中。   该包装器包含对原始环境的引用,   那个表情被捕获了。跟踪环境   表达式很重要,因为这是函数和对象的所在   表达式中提到的已定义。

     

象是可以用eval_tidy()求值的对象,就像   符号或函数调用。因为他们总是在他们的评价   原始环境可以看作是允许   表达式从一个函数传到另一个函数,但是回传   评估后立即恢复到原始环境。