如何用循环绘制多列ggplot折线图并按每页4个保存为PDF
解决ggplot批量绘图并分页输出PDF的问题
需求:处理149个序列,绘制以year为x轴的折线图,生成PDF文档,每页包含4个图。基础plot代码可正常运行,但适配ggplot时出现问题。
可正常运行的基础plot代码
dfMA <- structure(list(year = c(2000:2005),a = c(0.2569, 0.0145896, 0.0369, 0.025986, 0.12569, 0.3695), b = c(0.125, 0.04582, 0.2569, 0.256369, 0.25698, 0.1456 ), c = c(0.2584, 0.05698, 0.1258, 0.2569, 0.098563, 0.1569)), row.names = c(NA,-6L), class = "data.frame") pdf("test.pdf") par(mfrow=c(2,2)) invisible(lapply(colnames(dfMA),function(x){ plot(dfMA$year, dfMA[,x],main=x,type="l",xaxt="n", ylab = "Ring width [mm]", xlab = "", ylim=c(0,0.8)) grid(nx = NULL, ny = NULL,lty = 2, col = "gray", lwd = 2) axis(1, at = seq(1985, 2019, by = 2), las=2) })) dev.off()
原ggplot代码的问题
invisible(lapply(colnames(dfMA),function(x){ ggplot(dfMA, aes(x=i, y = year)) + ggarrange(ncol=2,nrow=2) + geom_line() + print() }))
存在的核心问题:
- 映射关系完全错误:
aes(x=i, y=year)颠倒了x/y轴,应该x为year,y为对应序列的数值 ggarrange用法错误:不能直接拼接在ggplot对象后,需先收集所有绘图对象再分组排版- 未正确引用循环变量:没有指定y轴对应
dfMA中的x列 - 缺少分页逻辑:没有处理每4个图分一页的需求
正确的ggplot解决方案
方法1:长格式数据+分面排版(简洁高效)
先将宽格式数据转为长格式,用facet_wrap自动实现每页4个图的排版:
library(tidyverse) library(ggpubr) # 转换为长格式数据 df_long <- dfMA %>% pivot_longer(cols = -year, names_to = "series", values_to = "ring_width") # 输出PDF pdf("ggplot_test_facet.pdf", width = 10, height = 8) ggplot(df_long, aes(x = year, y = ring_width)) + geom_line() + # 替代基础plot中的grid函数,添加灰色网格线 geom_hline(yintercept = seq(0, 0.8, 0.2), lty = 2, col = "gray", lwd = 2) + scale_x_continuous(breaks = seq(1985, 2019, by = 2)) + labs(y = "Ring width [mm]", x = "") + # 每页4个图,按序列分面 facet_wrap(~series, ncol = 2, nrow = 2) + theme_bw() + # 旋转x轴标签,对应基础plot的las=2 theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) dev.off()
方法2:循环生成绘图对象+分组排版(灵活可控)
如果需要对每个图做个性化调整,可先循环生成所有ggplot对象,再分组排版输出:
library(ggplot2) library(ggpubr) # 获取所有序列列名(排除year列) series_cols <- colnames(dfMA)[-1] # 生成所有ggplot对象的列表 plot_list <- lapply(series_cols, function(x) { ggplot(dfMA, aes(x = year, y = .data[[x]])) + geom_line() + geom_hline(yintercept = seq(0, 0.8, 0.2), lty = 2, col = "gray", lwd = 2) + scale_x_continuous(breaks = seq(1985, 2019, by = 2)) + labs(title = x, y = "Ring width [mm]", x = "") + ylim(0, 0.8) + theme_bw() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) }) # 将绘图列表按每4个一组拆分 n_pages <- ceiling(length(plot_list)/4) plot_groups <- split(plot_list, rep(1:n_pages, each=4)[1:length(plot_list)]) # 输出到PDF pdf("ggplot_test_loop.pdf", width = 10, height = 8) lapply(plot_groups, function(group) { print(ggarrange(plotlist = group, ncol = 2, nrow = 2)) }) dev.off()
说明:
- 用
.data[[x]]引用循环变量对应的列,规避ggplot非标准求值的问题 - 两种方法均实现了每页4个图的PDF输出,方法1更简洁,方法2适合需要单独调整单图细节的场景
内容的提问来源于stack exchange,提问作者Bellis
相关产品推荐
相关产品推荐

