Как индексировать фрейм данных для использования ggplot в цикле

Я борюсь с созданием нескольких ggplots с помощью цикла. Я использую данные в следующем формате:

a <- c(1,2,3,4)
b <- c(5,6,7,8)
c <- c(9,10,11,12)
d <- c(13,14,15,16)
time <- c(1,2,3,4)
data <- cbind(a,b,c,d,time)

То, что я хочу создать, — это список графиков, отображающих одну из букв в зависимости от переменного времени. Что я пробовал следующим образом:

library(ggplot2)
library(gridExtra)

plots <- list()
for (i in 1:4){
    plots[[i]] <- ggplot() + geom_line(data = data, aes(x = time, y = data[,i]))
}
grid.arrange(plots[[1]], plots[[2]], plots[[3]], plots[[4]])

В результате получается в четыре раза больше четвертого сюжета. Как правильно проиндексировать это, чтобы создать четыре предполагаемых графика?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

(Впереди: причина того, что все ваши графики идентичны, связана с «ленивой» оценкой кода ggplot. См. Мой № 2 ниже, где я указываю, что data[,i] оценивается, когда вы пытаетесь построить данные, и в этот момент i 4, последний проход в петле for.)

  1. Обычно предпочтительнее/рекомендуется использовать data.frames вместо матриц или векторов (как вы делаете здесь). Это дает немного больше силы и контроля.

    data <- data.frame(a,b,c,d,time)
    
  2. Кроме того, я предпочитаю lapplyfor-петлям и list-ам по разным (некоторые субъективным) причинам. В конечном счете, проблема, с которой вы столкнулись, заключается в том, что ggplot2 лениво оценивает данные, поэтому plots представляет собой список с четырьмя графиками, которые ссылаются на i... и это реализуется, когда вы пытаетесь построить их все, и в этот момент i 4 (из последнего прохода через петлю). Одним из преимуществ использования lapply является то, что ссылка на i является только локальной (внутри anon-func) версией i, которая сохраняется, как и следовало ожидать.

    plots <- lapply(names(data)[1:4],
      function(nm) ggplot(data, aes(x = time, y = .data[[nm]])) + geom_line())
    gridExtra::grid.arrange(plots[[1]], plots[[2]])
    

  3. Я также предпочитаю patchworkgridExtra, в основном потому, что он делает более настраиваемые макеты немного более интуитивно понятными, а также добавляет такие функции, как выравнивание по осям, общие легенды, общие заголовки и т. д. (ни одна из этих других функций здесь не демонстрируется).

    library(patchwork)
    plots[[1]] / plots[[2]] # same plot
    plots[[1]] + plots[[2]] # side-by-side instead of top/bottom
    (plots[[1]] + plots[[2]]) / (plots[[3]] + plots[[4]]) # grid
    
  4. В конечном счете, однако, я предполагаю, что аспекты могут быть полезными и очень мощными. Для этого нам нужно преобразовать данные в «длинный формат», чтобы имена столбцов a-b фактически находились в одном столбце.

    reshape2::melt(data, id.vars = "time") |>
      ggplot(aes(time, value)) +
      geom_line() +
      facet_grid(variable ~ ., scales = "free_y")
    

    Я предпочел независимые (бесплатные) y-шкалы, следовательно, scales = "free_y". Попробуйте без него, если хотите увидеть варианты. (Есть также scales = "free_x" и scales = "free" (оба).)

    Чтобы увидеть, что я имею в виду под «длинным» форматом:

    reshape2::melt(data, id.vars = "time")
    #    time variable value
    # 1     1        a     1
    # 2     2        a     2
    # 3     3        a     3
    # 4     4        a     4
    # 5     1        b     5
    # 6     2        b     6
    # 7     3        b     7
    # 8     4        b     8
    # 9     1        c     9
    # 10    2        c    10
    # 11    3        c    11
    # 12    4        c    12
    # 13    1        d    13
    # 14    2        d    14
    # 15    3        d    15
    # 16    4        d    16
    

    Это также можно сделать с помощью tidyr::pivot_longer(data, -time), хотя имя variable теперь name. Для этого использования нет преимущества для reshape2::melt или tidyr::pivot_longer; в последнем есть возможности для значительно более сложного поворота, не относящегося к этим данным.


Данные

data <- structure(list(a = c(1, 2, 3, 4), b = c(5, 6, 7, 8), c = c(9, 10, 11, 12), d = c(13, 14, 15, 16), time = c(1, 2, 3, 4)), class = "data.frame", row.names = c(NA, -4L))

Другие вопросы по теме