为什么quosures在group_by()中工作但不在filter()中工作?

时间:2017-10-12 15:05:45

标签: r dplyr nse

我正在构建一个函数,我将根据字符串操作数据框。在函数中,我将从字符串构建一个列名,并使用它来操作数据框,如下所示:

library(dplyr)

orig_df  <- data_frame(
     id = 1:3
   , amt = c(100, 200, 300)
   , anyA = c(T,F,T)
   , othercol = c(F,F,T)
)


summarize_my_df_broken <- function(df, my_string) {

  my_column <- quo(paste0("any", my_string))

  df %>% 
    filter(!!my_column) %>% 
    group_by(othercol) %>% 
    summarize(
        n = n()
      , total = sum(amt)
    ) %>%
    # I need the original string as new column which is why I can't
    # pass in just the column name
    mutate(stringid = my_string)


}


summarize_my_df_works <- function(df, my_string) {

  my_column <- quo(paste0("any", my_string))

  df %>% 
    group_by(!!my_column, othercol) %>% 
    summarize(
        n = n()
      , total = sum(amt)
    )  %>%
    mutate(stringid = my_string)

}

# throws an error: 
# Argument 2 filter condition does not evaluate to a logical vector
summarize_my_df_broken(orig_df, "A")

# works just fine
summarize_my_df_works(orig_df, "A")

我理解问题是什么:在断开版本中将quosure作为filter()的参数取消引用并不引用实际的列anyA。

我不明白为什么它适用于summarize(),而不是filter() - 为什么会有区别?

2 个答案:

答案 0 :(得分:4)

现在你正在进行字符串的输入,而不是符号名称。那不应该如何使用。 quo("hello")quo(hello)之间存在很大差异。如果要从字符串中创建正确的符号名称,则需要使用rlang::sym。所以快速修复就是

summarize_my_df_broken <- function(df, my_string) {

  my_column <- rlang::sym(paste0("any", my_string))
  ...
}

如果你仔细观察,我认为你会发现group_by/summarize实际上并没有按你期望的方式工作(尽管你不会得到相同的错误信息)。这两个不会产生相同的结果

summarize_my_df_works(orig_df, "A")
#  `paste0("any", my_string)` othercol     n total
#                        <chr>    <lgl> <int> <dbl>
# 1                       anyA    FALSE     2   300
# 2                       anyA     TRUE     1   300

orig_df  %>% 
  group_by(anyA, othercol) %>% 
  summarize(
    n = n()
    , total = sum(amt)
  )  %>%
  mutate(stringid = "A")
#    anyA othercol     n total stringid
#   <lgl>    <lgl> <int> <dbl>    <chr>
# 1 FALSE    FALSE     1   200        A
# 2  TRUE    FALSE     1   100        A
# 3  TRUE     TRUE     1   300        A

问题再次是使用字符串而不是符号。

答案 1 :(得分:0)

你的“{1}}在你被打破的情况下”没有任何条件。函数,您只需指定列名称。

除此之外,我不确定您是否可以将quosures插入更大的表达式中。例如,您可以尝试以下方式:

filter()

但我认为这不会起作用。

相反,我建议使用条件函数df %>% filter((!!my_column) == TRUE) 来定位相应的列。在这种情况下,您将quosure与过滤条件分开:

filter_at()

}