我正在构建一个函数,我将根据字符串操作数据框。在函数中,我将从字符串构建一个列名,并使用它来操作数据框,如下所示:
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()
- 为什么会有区别?
答案 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()
}