【问题标题】:Unexpected Behavior with dplyr::filter()dplyr::filter() 的意外行为
【发布时间】:2019-09-01 00:34:07
【问题描述】:

感谢您的宝贵时间。

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

使用filter() 似乎有效,除非列名和对象名相同。有关详细信息,请参阅下面的示例。

我希望 data 只返回 data$year 匹配 year 或 data$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。

标签: r filter dplyr tidyverse


【解决方案1】:

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 中)你使用 !!表示您想取消引用输入以便对其进行评估,而不是引用。这为我们提供了一个实际执行我们想要的功能。

【讨论】:

    【解决方案2】:

    要在其原始环境中评估表达式,您可以知道它在哪里定义,如下所示,这一点都不漂亮。

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

    或者你可以使用enquo。

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

    来自帮助页面help('enquo')。

    在quosures中捕获表达式

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

    Quosures 是可以用eval_tidy() 评估的对象,就像 符号或函数调用。因为他们总是在他们的 原始环境,quosures 可以被视为允许 表达式从一个函数传递到另一个函数,但它会返回 评估后立即恢复到原来的环境。

    【讨论】:

      猜你喜欢
      • 2019-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-20
      • 1970-01-01
      • 2019-07-09
      相关资源
      最近更新 更多