Как создать цикл for в r, который назначает ggplots как объект без сохранения поверх них

Я использую пакеты grid и grid.Extra, чтобы иметь несколько графиков ggplots в одной сетке, однако мне нужно всего создать 28 графиков ggplots, каждый с другой переменной y. Очевидно, цикл для создания и сохранения их как переменной достаточно прост, но мне нужно сохранять каждый график как отдельный объект. В настоящее время у меня всего 28 одинаковых графических кодов с измененной только осью, но я знаю, что есть способ получше.

Мой минимальный пример кода:

dattn <-  ggplot()+
  geom_boxplot(
  data = dat, 
  mapping = aes(x = site, y = tn)
)

Мой текущий заголовок цикла (не уверен, имеет ли это значение):

for (i in dat[, 10:12])

Мне нужно переключить значение y на tp и tss и сохранить эти графики ggplots как dattp и datss. Столбцы переменных - 10, 11 и 12 в указанном порядке.

Изучая эту проблему, я столкнулся с этим вопросом: Динамическое именование переменных в цикле для создания 30 ggplots, что очень похоже на мою проблему, однако в нем используются конвейерные функции, и я понятия не имею, как их использовать, тогда как у меня есть некоторое представление об использовании цикла for. Если кто-то думает, что это сработает лучше, я был бы благодарен за это.

Я также пробовал код paste("dat", i, sep="") <- ggplot... конкретно из вопроса Сохранить объекты ggplot в цикле, но это выдает сообщение об ошибке target of assignment expands to non-language object и снова использует функции конвейера.

Я обновлю свой вопрос, добавив всю необходимую информацию по мере необходимости. Заранее спасибо.


Причина, по которой я запросил помощь с использованием циклов for, заключается в том, что мне также нужно разбить мои данные на подмножества на основе местоположения («ферма» в данных), и я решил, что как только я смогу изменить текст цикла, я смогу изменить то, как я подмножество data и использовать вложенные циклы.


Данные с использованием dput(head(dat)):

structure(list(year = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = c("2018", 
"2019"), class = "factor"), month = structure(c(4L, 5L, 5L, 6L, 
7L, 7L), .Label = c("1", "2", "3", "4", "5", "6", "7", "9", "10", 
"11", "12"), class = "factor"), day = c(24L, 18L, 30L, 25L, 6L, 
19L), farm = structure(c(2L, 2L, 2L, 2L, 2L, 2L), .Label = c("ARR", 
"Car", "CAR", "Mur", "Muz", "PBR", "Pre", "PRE", "Sch", "SCH", 
"Sim", "SIM", "STU"), class = "factor"), treat = structure(c(1L, 
1L, 1L, 1L, 1L, 1L), .Label = c("CC", "Con"), class = "factor"), 
    tss = c(1955, 3540, 4893.3, 410, 3357.5, 1836), tn = c(17, 
    32.8, 7.26, 5.91, 16.1, 16.7), tp = c(4.35, 10, 49.5, 3.57, 
    9.79, 11.1), dis = c(8178, 184232, 401364, 1113947, 10728, 
    21869), tss.1 = c(1.576347171, 64.30227415, 193.6414217, 
    45.03046056, 3.551344392, 3.958763937), tn.1 = c(0.013707367, 
    0.595795082, 0.28729829, 0.649097614, 0.017029529, 0.036008365
    ), tp.1 = c(0.003507473, 0.181644842, 1.958851976, 0.392094498, 
    0.010355223, 0.023933704), site = structure(c(1L, 1L, 1L, 
    1L, 1L, 1L), .Label = c("Car CC", "Mur CC", "Muz CC", "Pre CC", 
    "Sch CC", "Sim CC", "CAR CC", "ARR CC", "PBR CC", "PRE CC", 
    "STU CC", "Car Con", "Mur Con", "Muz Con", "Pre Con", "Sch Con", 
    "Sim Con", "SCH Con", "PRE Con", "ARR Con", "PBR Con", "SIM Con", 
    "STU Con"), class = "factor")), row.names = c(NA, 6L), class = "data.frame")

