Предположим, что из цикла я получаю определенный набор графиков, которые сохраняю в списке графиков, как в моем воспроизводимом примере.
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, поэтому ответ должен быть совместим со структурой, которую я предлагаю.
вам просто не хватает явного grid::grid.draw, который необходим для сеточной графики в цикле?
@CPB ваше решение создает пустой PDF-файл, что было моей первоначальной проблемой.
Другое решение, предложенное @user20650, предоставляет поврежденный PDF-файл, который невозможно открыть.
Для меня все решения дают ожидаемый результат в формате PDF (5 страниц, не пустые, без ошибок) для минимального воспроизводимого примера в вопросе. Пустые страницы и поврежденные PDF-файлы могут указывать на то, что одновременно открыты несколько графических устройств. Посмотреть активные графические устройства можно по телефону .Devices.
Спасибо @CPB, тоже сработало, но решение пользователя лучше всего соответствовало требованиям моего исходного вопроса.





Вот подход, использующий 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 и посмотреть, смогу ли я что-нибудь с его помощью получить.
Адаптация этого решения с добавлением @CPB также сработала grDevices::cairo_pdf("plots.pdf", onefile = TRUE) for (i in plot_list) wrap_plots(i, design = lay) %>% print, хотя другое решение было более адаптировано к требованиям вопроса.
Чтобы построить 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()
Это работает, и исправление тоже. Спасибо!
Является ли проблема с ответом Стефана о том, что решение нужно вызывать в цикле, а не в режиме Lapply? В этом случае вы можете заменить вызов lapply в ответе Стефана на его широкий эквивалент
for (i in plot_list) wrap_plots(i, design = lay) %>% print(после загрузки библиотеки magrittr).