如何在使用tidyr和ggplot2的函数中使用dplyr的enquo和quo_name?

15
library(dplyr) #Devel version, soon-to-be-released 0.6.0
library(tidyr)
library(ggplot2)
library(forcats) #for gss_cat data

我正试图编写一个函数,将即将发布的dplyr开发版本中的quosure与tidyr::gatherggplot2组合在一起。到目前为止,它似乎可以与tidyr一起使用,但我在绘图方面遇到了问题。
下面的函数似乎可以与tidyr的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))
}

但我不知道如何让图表正常工作。我尝试使用 ggplot2!!gath,但没有成功。

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

我的回答适用于这个较新的问题:使用dplyr SE与ggplot2。简而言之,我建议您使用aes_(x =〜value,y =〜perc,fill = gath)。当您已经在使用quosures时,该语法比使用aes_string更清晰。 - Stanwood
4个回答

13
为了使这个方法起作用,我必须使用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))
}

1
有没有避免使用ggplot2::aes_string的方法? - Mike
1
我认为这是比被接受的答案更好的解决方案。 - Andrie
@Andrie 我同意。我在 OP 提出如何在不使用 aes_string 的情况下完成它的评论后发布了这篇文章。 - Akhil Nair

10
我觉得主要问题在于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示例的一些想法:
说实话,我对这里发生的事情感到相当不确定,但我想象它与ggplot2相对较旧且具有稍微不同的设计有关吗?通过debug进入aes,我们找到了一个类似的结构
structure(list(x = mpg, y = eval(rlang::UQE(var))), .Names = c("x", 
"y"), class = "uneval")

(这段代码无法通过解释器运行,但大致上是结构如此)。我认为这说明了为什么在这里需要eval调用,否则ggplot会尝试将rlang::UQE(var)映射到y美学,并报告不知道如何处理类name的内容。 eval评估名称,例如cyl,然后美学可以像往常一样映射。

我想象dplyr动词没有这个额外的映射步骤,其中参数以同样的方式转换为某些中间结构,因此我们没有这个问题。

另外,当我说您不必使用rlang调用时,那是因为我假定该函数已重新导出到新的dplyr版本中。因为我之前提到的整个!!(...)!(!(...))的事情,所以我更喜欢使用rlang ::“ !!”rlang :: UQE (我相信它们完全等效)。

虽然大部分都是猜测,但如果有人能纠正我任何错误的地方,我会非常感激。


感谢您的回答。所以当你说我不必使用rlang调用时,我是否仍然使用-fill = eval( !!(gath))?您能详细说明一下吗?此外,“eval”是什么意思?我对SE和NSE还很陌生,因此任何额外的细节都将不胜感激! - Mike
添加了一个编辑,猜测为什么 NSE 在这里不能按预期工作。上面的一些事情似乎违反了 NSE 说明书中概述的惯例:http://dplyr.tidyverse.org/articles/programming.html,因此我觉得 ggplot 可能略微超出了该文章的范围,因为它的年龄较大。 - Akhil Nair

4

现在可以在ggplot2 v3.0.0中使用整洁评估来处理aes。因此,不再需要使用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)

enter image description here


0

我最近在其他地方回答了这个问题(使用dplyr SE与ggplot2)。不确定如何标记重复,所以我会在这里重复一遍。

如果你已经处理过quosures,那么使用aes_而不是aes_string的语法更清晰。

这段代码应该适用于你的例子。请注意,所有硬编码的变量(value、perc、key)都用波浪线引起来,而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))

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