【问题标题】:How to use dplyr's enquo and quo_name in a function with tidyr and ggplot2如何在带有 tidyr 和 ggplot2 的函数中使用 dplyr 的 enquo 和 quo_name
【发布时间】:2017-09-10 09:03:27
【问题描述】:
library(dplyr) #Devel version, soon-to-be-released 0.6.0
library(tidyr)
library(ggplot2)
library(forcats) #for gss_cat data

我正在尝试编写一个函数,将即将发布的dplyr devel 版本中的quosures 与tidyr::gatherggplot2 结合起来。到目前为止,它似乎与tidyr 一起工作,但我在绘图时遇到了麻烦。

以下函数似乎适用于tidyr's gather

GatherFun<-function(gath){
  gath<-enquo(gath)

  gss_cat%>%select(relig,marital,race,partyid)%>%
    gather(key,value,-!!gath)%>%
    count(!!gath,key,value)%>%
    mutate(perc=n/sum(n))
}

但我不知道如何使情节发挥作用。我尝试将!!gathggplot2 一起使用,但没有成功。

GatherFun<-function(gath){
  gath<-enquo(gath)

  gss_cat%>%select(relig,marital,race,partyid)%>%
    gather(key,value,-!!gath)%>%
    count(!!gath,key,value)%>%
    mutate(perc=n/sum(n))%>%
    ggplot(aes(x=value,y=perc,fill=!!gath))+
       geom_col()+
       facet_wrap(~key, scales = "free") +
       geom_text(aes(x = "value", y = "perc", 
                     label = "perc", group = !!gath),
                 position = position_stack(vjust = .05))
}

【问题讨论】:

标签: r ggplot2 dplyr rlang tidyeval


【解决方案1】:

为了完成这项工作,我不得不使用 dplyr::quo_name 将 quosure 更改为字符串。我还必须使用ggplot2::aes_string,它还要求所有输入都是字符串,因此用"" 引用。

GatherFun <- function(gath){
  gath <- enquo(gath)
  gathN <- quo_name(gath)

  gss_cat %>% 
    select(relig, marital, race, partyid) %>%
    gather(key, value, -!!gath) %>%
    count(!!gath, key, value) %>%
    mutate(perc = round(n/sum(n), 2)) %>%
    ggplot() +
    geom_col(aes_string(x = "value", y = "perc", fill = gathN)) +
    facet_wrap(~key, scales = "free") +
    geom_text(aes_string(x = "value", y = "perc", label = "perc", group = gathN), 
              position = position_stack(vjust = .05))
}

【讨论】:

  • 有什么办法可以避免使用ggplot2::aes_string?
  • 我认为这比公认的答案更好
  • @Andrie 我同意。我在OP评论如何使用aes_string之后发布了
【解决方案2】:

我觉得主要问题是ggplot 在尝试评估!!gath 并执行!(!gath) 时是贪婪的,抛出错误not(gath) 没有意义。当我尝试使用!! 时,我经常遇到这个问题,所以我有点厌倦了以糖的形式使用它。

如果有更准确的人能够正确识别问题,那肯定会有所帮助。

gather_func = function(gath) {

  gath = enquo(gath)

  gss_cat %>%
    select(relig, marital, race, partyid) %>%
    gather(key, value, -!!gath) %>%
    count(!!gath, key, value) %>%
    mutate(perc = round(n/sum(n), 2)) %>%
    ggplot(aes(x = value, y = perc, fill = eval(rlang::`!!`(gath)))) +
    geom_col() + 
    facet_wrap(~key, scales = "free") +
    geom_text(
      aes(
        x = value, 
        y = perc, 
        label = perc, 
        group = eval(rlang::`!!`(gath))
      ),
      position = position_stack(vjust = .05)
    )
}

您在问题中编写的函数调用似乎有一些错误。适当地间隔你的代码将有助于避免这种情况。

