Цикл по переменным в ggplot

Я хочу использовать ggplot для цикла по нескольким столбцам для создания нескольких графиков, но использование заполнителя в цикле for изменяет поведение ggplot.

Если у меня есть это:

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

Это отлично работает:

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

Но это не так:

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

Это проблема, если я хочу в конечном итоге перебрать x, y и z. Любая помощь?


person Tom    schedule 31.01.2011    source источник
comment
Как правило, ggplot2 лучше работают с кадрами данных длинного формата, т. Е. m = melt(t, id="w") ; ggplot(subset(m, variable == "x"), aes(w, value)) + geom_line(), p <- ggplot(m, aes(w, value)) + geom_line(aes(colour=variable), d_ply(m, .(variable), function(d) p %+% d, .print=TRUE) и т. Д.   -  person baptiste    schedule 17.10.2011


Ответы (4)


Вам просто нужно использовать aes_string вместо aes, например:

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

Обратите внимание, что w также необходимо указать как строку.

person Matt Parker    schedule 31.01.2011

ggplot2 v3.0.0, выпущенный в июле 2018 г., поддерживает удобную оценку, например !! (Bang Bang) внутри aes(). Итак, мы можем сделать следующее:

  • Создайте функцию, которая принимает имена столбцов x и y в качестве входных данных. Обратите внимание на использование rlang::sym & !!.

  • Затем прокрутите каждый столбец, используя 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)
)

Определите функцию, которая принимает строки в качестве входных данных

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

Прокрутите каждый столбец

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)

Изменить: указанная выше функция также может быть написана без sym и !! с использованием .data[[]] местоимения.

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

Или мы можем просто использовать _14 _ / _ 15_ после преобразования фрейма данных из широкого в длинный формат ( 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')

person Tung    schedule 27.08.2018
comment
Другой вариант: использовать .data - person Tung; 11.03.2021

Проблема в том, как получить доступ к фрейму данных t. Как вы, наверное, знаете, есть несколько способов сделать это, но, к сожалению, использование символа явно не входит в их число в ggplot.

Один из способов, который может сработать, - использовать числовую позицию столбца в вашем примере, например, вы можете попробовать i <- 2. Однако, если это работает, опирается на ggplot, который я никогда не использовал (но я знаю другие работы Хэдли, и я думаю, что это должно сработать)

Другой способ обойти это - создавать новый временный фрейм данных каждый раз, когда вы вызываете ggplot. например.:

tmp <- data.frame(a = t[['w']], b = t[[i]])
ggplot(data=tmp, aes(a, b)) + geom_line()
person Henrik    schedule 31.01.2011
comment
Раньше я тоже использовал числовые индексы, что отлично подходит для просмотра каждого столбца в наборе данных. Я нервничаю каждый раз, когда мне приходится предполагать, что t $ x всегда будет t [, 2]. - person Matt Parker; 01.02.2011
comment
Отлично, спасибо. Я уверен, что мне понадобится этот обходной путь в будущем. - person Tom; 01.02.2011
comment
Это не работает для более сложных ggplots (например, использующих фасеты). - person Gregor Thomas; 27.10.2015
comment
@Gregor Если вы зададите новый вопрос с воспроизводимым примером, показывающим это, я думаю, кто-то может вам помочь. - person Henrik; 27.10.2015
comment
Метод aes_string() (в ответе Мэтта Паркера) работает в общем случае. Я просто комментировал, поэтому любой, кто использует этот ответ, вместо этого знает его ограничения. - person Gregor Thomas; 28.10.2015

В зависимости от того, что вы пытаетесь сделать, я считаю, что facet_wrap или facet_grid хорошо подходят для создания нескольких графиков с одинаковой базовой структурой. Что-то вроде этого должно привести вас в правильное положение:

t.m = melt(t, id="w")
ggplot(t.m, aes(w, value)) + facet_wrap(~ variable) + geom_line()
person Dan M.    schedule 17.10.2011