4

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

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")

我了解问题所在:filter()在损坏的版本中取消引用 quosure 作为参数不是引用实际列 anyA。

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

4

2 回答 2

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

同样,问题是使用字符串而不是符号。

于 2017-10-12T15:13:40.113 回答
0

您的“破碎”功能没有任何条件filter(),您只需指定列名。

除此之外,我不确定您是否可以在更大的表达式中插入引号。例如,在这里您可以尝试以下操作:

df %>% filter((!!my_column) == TRUE)

但我认为这行不通。

相反,我建议使用条件函数filter_at()来定位适当的列。在这种情况下,您将 quosure 与过滤条件分开:

summarize_my_df_broken <- function(df, my_string) {

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

  df %>% 
    filter_at(vars(!!my_column), all_vars(. == TRUE)) %>% 
    group_by(othercol) %>% 
    summarize(
      n = n()
      , total = sum(amt)
    ) %>%
mutate(stringid = my_string)

}

于 2017-10-12T15:37:56.247 回答