您也没有使用rlang 调用,我只是没有安装最新的dplyr 版本。

编辑一些想法使用更简单的mtcars 示例:

Tbh 我不太确定这里发生了什么,但我想这与ggplot2 现在相对较旧并且设计略有不同的事实有关?使用debug 进入aes,我们发现类似于

structure(list(x = mpg, y = eval(rlang::UQE(var))), .Names = c("x", 
"y"), class = "uneval")

(这不会通过解释器运行,但大致是结构的样子)。我认为这说明了为什么 eval 调用在这里是必要的,o/w ggplot 正在尝试将 rlang::UQE(var) 映射到 y 美学并报告它不知道如何处理 name 类的东西。 eval 将名称评估为cyl,然后可以正常映射美学。

我想dplyr 动词没有这个额外的映射步骤,其中参数以相同的方式被操纵到一些中间结构中,所以我们没有这个问题。

另外,当我说你不必使用rlang 调用时,这是因为我假设这个函数被重新导出到新的dplyr 版本中。由于我之前提到的整个!!(...)!(!(...)) 的东西,我更喜欢使用rlang::"!!"rlang::UQE(我相信它们完全一样)。

不过大部分都是猜测,如果有人能纠正我的任何错误,我将不胜感激。

【讨论】:

  • 感谢您的回答。所以当你说我不必使用 rlang 调用时,我还使用 - fill=eval(!!(gath)))) 吗?你能详细说明一下吗?另外,“eval”有什么作用?我还是 SE 和 NSE 的新手,所以任何额外的细节都将不胜感激!
  • 添加了一个编辑,对为什么 NSE 在这里不能按预期工作进行了一些猜测。上面的一些事情似乎打破了 NSE 小插图中概述的惯例:dplyr.tidyverse.org/articles/programming.html,因此我觉得ggplot 可能稍微超出了本文的范围,因为它的年龄
【解决方案3】:

现在可以在aesggplot2 v3.0.0 中使用整洁的评估。因此不再需要aes_string

# install.packages("ggplot2", dependencies = TRUE)

library(tidyverse) 

GatherFun2 <- function(gath) {

  gath <- enquo(gath)

  gss_cat %>% 
    select(relig, marital, race, partyid) %>%
    gather(key, value, -!! gath) %>%
    count(!!gath, key, value) %>%
    mutate(perc = round(n/sum(n), 2)) %>%
    ggplot() +
      geom_col(aes(x = value, y = perc, fill = !! gath)) +
      facet_wrap(~ key, scales = "free") +
      xlab(NULL) +
      geom_text(aes(x = value, y = perc, 
                    label = ifelse(perc == 0, "", perc), 
                    group = !! gath), 
                position = position_stack(vjust = .2)) +
      theme(legend.position = "bottom",
            axis.text.x = element_text(angle = 90, hjust = 1.0)) 
}

GatherFun2(marital)

【讨论】:

    【解决方案4】:

    我最近在其他地方回答了这个问题 (Use dplyr SE with ggplot2)。不知道如何标记重复,所以我会在这里重复。

    如果您已经在处理 quosures,那么使用 aes_ 而不是 aes_string

    这段代码应该可以在您的示例中使用。请注意,所有硬编码的变量(value、perc、key)都用 tilda 引用,而 quosure (gath) 则直接使用。

    ggplot(aes_(x = ~value, y = ~perc, fill = gath) +
      geom_col() +
      facet_wrap(~key, scales = "free") +
      geom_text(aes_(x = ~value, y = ~perc, label = ~perc, group = gath),
                position = position_stack(vjust = .05))
    

    【讨论】:

      猜你喜欢
      • 2017-12-09
      • 2019-09-26
      • 2016-07-05
      • 1970-01-01
      • 2020-01-17
      • 1970-01-01
      • 2021-02-17
      • 2016-06-05
      • 1970-01-01
      相关资源
      最近更新 更多