如何使用purrr循环遍历一个整洁评估函数?

10

我有以下数据集(样本):

train <- data.frame(ps_ind_06_bin = c(FALSE, FALSE, FALSE, TRUE, TRUE, FALSE),
                        ps_ind_07_bin = c(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE),
                        ps_ind_08_bin = c(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE),
                        ps_ind_09_log = c(1, 3, 4, 2, 3, 2))

我有以下函数,用于展示group_by()操作的ggplot图表:
get_charts1 <- function(mygroup){
  quo_var <- enquo(mygroup)
  train %>% 
    group_by(!!quo_var) %>% 
    count() %>%
    ungroup() %>%
  ggplot(aes_q(x = quo_var, y = quote(n), fill = quo_var)) + 
    geom_col() +
    theme(legend.position = "none")
    }

当我手动输入列名时,它能正常工作,例如:

get_charts1(ps_ind_07_bin)

然而,我想在多列上使用该函数,我将它们放在一个向量上:

binarias <- train %>% 
             select(ends_with("bin")) %>% 
             colnames()

使用 map 函数并参考一些建议,我尝试使用:

listaplots <- map(quo(!!! syms(binarias)), get_charts1)

但是这会导致以下错误:

"Error: Can't splice at top-level"
有人知道我需要做什么才能让这个工作吗?

看起来 map(quos(ps_ind_06_bin , ps_ind_07_bin ), get_charts1) 不起作用,所以问题并不在于扩展。似乎 map() 只是强制评估参数。 - MrFlick
在下一个 rlang 版本中,捕获强制对象(包括强制 quosures 或符号)将直接生效。这将等同于 unquoting。 - Lionel Henry
1
当有人回答我的问题时,我应该怎么做? - zx8754
3个回答

16

我将开始创建一个reprex (你很接近了,但是忘记加载必要的包),并使用styler重新设置为一致的格式:

library(tidyverse)
library(rlang)

train <- data.frame(
  ps_ind_06_bin = c(FALSE, FALSE, FALSE, TRUE, TRUE, FALSE),
  ps_ind_07_bin = c(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE),
  ps_ind_08_bin = c(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE),
  ps_ind_09_log = c(1, 3, 4, 2, 3, 2)
)

get_charts <- function(mygroup) {
  quo_var <- enquo(mygroup)
  train %>%
    group_by(!! quo_var) %>%
    count() %>%
    ungroup() %>%
    ggplot(aes_q(x = quo_var, y = quote(n), fill = quo_var)) +
    geom_col() +
    theme(legend.position = "none")
}
你想要自动化生成类似这样的代码:
get_charts(ps_ind_06_bin)
get_charts(ps_ind_07_bin)
get_charts(ps_ind_08_bin)

这将需要使用 for 循环或 apply/map 函数。在这里,map() 函数非常适合,因为我们希望返回 ggplot2 对象,而使用 for 循环需要更多的基础设施。一旦你记住需要在此处使用符号而不是原始字符串,就可以轻松实现。

vars <- train %>% select(ends_with("bin")) %>% colnames()

vars %>%
  syms() %>%
  map(function(var) get_charts(!!var))

## [[1]]


(翻译:该段内容为一个带有图片的HTML段落)
## 
## [[2]]


注:这是一段HTML代码,没有具体的文字内容需要翻译。
## 
## [[3]]


2
我认为你需要使用invoke_map而不是map。这似乎可以给出你想要的结果。
listaplots  <- invoke_map(get_charts1, rlang::syms(binarias))

map() 函数似乎会强制求值参数,而 invoke_map 则不会。


1
enquo()更改为sym(),您的代码就可以像这样正常工作:
get_charts1 <- function(mygroup){
    quo_var <- sym(mygroup)  # <- HERE

    train %>% 
      group_by(!!quo_var) %>% 
      count() %>%
      ungroup() %>%
      ggplot(aes_q(x = quo_var, y = quote(n), fill = quo_var)) + 
      geom_col() +
      theme(legend.position = "none")
}

binarias <- train %>% select(ends_with("bin")) %>% colnames()

binarias %>% map(get_charts1)

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