web-dev-qa-db-fra.com

Utilisation de ggplot () dans une autre fonction de R

J'essaie d'écrire une fonction de tracé simple en utilisant la bibliothèque ggplot2. Mais l'appel à ggplot ne trouve pas l'argument de la fonction.

Prenons un data.frame appelé means qui stocke deux conditions et deux valeurs moyennes que je veux tracer (la condition apparaîtra sur l’axe des X et signifiera sur le Y).

library(ggplot2)
m <- c(13.8, 14.8)
cond <- c(1, 2)
means <- data.frame(means=m, condition=cond)
means
# The output should be:
#     means    condition
#   1 13.8     1
#   2 14.8     2

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, aes(fill=meansdf$condition, y=meansdf$means, x = meansdf$condition))
  p + geom_bar(position="dodge", stat="identity")
}

testplot(means)
# This will output the following error:
# Error in eval(expr, envir, enclos) : object 'meansdf' not found

Il semble donc que ggplot appelle eval, qui ne trouve pas l'argument meansdf. Est-ce que quelqu'un sait comment je peux passer avec succès l'argument de la fonction à ggplot?

(Remarque: oui, je pourrais simplement appeler directement la fonction ggplot, mais au final, j'espère que ma fonction de complot fera des choses plus compliquées! :))

40
trev

Comme Joris et Chase ont déjà répondu correctement, la meilleure pratique consiste à omettre simplement la partie meansdf$ et à se référer directement aux colonnes du bloc de données.

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, 
              aes(fill = condition,
                  y = means,
                  x = condition))
  p + geom_bar(position = "dodge", stat = "identity")
}

Cela fonctionne car les variables référencées dans aes sont recherchées soit dans l'environnement global, soit dans le cadre de données transmis à ggplot. C'est aussi la raison pour laquelle votre exemple de code - avec meansdf$condition etc. - n'a pas fonctionné: meansdf n'est pas disponible dans l'environnement global, ni dans le cadre de données transmis à ggplot, qui est meansdf.


Le fait que les variables soient recherchées dans l'environnement global plutôt que dans l'environnement appelant est en fait un bogue connu dans ggplot2 que Hadley ne considère pas réparable pour l'instant on souhaite utiliser une variable locale, par exemple, scale, pour influencer les données utilisées pour le tracé:

testplot <- function(meansdf)
{
  scale <- 0.5
  p <- ggplot(meansdf, 
              aes(fill = condition,
                  y = means * scale,   # does not work, since scale is not found
                  x = condition))
  p + geom_bar(position = "dodge", stat = "identity")
}

Winston Chang fournit une solution de contournement très intéressante dans le problème GitHub référencé: Définition explicite du paramètre environment sur l'environnement actuel lors de l'appel à ggplot. Voici à quoi ressemblerait cet exemple:

testplot <- function(meansdf)
{
  scale <- 0.5
  p <- ggplot(meansdf, 
              aes(fill = condition,
                  y = means * scale,
                  x = condition),
              environment = environment())   # This is the only line changed / added
  p + geom_bar(position = "dodge", stat = "identity")
}

## Now, the following works
testplot(means)
34
jhin

La "bonne" façon d'utiliser ggplot par programme consiste à utiliser aes_string() au lieu de aes() et à utiliser les noms des colonnes sous forme de caractères plutôt que sous forme d'objets:

Pour des utilisations plus programmatiques, par exemple si vous voulez que les utilisateurs puissent spécifier des noms de colonne pour diverses esthétiques sous forme d'arguments, ou si cette fonction est intégrée à un paquet devant passer R CMD CHECK sans avertissements sur les noms de variable sans définitions, vous pouvez utiliser aes_string() , avec les colonnes nécessaires en tant que caractères.

testplot <- function(meansdf, xvar = "condition", yvar = "means",
                     fillvar = "condition") {
    p <- ggplot(meansdf,
                aes_string(x = xvar, y= yvar, fill = fillvar)) +
             geom_bar(position="dodge", stat="identity")
}
24
Gregor

Voici une astuce simple que j'utilise beaucoup pour définir mes variables dans l'environnement de mes fonctions (deuxième ligne): 

FUN <- function(fun.data, fun.y) {
    fun.data$fun.y <- fun.data[, fun.y]
    ggplot(fun.data, aes(x, fun.y)) + 
        geom_point() + 
        scale_y_continuous(fun.y)    
}

datas <- data.frame(x = rnorm(100, 0, 1),
                    y = x + rnorm(100, 2, 2),
                    z = x + rnorm(100, 5, 10))
FUN(datas, "y")
FUN(datas, "z")

Notez que l'étiquette de l'axe des y change également lorsque différentes variables ou ensembles de données sont utilisés.

18
David Douterlungne

Je ne pense pas que vous deviez inclure la partie meansdf$ dans votre appel de fonction. Cela semble fonctionner sur ma machine:

meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)

testplot <- function(meansdf)
{
p <- ggplot(meansdf, aes(fill=condition, y=means, x = condition))
p + geom_bar(position="dodge", stat="identity")
}


testplot(meansdf)

produire:

enter image description here

16
Chase

Ceci est un exemple de problème qui est discuté plus tôt . En gros, il faut que ggplot2 soit codé pour être utilisé principalement dans l’environnement mondial. Dans l'appel aes (), les variables sont recherchées dans l'environnement global ou dans la trame de données spécifiée.

