在R中使用lapply与ggplot保存多幅PDF图像失败的问题求助
问题:使用ggplot按分组保存图像时生成的PDF无法打开
我有一个带分组变量的数据框,想用ggplot为每个分组生成并保存单独的图像。用lapply拆分数据后,用pdf函数保存以分组命名的文件,但生成的文件打不开。
示例代码与数据
# 生成带分组变量的随机数据框 set.seed(123) df <- data.frame( x = rnorm(100), y = rnorm(100), group = sample(LETTERS[1:4], 100, replace = TRUE) ) # 使用lapply遍历每个分组 lapply(split(df, df$group), function(d) { # 绘制每个分组的x vs y散点图 p <- ggplot(d, aes(x, y)) + geom_point() + ggtitle(paste("Group", d$group[1])) # 保存为以分组命名的PDF文件 pdf(paste0("output_data/", d$group[1], ".pdf")) p dev.off() })
已尝试的方法
- 使用
print(p)替代p ggsave在我的数据上表现不佳- 不在
sapply或lapply中运行脚本时一切正常
注意:同一脚本在其他设备上可以正常输出。
更新:sessionInfo() 输出
R version 4.2.1 (2022-06-23 ucrt) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows 10 x64 (build 22000) Matrix products: default locale: [1] LC_COLLATE=Chinese (Simplified)_China.utf8 LC_CTYPE=Chinese (Simplified)_China.utf8 LC_MONETARY=Chinese (Simplified)_China.utf8 [4] LC_NUMERIC=C LC_TIME=Chinese (Simplified)_China.utf8 attached base packages: [1] stats4 stats graphics grDevices utils datasets methods base other attached packages: [1] cowplot_1.1.1 patchwork_1.1.2 ggrepel_0.9.1 plyranges_1.16.0 org.Hs.eg.db_3.15.0 annotatr_1.22.0 [7] ggforce_0.4.1 GenomicFeatures_1.48.4 AnnotationDbi_1.58.0 Biobase_2.56.0 ggbio_1.44.1 ggplot2_3.4.0 [13] dplyr_1.1.0 rtracklayer_1.56.1 GenomicRanges_1.48.0 GenomeInfoDb_1.32.4 IRanges_2.30.1 S4Vectors_0.34.0 [19] AnnotationHub_3.4.0 BiocFileCache_2.4.0 dbplyr_2.2.1 BiocGenerics_0.42.0 loaded via a namespace (and not attached): [1] backports_1.4.1 Hmisc_4.7-1 systemfonts_1.0.4 plyr_1.8.7 [5] lazyeval_0.2.2 splines_4.2.1 BiocParallel_1.30.4 digest_0.6.29 [9] ensembldb_2.20.2 htmltools_0.5.3 fansi_1.0.3 magrittr_2.0.3 [13] checkmate_2.1.0 memoise_2.0.1 BSgenome_1.64.0 cluster_2.1.3 [17] tzdb_0.3.0 readr_2.1.3 Biostrings_2.64.1 matrixStats_0.62.0 [21] timechange_0.1.1 prettyunits_1.1.1 jpeg_0.1-9 colorspace_2.0-3 [25] blob_1.2.3 rappdirs_0.3.3 textshaping_0.3.6 xfun_0.33 [29] crayon_1.5.2 RCurl_1.98-1.9 graph_1.74.0 survival_3.3-1 [33] VariantAnnotation_1.42.1 glue_1.6.2 polyclip_1.10-0 gtable_0.3.1 [37] zlibbioc_1.42.0 XVector_0.36.0 DelayedArray_0.22.0 scales_1.2.1 [41] DBI_1.1.3 GGally_2.1.2 Rcpp_1.0.9 xtable_1.8-4 [45] progress_1.2.2 htmlTable_2.4.1 foreign_0.8-82 bit_4.0.4 [49] OrganismDbi_1.38.1 Formula_1.2-4 htmlwidgets_1.5.4 httr_1.4.4 [53] RColorBrewer_1.1-3 ellipsis_0.3.2 pkgconfig_2.0.3 reshape_0.8.9 [57] XML_3.99-0.11 farver_2.1.1 nnet_7.3-17 deldir_1.0-6 [61] utf8_1.2.2 RMariaDB_1.2.2 tidyselect_1.2.0 labeling_0.4.2 [65] rlang_1.0.6 reshape2_1.4.4 later_1.3.0 munsell_0.5.0 [69] BiocVersion_3.15.2 tools_4.2.1 cachem_1.0.6 cli_3.4.1 [73] generics_0.1.3 RSQLite_2.2.18 stringr_1.4.1 fastmap_1.1.0 [77] ragg_1.2.4 yaml_2.3.5 knitr_1.40 bit64_4.0.5 [81] purrr_0.3.5 KEGGREST_1.36.3 AnnotationFilter_1.20.0 RBGL_1.72.0 [85] nlme_3.1-157 mime_0.12 xml2_1.3.3 biomaRt_2.52.0 [89] compiler_4.2.1 rstudioapi_0.14 filelock_1.0.2 curl_4.3.3 [93] png_0.1-7 interactiveDisplayBase_1.34.0 tweenr_2.0.2 tibble_3.1.8 [97] stringi_1.7.8 lattice_0.20-45 ProtGenerics_1.28.0 Matrix_1.4-1 [101] vctrs_0.5.2 pillar_1.8.1 lifecycle_1.0.3 BiocManager_1.30.18 [105] data.table_1.14.2 bitops_1.0-7 httpuv_1.6.6 R6_2.5.1 [109] BiocIO_1.6.0 latticeExtra_0.6-30 promises_1.2.0.1 gridExtra_2.3 [113] codetools_0.2-18 dichromat_2.0-0.1 MASS_7.3-57 assertthat_0.2.1 [117] SummarizedExperiment_1.26.1 rjson_0.2.21 withr_2.5.0 regioneR_1.28.0 [121] GenomicAlignments_1.32.1 Rsamtools_2.12.0 GenomeInfoDbData_1.2.8 mgcv_1.8-40 [125] parallel_4.2.1 hms_1.1.2 grid_4.2.1 rpart_4.1.16 [129] MatrixGenerics_1.8.1 biovizBase_1.44.0 lubridate_1.9.0 shiny_1.7.3 [133] base64enc_0.1-3 interp_1.1-3 restfulr_0.0.15
解决方案
问题根源
在lapply的匿名函数中,直接调用pdf()+p+dev.off()的方式,ggplot对象不会被正确渲染到PDF设备中——非交互式环境(比如循环、apply函数)里,ggplot需要显式的print()触发绘制。结合你的Windows环境,还可能存在设备切换缓存、路径编码的问题。
可行方案
方案1:修复print()的使用逻辑
确保print(p)在PDF设备打开的范围内执行,同时用invisible()避免lapply返回大量无用的绘图对象:
# 先确保输出文件夹存在 if (!dir.exists("output_data")) dir.create("output_data") lapply(split(df, df$group), function(d) { p <- ggplot(d, aes(x, y)) + geom_point() + ggtitle(paste("Group", d$group[1])) pdf_file <- paste0("output_data/", d$group[1], ".pdf") pdf(pdf_file) print(p) dev.off() invisible(p) })
方案2:调整ggsave参数解决适配问题
如果之前ggsave表现不佳,试试指定PDF设备、明确尺寸,同时确保路径合法:
if (!dir.exists("output_data")) dir.create("output_data") lapply(split(df, df$group), function(d) { group_name <- d$group[1] p <- ggplot(d, aes(x, y)) + geom_point() + ggtitle(paste("Group", group_name)) ggsave( filename = paste0("output_data/", group_name, ".pdf"), plot = p, device = "pdf", width = 6, height = 4, dpi = 300 ) })
方案3:改用purrr::walk替代lapply
walk专门用于执行无返回值的操作,比lapply更适合保存文件的场景,避免不必要的对象缓存:
library(purrr) library(ggplot2) if (!dir.exists("output_data")) dir.create("output_data") split(df, df$group) %>% walk(function(d) { group_name <- d$group[1] p <- ggplot(d, aes(x, y)) + geom_point() + ggtitle(paste("Group", group_name)) ggsave(paste0("output_data/", group_name, ".pdf"), p) })
方案4:处理Windows路径编码问题
Windows下中文或特殊字符路径可能导致文件损坏,用normalizePath标准化路径:
if (!dir.exists("output_data")) dir.create("output_data") lapply(split(df, df$group), function(d) { p <- ggplot(d, aes(x, y)) + geom_point() + ggtitle(paste("Group", d$group[1])) pdf_file <- normalizePath(paste0("output_data/", d$group[1], ".pdf"), mustWork = FALSE) pdf(pdf_file) print(p) dev.off() })
额外排查点
- 关闭所有可能占用PDF文件的程序(比如Adobe Reader)
- 尝试更新
ggplot2到最新稳定版,或回退到3.3.6版本测试 - 检查R的工作目录是否正确,用
getwd()确认
内容的提问来源于stack exchange,提问作者zhang
相关产品推荐
相关产品推荐

