Список графиков, расположенных в определенном макете в многостраничном PDF-файле, не работает

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

plot_list = list()

for (i in 1:5){

dummy1 = ggplot(iris, aes(x = Petal.Length)) +
  geom_histogram()

dummy2 = ggplot(iris, aes(x = Petal.Length, color=Species)) +
  geom_boxplot()

dummy3 = ggplot(iris, aes(x = Petal.Length, y=Petal.Width, color=Species)) +
  geom_point()

plot_list[[length(plot_list)+1]] = list(dummy1,dummy2,dummy3) 

}

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

lay = rbind (c(1,1,2,2),
             c(1,1,3,3),
             c(1,1,3,3))


grDevices::cairo_pdf("plots.pdf", onefile = TRUE)
for (i in seq(length(plot_list))) {
  do.call('marrangeGrob',list(plot_list[[i]], layout_matrix=lay))  
}
dev.off()

К сожалению, он возвращает только пустой PDF-файл с одной страницей. Любая помощь приветствуется.

ОБНОВЛЕНИЕ: Очень важно, что мой пример является воспроизводимым, но его структуру нельзя изменить. Причина, по которой он возникает из for, заключается в том, что в моем исходном коде он состоит из foreach, поэтому ответ должен быть совместим со структурой, которую я предлагаю.

Является ли проблема с ответом Стефана о том, что решение нужно вызывать в цикле, а не в режиме Lapply? В этом случае вы можете заменить вызов lapply в ответе Стефана на его широкий эквивалент for (i in plot_list) wrap_plots(i, design = lay) %>% print (после загрузки библиотеки magrittr).

CPB 26.02.2024 11:46

вам просто не хватает явного grid::grid.draw, который необходим для сеточной графики в цикле?

user20650 26.02.2024 16:37

@CPB ваше решение создает пустой PDF-файл, что было моей первоначальной проблемой.

Unai Vicente 27.02.2024 10:23

Другое решение, предложенное @user20650, предоставляет поврежденный PDF-файл, который невозможно открыть.

Unai Vicente 27.02.2024 10:23

Для меня все решения дают ожидаемый результат в формате PDF (5 страниц, не пустые, без ошибок) для минимального воспроизводимого примера в вопросе. Пустые страницы и поврежденные PDF-файлы могут указывать на то, что одновременно открыты несколько графических устройств. Посмотреть активные графические устройства можно по телефону .Devices.

CPB 27.02.2024 13:55

Спасибо @CPB, тоже сработало, но решение пользователя лучше всего соответствовало требованиям моего исходного вопроса.

Unai Vicente 28.02.2024 11:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
6
199
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вот подход, использующий lapply вместо цикла for и patchwork для объединения ваших графиков:

library(patchwork)
library(ggplot2)

plot_list <- lapply(
  1:5,
  \(x) {
    list(
      ggplot(iris, aes(x = Petal.Length)) +
        geom_histogram(),
      ggplot(iris, aes(x = Petal.Length, color = Species)) +
        geom_boxplot(),
      ggplot(iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
        geom_point()
    )
  }
)

lay <-
"
1122
1133
1133
"

grDevices::cairo_pdf("plots.pdf", onefile = TRUE)
lapply(
  plot_list, patchwork::wrap_plots,
  design = lay
)
#> [[1]]
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#> 
#> [[2]]
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#> 
#> [[3]]
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#> 
#> [[4]]
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#> 
#> [[5]]
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
dev.off()
#> quartz_off_screen 
#>                 2

И второй вариант — использовать grid.arrange:

grDevices::cairo_pdf("plots.pdf", onefile = TRUE)
for (i in seq(length(plot_list))) {
  grid.arrange(plot_list[[i]], layout_matrix = lay)
}
dev.off()

Спасибо, Стефан, к сожалению, я добавил for в пример, потому что перед графиком на каждой отдельной итерации происходит несколько манипуляций с данными, и, следовательно, мне нужно убрать их из plot_list. В этом случае, даже если мне нравится использовать семейные функции apply, мне понадобится другая альтернатива, использующая метод извлечения из списка. Я попробую прочитать о patchwork и посмотреть, смогу ли я что-нибудь с его помощью получить.

Unai Vicente 22.02.2024 18:10

Адаптация этого решения с добавлением @CPB также сработала grDevices::cairo_pdf("plots.pdf", onefile = TRUE) for (i in plot_list) wrap_plots(i, design = lay) %>% print, хотя другое решение было более адаптировано к требованиям вопроса.

Unai Vicente 28.02.2024 11:41
Ответ принят как подходящий

Чтобы построить ggplot или другую сеточную графику, требуется явная печать: используйте grid::grid.draw (см. FAQ 7.22).

Кроме того, с помощью marrangeGrob можно добавить дополнительную новую страницу; это исправление к сожалению, имеет пустую страницу в начале, но это все еще работает. Или вы можете переместить marrangeGrob за пределы вызова pdf... если это соответствует нашему рабочему процессу.

library(ggplot2)
library(gridExtra)
library(grid)

# Baptiste's fix
# if your example in your question represents your real example then you 
# could use `arrangeGrob` and avoid some of the newpage tweaks
grid.draw.arrangelist <- function(x, ...) {
  for(ii in seq_along(x)){
    if (ii>1) grid.newpage() 
     grid.draw(x[[ii]])
}
}

# Output plot
grDevices::cairo_pdf("plots.pdf", onefile = TRUE)

for (i in seq_along(plot_list)) {
  
  p <- marrangeGrob(grobs=plot_list[[i]], 
                    layout_matrix=lay, 
                    top = quote(paste("page", i, "of", length(plot_list))))
  grid::grid.draw(p) # need to be explicitly drawn 
  
  if (i < length(plot_list)) grid.newpage() # so plots are not drawn over each other
}

dev.off()

Это работает, и исправление тоже. Спасибо!

Unai Vicente 27.02.2024 18:25

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

Построение графика с помощью facet_wrap дает ошибку, размер которой должен быть 2 или 1, а не 28
Как использовать отдельные дни на годовом графике
Рисование линий между странами на заранее определенной карте Робинсона
Как удалить любые ячейки сетки за пределами места их пересечения с многоугольником в ggplot
Как создать несколько осей Y в ggplot2 (по одной для каждой переменной)
Ошибка «неиспользуемый аргумент» при использовании «purrr::pwalk» для сохранения ggplots из столбца списка во вложенном фрейме данных
Несколько значений в одной плитке с помощью geom_tile
Постройте все графики, созданные в функции, с минимальным диапазоном оси Y, необходимым для отображения доверительного интервала (r)
Ggplot — Цветная геометрическая рабочий пример с заполненными точками
Можно ли построить доверительный интервал, выходящий за пределы диапазона в ggplot?