library(ggplot2)
means <- data.frame(means=c(13.8,14.8),condition=1:2)

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, aes(fill=condition, 
          y=means, x = condition))
  p + geom_bar(position="dodge", stat="identity")
}

MODIFIER:

update: Après avoir vu l'autre réponse et mis à jour le paquet ggplot2, le code ci-dessus fonctionne. Comme expliqué dans les commentaires, le motif est que ggplot recherchera les variables dans aes dans l'environnement global (lorsque le cadre de données est spécifiquement ajouté en tant que meandf $ ...) ou dans l'environnement mentionné.

Pour cela, assurez-vous de travailler avec la dernière version de ggplot2.

15
Joris Meys

Cela m'a frustré pendant un certain temps. Je voulais envoyer différents cadres de données avec différents noms de variables et pouvoir tracer différentes colonnes du cadre de données. J'ai finalement trouvé un moyen de contourner le problème en créant des variables factices (globales) pour gérer le traçage et forcer l'affectation dans la fonction.

plotgraph function(df,df.x,df.y) {

dummy.df <<- df
dummy.x <<- df.x
dummy.y <<- df.y

p = ggplot(dummy.df,aes(x=dummy.x,y=dummy.y,.....)
print(p)

}

puis dans le code principal, je peux simplement appeler la fonction

plotgraph(data,data$time,data$Y1)
plotgraph(data,data$time,data$Y2)
3
Raghu Ramachandran

Vous n'avez besoin de rien d'extraordinaire. Même pas de variables factices. Il vous suffit d’ajouter un print () dans votre fonction, c’est comme utiliser cat () lorsque vous voulez que quelque chose soit affiché dans la console.

myplot <- ggplot (......) + Tout ce que vous voulez ici print (myplot) 

Cela a fonctionné pour moi plus d'une fois dans la même fonction

1
Perla Reyes

Réponse courte: Utilisez qplot

Réponse longue: Essentiellement, vous voulez quelque chose comme ceci:

my.barplot <- function(x=this.is.a.data.frame.typically) {
   # R code doing the magic comes here
   ...
}

Mais cela manque de flexibilité, car vous devez vous en tenir à un nom de colonne cohérent pour éviter les idiosyncrasies gênantes de l'étendue R. Bien sûr, la prochaine étape logique est la suivante:

my.barplot <- function(data=data.frame(), x=..., y....) {
   # R code doing something really really magical here
   ...
}

Mais alors cela commence à ressembler étrangement à un appel à qplot (), non?

qplot(data=my.data.frame, x=some.column, y=some.other column,
      geom="bar", stat="identity",...)

Bien sûr, vous voudriez maintenant changer des choses comme les titres d'échelle, mais pour cela, une fonction est pratique ... La bonne nouvelle est que les problèmes de cadrage ont pour la plupart disparu.

my.plot <- qplot(data=my.data.frame, x=some.column, y=some.other column,...)
set.scales(p, xscale=scale_X_continuous, xtitle=NULL,
           yscale=scale_y_continuous(), title=NULL) {
  return(p + xscale(title=xtitle) + yscale(title=ytitle))
}
my.plot.prettier <- set.scale(my.plot, scale_x_discrete, 'Days',
                              scale_y_discrete, 'Count')
1
jcb

Une autre solution consiste à définir le aes (...) en tant que variable de votre fonction:

func<-function(meansdf, aes(...)){}

Cela a bien fonctionné pour moi sur un sujet similaire

1
user3216394

S'il est important de transmettre les variables (noms de colonnes) à la fonction de traçage personnalisée sans les citer, tandis que différents noms de variables sont utilisés dans la fonction, une autre solution que j'ai essayée consistait à utiliser match.call() et eval (comme ici ainsi que): 

library(ggplot2)

meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)

testplot <- function(df, x, y) {
  arg <- match.call()
  scale <- 0.5
  p <- ggplot(df, aes(x = eval(arg$x),
                      y = eval(arg$y) * scale,
                      fill = eval(arg$x)))
  p + geom_bar(position = "dodge", stat = "identity")
}

testplot(meansdf, condition, means)

Créé le 2019-01-10 par le paquet reprex (v0.2.1)

Une autre solution de contournement, mais en passant des variables entre guillemets à la fonction de traçage personnalisée, utilise get():

meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)

testplot <- function(df, x, y) {
  scale <- 0.5
  p <- ggplot(df, aes(x = get(x),
                      y = get(y) * scale,
                      fill = get(x)))
  p + geom_bar(position = "dodge", stat = "identity")
}

testplot(meansdf, "condition", "means")

Créé le 2019-01-10 par le paquet reprex (v0.2.1)

0
Valentin

Je viens de générer de nouvelles variables de cadre de données avec les noms souhaités dans la fonction:

testplot <- function(df, xVar, yVar, fillVar) {
    df$xVar = df[,which(names(df)==xVar)]
    df$yVar = df[,which(names(df)==yVar)]
    df$fillVar = df[,which(names(df)==fillVar)]
    p <- ggplot(df,
                aes(x=xvar, y=yvar, fill=fillvar)) +
             geom_bar(position="dodge", stat="identity")
    }
0
jhelvy