И немного кода форматирования:

# Set variables as factors
cols <- c("year", "month")
dat[cols] <- lapply(dat[cols], as.factor)

# Set dat$site to combine farm and treat
dat$site <- paste(dat$farm, dat$treat)

# Sets the sites in order instead of aphabetically.
# unique() is needed else Error: duplicates
dat$site <- factor(dat$site, levels =unique(dat$site))

Не уверен, что это поможет, но мне предложили именно это.


person Orisa is your shield    schedule 05.08.2019    source источник


Ответы (3)


Вариант map. Прокрутите имена столбцов в виде строки и передайте ее aes_string

library(tidyverse)
v1 <- c('tn', 'tp', 'tss')
out <- map(v1, ~ 
            ggplot()+
     geom_boxplot(
      data = dat, 
      mapping = aes_string(x = "site", y =.x) #or
      # mapping = aes(x, !! rlang::sym(.x))
      ))
person akrun    schedule 05.08.2019
comment
Разве aes_string не относится к мягкому устареванию для документов и блог? Кто-то сообщил мне об этом, когда я предложил это. Вы действительно ответили на тот же вопрос, используя rlang челку! - person Parfait; 05.08.2019
comment
Похоже, он устарел - person akrun; 05.08.2019
comment
Это хороший прорыв. Теперь код создает 3 графика ggplots и сохраняет их в виде списка из трех, но как мне получить графики? - person Orisa is your shield; 05.08.2019
comment
@Orisaisyourshield. Использование стандартных методов извлечения списка. Если у вас есть именованный список, его можно извлечь с помощью out$name1 или, в общем случае, out[[1]] для первого элемента списка. - person akrun; 05.08.2019
comment
@akrun Я очень ценю, что вы нашли время помочь мне с этим, я стараюсь out[[1]], но получаю Error in FUN(X[[i]], ...) : object 'x' not found - person Orisa is your shield; 05.08.2019
comment
@Orisaisyourshield Вы говорите, что out <- map(... работал без ошибок, но обнаружил ошибку только при подмножестве - person akrun; 05.08.2019
comment
@Orisaisyourshield. У меня была опечатка, mapping = aes_string(x = "site", y =.x). site будет цитироваться - person akrun; 05.08.2019
comment
@akrun, Ах, я получил ошибки при использовании исходного кода и предположил, что он был на моей стороне, поэтому я использовал ваш альтернативный код, но с вашим редактированием он работает. - person Orisa is your shield; 05.08.2019

Или вы можете использовать .data[[]] для извлечения желаемых имен столбцов

library(tidyverse)

# define plotting function
plot_gg <- function(dat, x_var, y_var) {

  dattn <-  ggplot() +
    geom_boxplot(
      data = dat, 
      mapping = aes(x = .data[[x_var]], y = .data[[y_var]])
    ) +
    labs(x = x_var, y = y_var)
  return(dattn)
}

# save plots in a list
plot_list <- c('tn', 'tp', 'tss') %>% 
  map(~ plot_gg(dat, 'site', .x))
plot_list
#> [[1]]

#> 
#> [[2]]

#> 
#> [[3]]

Создано 5 августа 2019 г. пакетом REPEX (v0.3.0)

person Tung    schedule 05.08.2019

Подумайте о том, чтобы преобразовать ваши широкие данные в длинный формат, а затем построить график с facet_wrap или facet_grid без каких-либо сложных циклов, сопоставления или сохранения множества графиков для gridExtra::grid. Ниже демонстрируется случайная выборка данных.

Данные (предполагается, что приведенная ниже структура отражает фактические данные OP)

set.seed(852019)

### DATA BUILD
random_df <- data.frame(
  site = sample(c("sas", "stata", "spss", "python", "r", "julia"), 500, replace=TRUE),
  var2 = NA,
  var3 = NA,
  var4 = NA,
  var5 = NA,
  var6 = NA,
  var7 = NA,
  var8 = NA,
  var9 = NA,
  tn = rnorm(500),
  tss = rnorm(500),
  tp = rnorm(500)
)

head(random_df)

#    site var2 var3 var4 var5 var6 var7 var8 var9         tn         tss          tp
# 1 stata   NA   NA   NA   NA   NA   NA   NA   NA  2.0237416 -1.30919981 -1.71918905
# 2     r   NA   NA   NA   NA   NA   NA   NA   NA  0.6052126 -0.27231149  0.18739618
# 3     r   NA   NA   NA   NA   NA   NA   NA   NA  1.3270657 -0.70308896  0.04996251
# 4   sas   NA   NA   NA   NA   NA   NA   NA   NA -0.8690220  0.09934931 -0.12513907
# 5 julia   NA   NA   NA   NA   NA   NA   NA   NA -1.8871174  0.08761820 -0.45409606
# 6   sas   NA   NA   NA   NA   NA   NA   NA   NA  0.3205017 -0.61696052  0.32586570

Сюжет

# RESHAPE WIDE TO LONG
yvars <- c("tn", "tss", "tp")

long_df <- reshape(random_df, varying = yvars, v.names = "y_value",
                   times = yvars, timevar = "y_var",
                   idvar = c("site"), drop = c(2:9),
                   new.row.names = 1:1E4, direction = "long")

head(long_df)
#    site y_var    y_value
# 1 stata    tn  2.0237416
# 2     r    tn  0.6052126
# 3     r    tn  1.3270657
# 4   sas    tn -0.8690220
# 5 julia    tn -1.8871174
# 6   sas    tn  0.3205017

# BOXPLOT WITH FACET
ggplot() + geom_boxplot(data = long_df, mapping = aes(x = site, y = y_value)) + 
  facet_wrap(~y_var)

Вывод коробчатой ​​диаграммы


Если вам нужны отдельные графики, по-прежнему рассмотрите возможность использования длинного формата и используйте by вне столбца индикатора y_var для построения списка графиков. Затем постройте с gridExtra::grid.arrange:

plot_list <- by(long_df, long_df$y_var, function(sub) {
  ggplot() + geom_boxplot(data = sub, mapping = aes(x = site, y = y_value)) +
    ggtitle(sub$y_var[[1]])
}) 

do.call(grid.arrange, args=list(grobs=plot_list, nrow = 1))

введите описание изображения здесь


Использование образцов данных OP

# RESHAPE WIDE TO LONG
yvars <- c("tn.1", "tss.1", "tp.1")

long_df <- reshape(df, varying=yvars, v.names="y_value",
                   times = yvars, timevar = "y_var",
                   idvar = c("site"), drop=c(2:9),
                   new.row.names = 1:1E4, direction = "long")
long_df$y_var <- gsub(".1", "", long_df$y_var)

# GRID ARRANGE PLOT
plot_list <- by(long_df, long_df$y_var, function(sub) {
  ggplot() + geom_boxplot(data = sub, mapping = aes(x = site, y = y_value)) +
    ggtitle(sub$y_var[[1]])
}) 

do.call(grid.arrange, args=list(grobs=plot_list, nrow = 1))

Вывод графика данных OP

person Parfait    schedule 05.08.2019
comment
Я рассматривал возможность использования facet_wrap, но мои значения данных сильно различаются: tss находится в пределах 1000, а tp в основном ниже 5. Я знаю, что можно установить разные масштабы, но проще просто создать отдельные графики и объединить их с grid. Я также скоро отредактирую свой вопрос с моими данными и дополнительными пояснениями. - person Orisa is your shield; 05.08.2019
comment
Понятно. Но все же рассмотрите длинный формат и запустите by на уникальных индикаторах (tn, tss, tp) из длинного формата. Затем используйте gridExtra.grid.arrange. Смотрите расширенный ответ. - person Parfait; 05.08.2019