55 votes

Bouclage sur les variables dans ggplot

Je veux utiliser ggplot pour boucler sur plusieurs colonnes afin de créer plusieurs graphiques, mais l'utilisation du placeholder dans la boucle for change le comportement de ggplot.

Si j'ai ça :

t <- data.frame(w = c(1, 2, 3, 4), x = c(23,45,23, 34), 
y = c(23,34,54, 23), z = c(23,12,54, 32))

Cela fonctionne bien :

ggplot(data=t, aes(w, x)) + geom_line()

Mais ce n'est pas le cas :

i <- 'x'
ggplot(data=t, aes(w, i)) + geom_line()

Ce qui est un problème si je veux éventuellement boucler sur x, y et z. Une aide ?

65voto

Matt Parker Points 7373

Il suffit d'utiliser aes_string au lieu de aes comme ceci :

ggplot(data=t, aes_string(x = "w", y = i)) + geom_line() 

Notez que w doit également être spécifié comme une chaîne de caractères.

31voto

Tung Points 7486

ggplot2 > 3.0.0 soutient le pronom d'évaluation rangé .data . Nous pouvons donc faire ce qui suit :

  • Construisez une fonction qui prend les noms des colonnes x et y comme entrées. Notez l'utilisation de .data[[]] .

  • Ensuite, bouclez à travers chaque colonne en utilisant purrr::map .

    library(rlang) library(tidyverse)

    dt <- data.frame( w = c(1, 2, 3, 4), x = c(23, 45, 23, 34), y = c(23, 34, 54, 23), z = c(23, 12, 54, 32) )

Définir une fonction qui accepte les chaînes de caractères comme entrée

plot_for_loop <- function(df, x_var, y_var) {

  ggplot(df, aes(x = .data[[x_var]], y = .data[[y_var]])) + 
    geom_point() + 
    geom_line() +
    labs(x = x_var, y = y_var) +
    theme_classic(base_size = 12)
}

Boucle dans chaque colonne

plot_list <- colnames(dt)[-1] %>% 
  map( ~ plot_for_loop(dt, colnames(dt)[1], .x))

# view all plots individually (not shown)
plot_list

# Combine all plots
library(cowplot)
plot_grid(plotlist = plot_list,
          ncol = 3)

Editar la fonction ci-dessus peut également être écrite w/ rlang::sym & !! (bang bang).

plot_for_loop2 <- function(df, .x_var, .y_var) {

  # convert strings to variable
  x_var <- sym(.x_var)
  y_var <- sym(.y_var)

  # unquote variables using !! 
  ggplot(df, aes(x = !! x_var, y = !! y_var)) + 
    geom_point() + 
    geom_line() +
    labs(x = x_var, y = y_var) +
    theme_classic(base_size = 12)
}

Ou nous pouvons simplement utiliser facet_grid / facet_wrap après avoir converti le cadre de données du format large au format long ( tidyr::gather )

dt_long <- dt %>% 
  tidyr::gather(key, value, -w)
dt_long
#>    w key value
#> 1  1   x    23
#> 2  2   x    45
#> 3  3   x    23
#> 4  4   x    34
#> 5  1   y    23
#> 6  2   y    34
#> 7  3   y    54
#> 8  4   y    23
#> 9  1   z    23
#> 10 2   z    12
#> 11 3   z    54
#> 12 4   z    32

### facet_grid
ggp1 <- ggplot(dt_long, 
       aes(x = w, y = value, color = key, group = key)) +
  facet_grid(. ~ key, scales = "free", space = "free") +
  geom_point() + 
  geom_line() +
  theme_bw(base_size = 14)
ggp1

### facet_wrap
ggp2 <- ggplot(dt_long, 
       aes(x = w, y = value, color = key, group = key)) +
  facet_wrap(. ~ key, nrow = 2, ncol = 2) +
  geom_point() + 
  geom_line() +
  theme_bw(base_size = 14)
ggp2

### bonus: reposition legend
# https://cran.r-project.org/web/packages/lemon/vignettes/legends.html
library(lemon)
reposition_legend(ggp2 + theme(legend.direction = 'horizontal'), 
                  'center', panel = 'panel-2-2')

3voto

Henrik Points 4728

Le problème est la façon dont vous accédez au cadre de données t . Comme vous le savez probablement, il y a plusieurs façons de le faire, mais malheureusement, l'utilisation d'un caractère n'en fait pas partie. ggplot .

Une façon de procéder qui pourrait fonctionner est d'utiliser la position numérique de la colonne dans votre exemple, par exemple, vous pourriez essayer de i <- 2 . Cependant, si cela fonctionne repose sur ggplot que je n'ai jamais utilisé (mais je connais d'autres travaux de Hadley et je suppose que cela devrait fonctionner)

Une autre façon de contourner ce problème est de créer un nouveau cadre de données temporaire chaque fois que vous appelez ggplot, par exemple :

tmp <- data.frame(a = t[['w']], b = t[[i]])
ggplot(data=tmp, aes(a, b)) + geom_line()

1voto

Dan M. Points 1348

En fonction de ce que vous essayez de faire, je trouve que facet_wrap o facet_grid fonctionne bien pour créer plusieurs parcelles ayant la même structure de base. Quelque chose comme ceci devrait vous mettre sur la bonne voie :

t.m = melt(t, id="w")
ggplot(t.m, aes(w, value)) + facet_wrap(~ variable) + geom_line()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X