web-dev-qa-db-fra.com

Comment utiliser enquo et quo_name de dplyr dans une fonction avec tidyr et ggplot2

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

J'essaie d'écrire une fonction qui combine les quosures de la version de développement dplyr à paraître prochainement avec tidyr::gather et ggplot2. Jusqu’à présent, cela semble fonctionner avec tidyr, mais j’ai de la difficulté avec le complot. 

La fonction ci-dessous semble fonctionner avec 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))
}

Mais je n'arrive pas à comprendre comment faire fonctionner les parcelles. J'ai essayé d'utiliser !!gath avec ggplot2, mais cela n'a pas fonctionné. 

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

Je pense que le problème principal est ggplot est gourmand quand il essaie d'évaluer !!gath et fait !(!gath), émettant une erreur car not(gath) n'a pas de sens. J'ai souvent rencontré ce problème lorsque j'ai essayé d'utiliser !!, donc je suis un peu fatigué de l'utiliser sous sa forme sucrée. 

Si quelqu'un de plus précis pouvait identifier correctement le problème, ce serait certainement utile.

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

Il semble y avoir quelques erreurs dans l'appel de fonction que vous avez écrit dans la question. en espaçant correctement votre code, vous éviterez cela.

De plus, vous n'avez pas utilisé l'appel rlang, la version la plus récente dplyr n'est pas installée. 

EDITQuelques réflexions utilisant un exemple mtcars plus simple:

Tbh Je ne suis pas sûr de ce qui se passe ici, mais j'imagine que cela a à voir avec le fait que le ggplot2 est relativement vieux maintenant et a un design légèrement différent? En entrant dans aes avec debug, nous trouvons une structure similaire à

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

(Cela ne passera pas par l’interprète, mais correspond plus ou moins à la structure). Je pense que cela montre pourquoi l'appel eval est nécessaire ici, o/w ggplot tente de mapper rlang::UQE(var) avec l'esthétique y et indique qu'il ne sait pas quoi faire avec quelque chose de classe name. eval évalue le nom à, par exemple, cyl, alors l'esthétique peut être mappée comme d'habitude. 

J'imagine que les verbes dplyr n'ont pas cette étape de mappage supplémentaire dans laquelle les arguments sont manipulés de la même manière dans une structure intermédiaire, de sorte que nous n'avons pas ce problème.

De plus, lorsque j'ai dit que vous ne deviez pas utiliser l'appel rlang, c'était parce que je supposais que cette fonction avait été réexportée dans la nouvelle version dplyr. En raison de la totalité de !!(...) ou !(!(...)) que j'ai mentionné plus tôt, je préfère utiliser rlang::"!!" ou rlang::UQE (qui sont exactement équivalents, je crois).

La plupart de ceci est cependant de la spéculation et si quelqu'un pouvait me corriger sur quelque chose que je me suis trompé, ce serait apprécié.

8
Akhil Nair

Pour que cela fonctionne, je devais utiliser dplyr::quo_name pour changer le contenu en chaîne. J'ai également dû utiliser ggplot2::aes_string, qui requiert également que toutes les entrées soient des chaînes, et donc cité avec ""

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

Il est maintenant possible d'utiliser l'évaluation nettoie dans aes dans ggplot2 v3.0.0 . Ainsi, aes_string n'est plus nécessaire.

# 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

2
Tung

J'ai récemment répondu à cette question ailleurs récemment ( Utilisez dplyr SE avec ggplot2 ). Vous ne savez pas comment marquer les doublons, je vais donc répéter ici.

Si vous gérez déjà des quosures, la syntaxe est plus propre si vous utilisez aes_ plutôt que aes_string.

Ce morceau de code devrait fonctionner dans votre exemple. Notez que toutes les variables codées en dur (valeur, perc, clé) sont citées avec un tilda alors que le quosure (gath) est utilisé directement.

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))
0
Stanwood