dplyr::filter() 函数出现意外行为

4

感谢您的时间。

这可能是我忽视的一个明显的问题,但今天早上我使用 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

虽然不美观,但可以使用: filter(year == .GlobalEnv$year)。此外,如果加载了tidyverse包(其中包含dplyr),则不需要使用dplyr :: filter - Rui Barradas
2个回答

5
TLDR: 使用filter(year == !!year) 这是由于dplyr的非标准评估(NSE)引起的 - 不清楚您是在引用df$year还是外部变量year。 NSE使用所谓的“quosures”推断当您在LHS上写入year时,您正在引用管道输入的列的列。这种引号技巧允许您在tidyverse系列软件包中引用在管道输入范围内定义的名称(即数据框列),并通过(i)避免在各处键入引号和(ii)允许Rstudio为您提供自动完成建议使您的生活更轻松。
然而,在这种情况下,RHS上的year意味着引用输入数据框之外的东西,即使该名称也在那里使用。此时,!!(“bangbang”)运算符告诉NSE您的变量不应加引号,而应按原样计算。
您可以在此处找到更多信息:https://dplyr.tidyverse.org/articles/programming.html,特别是“不同表达式”一节。来自上面的手册的引文:
在dplyr(以及通常的tidyeval中),您使用!!表示要取消引用输入,以便对其进行评估,而不是引用。这给了我们一个实际执行所需操作的函数。

3

为了在原始环境中计算表达式,您可以像下面这样知道其定义位置,这并不是很好看。

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

或者你可以使用enquo函数。
data %>%
  dplyr::filter(month == !!enquo(month))

以下内容翻译自help('enquo')页面。

捕获quosures中的表达式

quo()enquo() 与它们的 expr 对应物相似,但是在一个叫做quosure的对象中捕获了 表达式和它的环境。这个包装器包含对原始环境的引用,在那里该表达式被捕获。跟踪表达式所在的环境很重要, 因为这是函数和在表达式中提到的对象定义的位置。

Quosure是可以使用 eval_tidy() 评估的对象,就像符号或函数调用一样。由于它们总是在其原始环境中进行评估, 因此quosure可以被看作是允许表达式从函数到函数传递的载体,但在评估时会立即返回其原始环境。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接