简介:这是一套包含五十套R语言科研绘图代码的资源包,面向有一定R基础的研究人员,覆盖柱状图、箱线图、小提琴图、热图、火山图、韦恩图、雷达图、桑基图、生存曲线、ROC曲线、PCA分析、GSEA/KEGG富集可视化等常用SCI图表类型。把input文件夹中的数据替换为自己的数据,在Rstudio中运行脚本即可出图,适合论文配图、课题探索与文章复现。
包体共六百九十个文件,约九十点二兆字节。核心为五十个R脚本,另有二百九十三张PNG示例图、一百一十一个XLS数据表、一百零二个HTML富集分析页、五十五个PDF说明文档和若干txt/gmt辅助文件,便于对照结果理解参数。已有一千一百一十人学习下载,这套即取即用的绘图模板覆盖多组差异比较、生存分析和富集分析等场景,能显著节省科研绘图时间。
1. 50套R语言SCI绘图脚本:为什么我建议你直接从这套代码改起
如果你跟我一样被SCI审稿人打回过“图太简陋”,或者在Rstudio里从零写一张火山图写到半夜,那你应该能理解:科研绘图真正贵的不是画图那一下,而是从数据清洗到出图参数的整个过程。这套资源我拿到手第一反应是,它把我过去两年积累的ggplot2、pheatmap、ComplexHeatmap、ggpubr等用法直接做成了50套可跑的R语言脚本,每套自带示例数据和出图逻辑,适用场景覆盖转录组、生信富集、临床预后这类最常被要求“重画”的图形。
它不是一个教学PPT,也不是把一堆代码扔给你自己琢磨。它更像一个可复现的起点:你要做的不是看懂每一行,而是找到跟你课题最接近的那一套,替换数据、改改颜色和坐标轴,就能在半小时内得到一张符合投稿规范的矢量图。适合正在写毕业论文、补实验数据、赶返修材料的人,也适合刚接触R语言但不想从函数手册啃起的新手,甚至是只想把origin那套图换成R出图风格的熟手。
2. 环境先行:Rstudio中把这套绘图脚本跑起来的配置顺序
2.1 版本与包管理:先用一棵“依赖树”看这套脚本
很多人在R语言绘图资源上翻车的第一个点不是代码本身,而是R版本和包版本对不上。拿到这50套脚本后,别急着立刻source,先去看它依赖哪些包。我过了一遍,核心依赖集中在 ggplot2、dplyr、tidyr、ggpubr、pheatmap、ComplexHeatmap、RColorBrewer、scales、gridExtra 和 survival 这几个上,其中ggplot2的版本建议在3.4以上,但不是越新越好。
判断方法很简单:脚本头注释里如果写了library(ggplot2),那么ggplot2版本至少得支持theme()和element_text()的最新参数。我一般会打开Rstudio的Session Info看一眼,或者直接在控制台跑一句packageVersion("ggplot2"),把结果和脚本注释里要求的版本对照。版本追得太新,有些老脚本用的aes_string()在新版里被废弃,反而要改代码;版本太老,after_stat()这类新写法的图又出不来。所以这套资源的思路是尽量避开版本敏感的函数,遇到确实用了新语法的脚本,脚本里基本会有注释说明需要哪个版本以上。
依赖树还有一个容易忽略的分支:系统级依赖。pheatmap本身是纯R包,但ComplexHeatmap会依赖circlize,而circlize在Linux服务器上安装时需要系统有libcurl、libxml2的开发头文件。你在自己Windows电脑上装好了,换到服务器上Rscript跑就报configuration failed,这不是脚本问题,是系统环境缺东西。我一般先在本地把图跑通,再决定要不要上服务器。
2.2 三步导入:从下载到第一次出图
拿到下载包后,我习惯的步骤是这样的。先把压缩包解压到一个没有中文和空格的路径,比如D:/Rplot_50,然后打开Rstudio,用setwd()设置工作目录,再跑一遍依赖安装。这里我给一个可以直接抄的检查流程:
cd /d/Rplot_50 Rscript -e "options(repos='https://cloud.r-project.org'); install.packages(c('ggplot2','dplyr','tidyr','ggpubr','pheatmap','RColorBrewer','scales','gridExtra'))"这条命令用Rscript从命令行触发包安装,options(repos=...)指定了清华或云镜像源,避免默认源下载超时。如果你在Rstudio里操作,也可以直接点右下角Packages面板的Install,但命令行方式的好处是会输出每个包的安装日志,哪个包失败一眼就能看到。安装完成后,先别急着看50个脚本,去找名字类似01_quick_start.R或00_check_environment.R的那个文件,这类脚本通常是作者用来做环境自检的。
自检脚本一般会做三件事:检查所有依赖包是否可加载、检查工作目录是否正确、跑一张最简单的散点图验证绘图链通不通。跑通了,说明你的Rstudio环境已经能承接后面的复杂脚本。如果自检脚本报there is no package called 'xxx',那说明这个包没装上,回到上一步单独装。装的时候注意看有没有ERROR: dependencies 'yyy' are not available这种提示,有的话要先装yyy,这是R包依赖的传递,硬装会一直报错。
这三步走完,理论上你已经能打开任意一个编号脚本,选中全部代码点Run。第一次跑我建议用Rstudio的Source按钮而不是Run,因为Source会完整执行脚本,变量名、函数定义和最后保存文件的语句都不会漏。之后每次修改参数,再按需选中局部代码段执行。
2.3 目录结构:先知道去哪改颜色和尺寸
这类绘图资源通常会把数据、脚本和输出分成三个目录。我看到的这套里面,结构大致是下面的样子:
| 目录/文件 | 作用 | 你需要动它的时机 |
|---|---|---|
data/ | 存放每个脚本配套的示例数据,多为CSV或TXT | 换成你自己的表达矩阵或临床表时 |
scripts/ | 50个R脚本,按图形类型编号 | 改配色、改坐标轴、改输出格式时 |
output/ | 跑完脚本后自动生成的图片文件 | 基本不用动,脚本会自动写入 |
theme_scipub.R | 统一的主题函数,控制字体、网格线、图例 | 想让所有图风格一致时优先改这里 |
README.md | 每个脚本的用途说明和依赖清单 | 不确定某个脚本是画什么图时先看这里 |
我特别建议你花半小时把theme_scipub.R或类似的公共函数文件读一遍。因为这50个脚本里可能有20个都在ggplot2后面加了+ theme_scipub(),这个公共函数决定了所有图的字体大小、坐标轴粗细、图例位置和背景网格。你后期想让所有图的标题从14号改成16号,不需要去50个脚本里逐个找,改这一个函数就行。
很多人拿到资源后直接挑一个最像的脚本往里面塞自己的数据,结果图出来的样式和自己想象的不一样,然后又去逐行改。我遇到过类似的翻车:改完数据跑出来坐标轴标签间距挤在一起,以为是数据问题,折腾半天才发现是主题函数里legend.position写死导致图例占了主图位置。所以我的习惯是,正式改数据之前,先跑一遍原始脚本,把原图保存好,之后再改,这个对比步骤能省很多排查时间。
3. 从数据到成图的完整链路:火山图与富集气泡图的改法
3.1 火山图:看懂 log2FoldChange 与 padj 的双重过滤
火山图是转录组差异表达分析的标准出图,也是这套资源里最常被单独拎出来改的脚本。原理上,它把每个基因的差异表达倍数取log2作为横轴,把校正后的p值取负log10作为纵轴,于是显著上调的基因在右上角,显著下调的在左上角,不显著的全部沉到底部。脚本里最关键的一段是阈值过滤,常见写法是|log2FoldChange| > 1且padj < 0.05。
library(ggplot2) library(dplyr) # 读取差异表达结果,通常来自DESeq2或edgeR输出 deg <- read.csv("data/deg_results.csv", row.names = 1) # 双重过滤:倍数变化绝对值大于2,校正p值小于0.05 deg <- deg %>% mutate(threshold = ifelse( abs(log2FoldChange) > 1 & padj < 0.05, ifelse(log2FoldChange > 0, "up", "down"), "ns" )) p <- ggplot(deg, aes(x = log2FoldChange, y = -log10(padj), color = threshold)) + geom_point(size = 1.2, alpha = 0.7) + scale_color_manual(values = c("up" = "#C0392B", "down" = "#2874A6", "ns" = "#B0B0B0")) + theme_classic(base_size = 14) + labs(x = "log2 Fold Change", y = "-log10(adj P value)") ggsave("output/volcano_plot.pdf", p, width = 6, height = 5, dpi = 300)这里有个容易被新手忽略的细节:mutate()里嵌套了两个ifelse()。外层的ifelse(abs(log2FoldChange) > 1 & padj < 0.05, ...)先做综合判断,是候选差异基因才进入下一步;内层再用log2FoldChange的正负区分上下调。如果只写一层判断,会出现“padj显著但倍数变化极小”的基因被错标成显著,这就是火山图上那些不该出现在两侧的散点的来源。
scale_color_manual(values = ...)里的三个颜色值我建议固定住:上调用红色,下调用蓝色,不显著用灰色。这是期刊最常接受的三色方案,不要换成渐变色。ggsave()里我写了width=6, height=5, dpi=300,这是单栏图的典型尺寸;如果是双栏图,宽度改成7.5到8英寸更合适。这个尺寸后面每套脚本都会用到,先记住这个参数。
3.2 富集气泡图:GeneRatio、Count 与 p.adjust 三列怎么喂进去
富集气泡图是GO或KEGG富集分析结果的标配图形,它用横轴GeneRatio表示注释到该通路的基因占全部基因的比例,纵轴是通路名称,气泡大小代表基因Count,颜色深浅代表p.adjust。这套资源里画气泡图的脚本,核心是把clusterProfiler的输出整理成ggplot2能直接识别的长格式。
library(ggplot2) # 假设enrich结果已存在,包含GeneRatio、Count、p.adjust、Description四列 go <- read.csv("data/GO_enrich_results.csv") # 把Description转成因子并按GeneRatio排序,避免y轴乱序 go$Description <- factor(go$Description, levels = go$Description[order(go$GeneRatio)]) p <- ggplot(go, aes(x = GeneRatio, y = Description)) + geom_point(aes(size = Count, color = p.adjust), alpha = 0.8) + scale_color_gradient(low = "#B03A2E", high = "#F5B7B1") + scale_size_continuous(range = c(3, 10)) + theme_bw(base_size = 13) + labs(x = "GeneRatio", y = NULL, color = "adj P value", size = "Count") ggsave("output/go_bubble.pdf", p, width = 7, height = 6, dpi = 300)很多人在这一步会遇到图出来之后y轴通路顺序乱七八糟的问题。原因不是数据错了,而是没把Description转成因子并按数值排序。ggplot2的y轴字符变量默认按字母表排序,你必须手动指定因子水平。上面代码里order(go$GeneRatio)这一步就是把通路按GeneRatio从小到大排好,再把这个顺序固化成因子水平。
scale_color_gradient()的参数我一般会按“富集的通路p.adjust越小越红”来调,low端是显著,high端是不显著。注意这里和火山图相反:火山图颜色区分上下调,富集图颜色连续渐变,二者不要套同一个配色习惯。气泡大小方面,scale_size_continuous(range = c(3, 10))里的范围要根据你自己数据里Count的分布微调,Count最大值只有5的时候,range上限设20就会让你的气泡全部变成大饼。
3.3 配色与出图尺寸:把 300 DPI 写进函数而不是记在脑子里
这套50个脚本里,几乎所有ggsave()的参数风格都是统一的:width、height用英寸为单位,dpi=300。但真正到投稿的时候你会发现,不同期刊对图片尺寸的要求不一样。有的要单栏86mm,有的要双栏170mm,还有的只要像素宽度。所以我拿到这套资源后做的第一件事,就是把出图参数从所有脚本里抽出来,写成一个统一的保存函数。
save_fig <- function(p, filename, width = 6, height = 5, device = "pdf") { ggsave(filename, plot = p, width = width, height = height, dpi = 300, device = device) }这个函数的好处是,你只需要记住save_fig(p, "fig1.pdf")这一句,所有脚本里的ggsave()都可以替换成它。要转成PNG或TIFF时,改device参数就行。我一般PDF用于投稿,PNG用于组会PPT,TIFF用于部分期刊系统要求的分辨率检查,三者用的都是同一个函数入口,不会出现同一个图在两种格式下尺寸不一致的情况。
字体方面也有一个统一约定:所有文本元素建议用theme()统一设置family="sans",不要在一张图里混用Arial和Helvetica。R在Linux服务器上渲染PDF时,波恩字体匹配不像Windows那么智能,混用字体可能在最后印刷时被字体替换,出现图注字体和其他图不一致的尴尬。统一用sans会省很多事。
4. 这批脚本里最容易被忽略的三个硬点
4.1 因子顺序:图例和坐标轴不听你话的根源
做过几个图之后你就会发现,ggplot2里图例顺序、坐标轴顺序和你的预期对不上,十有八九是因子顺序问题。比如你有一个分组变量Group,值是c("Control", "Treat", "Model"),读入R后默认是字符型,ggplot2按字母排序,于是图例顺序变成Control, Model, Treat,完全打乱了你论文里分组的逻辑顺序。
解决方式是把分组变量先转成factor并显式指定levels:
data$Group <- factor(data$Group, levels = c("Control", "Model", "Treat"))这套资源里的脚本普遍已经写了这一行,所以直接跑没问题。但换成你自己的数据时,要注意你的分组名和原脚本的分组名是否一致。不一致时,factor()的levels里写的是原脚本的组名,你的数据会变成NA,跑出来的图直接缺一个分组,而且不报错。这是静默错误里最坑的一种。
我一般的检查顺序是:换数据后先跑一个unique(data$Group),看返回的分组列表,再回过去对照脚本里的levels,二者完全一致才继续。这一步能省掉后面至少十分钟的排查。
4.2 分组颜色固定:换数据后颜色不要跳
另一个我见过很多人踩的地方是,同一张散点图,数据换了一个批次,跑出来两组的颜色对调了。原因在于你没显式固定颜色映射。ggplot2的默认配色是从当前数据的分组水平推导的,数据顺序变了,因子水平顺序变了,颜色分配自然就变了。
在脚本里用scale_color_manual(values = c(...))或scale_fill_manual()固定颜色时,values的顺序一定要和你数据的因子水平顺序对应。推荐写法是:
levels_of_group <- c("Control", "Model", "Treat") colors_for_group <- c("#1F618D", "#B03A2E", "#117A65") data$Group <- factor(data$Group, levels = levels_of_group) p <- ggplot(data, aes(x = time, y = value, color = Group)) + geom_point() + scale_color_manual(values = colors_for_group)这里遵循一个原则:levels定义和colors定义分开写,不要直接在scale_color_manual()里写颜色字符串。这样当你的数据分组顺序变化时,只需调整levels,颜色自动跟着对应。资源里有一小部分脚本直接把颜色写在scale_color_manual里,我建议你拿到手后统一改成这种分开写的形式,不然下次复用一定会踩颜色跳变的坑。
4.3 字体与中文:PDF 导出发虚的常见原因
很多R语言绘图脚本在屏幕上预览时一切正常,一导出PDF再打开,中文标签变成方框,或者文字边缘发虚。这不是因为你眼睛花,而是R的默认PDF设备对字体处理不完整。Windows环境下,Rstudio里直接ggsave("fig.pdf")用的cairo后端对中文支持时好时坏,尤其是ggplot2里element_text(family="SimSun")这类中文字体设置,导出后几乎必出问题。
我在这套资源的实际使用中,把出图设备统一改成device = cairo_pdf,中文字体才稳定下来。具体到代码里是这样:
ggsave("output/fig_chinese.pdf", plot = p, width = 6, height = 5, device = cairo_pdf())注意这里device = cairo_pdf()是带括号的调用,不是字符串传参。如果你传device = "pdf",走的是系统默认的pdf后端,中文字体还是可能丢失。另外,如果你的图里本身不需要中文,我强烈建议图内所有标签都用英文,投稿时也能避免字体匹配问题。把中文留给Excel里的原始数据列名,画图时再改成英文标签,是代价最小、最稳妥的做法。
5. 避坑手册:R语言科研绘图常见的五个翻车现场
5.1 散点标签全部挤成一坨黑疙瘩
现象:画富集图或韦恩图时,geom_text()或geom_label()加的标签在图上挤成一团,交叠在一起完全看不清。
原因:ggplot2的geom_text()默认不会自动避让。标签位置是由你指定的x和y决定的,当多个点的坐标接近时,文本自然叠在一起。很多人以为是字体太小,改小字号后发现更糊了。
解决:改用ggrepel包的geom_text_repel()。它会自动计算标签间的重叠并做偏移,代价是运行速度变慢。我一般在标注基因名、通路名时用geom_text_repel(size=3, max.overlaps=20)。如果你不想要自动偏移带来的随机性,也有一个笨办法:手动给坐标列乘一个1.02的偏移系数,把相邻标签稍微分开。ggrepel是这套资源环境里最好用的后悔药。
5.2 Rstudio里跑中文文本,图出来全是乱码
现象:脚本注释里有中文,或者数据文件里的分组名称是中文,跑出来的图例、坐标轴标签全部变成一个个方块或问号。
原因:Windows下R的默认locale可能不是UTF-8,读取CSV时用了系统默认编码,把UTF-8的中文读成了乱码,后面画图时乱码跟着进了图。
解决:读CSV时明确指定编码,read.csv("data/xxx.csv", fileEncoding = "UTF-8")。如果还乱,就把fileEncoding改成GBK试试,因为部分Windows数据源导出的CSV是GBK编码。另外Rstudio里File > Reopen with Encoding可以直接看到当前文件的真实编码。我的一般习惯是:先把所有数据文件统一转成UTF-8无BOM格式,再在脚本里固定写fileEncoding="UTF-8",一劳永逸。
5.3 拼多图时图例重复出现,占了一整行空间
现象:用gridExtra::grid.arrange()或cowplot::plot_grid()把四个小图拼成大图时,每个小图都带着自己的图例,拼完之后图例重复,还占掉了大量绘图区域。
原因:拼图前小图各自生成了独立的图例,拼图函数默认不做去重。
解决:在拼图前,从每个子图里提取图例,只保留一个,其余全部删掉。这里给一个常用写法:
library(ggplot2) library(gridExtra) # p1、p2、p3、p4 是四个小图 # 先删掉p1以外的图例 p2_no_legend <- p2 + theme(legend.position = "none") p3_no_legend <- p3 + theme(legend.position = "none") p4_no_legend <- p4 + theme(legend.position = "none") # 保留p1的图例,通过legend.position调整位置 grid.arrange(p1, p2_no_legend, p3_no_legend, p4_no_legend, ncol = 2)实际投稿时,期刊编辑对小图图例重复这件事非常敏感,经常直接打回要求重拼。我更推荐的做法是:四张小图都用theme(legend.position="none")去掉图例,然后用cowplot::get_legend()单独提取一个图例放到拼图的下方或右侧,这样拼出来干净也专业。
5.4 坐标轴截断后,截断处毫无标记
现象:为了让图更好看,想截断坐标轴,直接在scale_y_continuous(limits=c(0, 50))里写死范围,结果超范围的数据点全部消失,而且截断处没有提示标记。
原因:limits的作用是裁掉数据而不是截断坐标轴。ggplot2本身不提供简单的坐标轴截断标记功能,直接写limits会把超出范围的数据静默删掉,审稿人一眼就能看出异常。
解决:不要用limits做数据截断,改用coord_cartesian(ylim = c(0, 50))。两者区别是:coord_cartesian只放大可视区域,不删除数据点,超出的数据点仍然参与统计和位置计算,只是不显示。如果你的图形要求体现代谢或表达量差异很大,最好的处理是保留完整坐标轴,而不是截断,或者使用分面图把高表达组单独放在一个子图里。
5.5 PDF导出后在阅读器里文字发虚、线条毛糙
现象:同一个代码,输出的PNG很清晰,PDF放大到200%后线条发虚,文字边缘不锐利。
原因:R的默认PDF设备在部分环境下没有开启cairo抗锯齿,或者你导出的PDF实际是矢量没有真正嵌入字体。另外,ggplot2默认的line宽度是以mm为单位,过细的线在印刷时会被削平。
解决:导出时用cairo_pdf()设备,并且把线条粗细整体调大一号。在主图脚本里,可以把geom_line(linewidth=0.8)这类参数统一改成1.0以上,特别是方框、坐标轴线用theme(axis.line = element_line(linewidth=0.8))。如果你要投稿的期刊接受TIFF,直接导300 DPI的TIFF比PDF更省心,字体问题完全绕开。
6. 改脚本不变形:把50套固化成一个科研绘图流水线
6.1 用 config.R 把 DPI、字体、配色收拢到一处
这50套脚本最大的复用价值,不是每张图的代码本身,而是里面沉淀的绘图习惯。我建议你新建一个config.R,把散落在各脚本里的公共参数集中起来。比如字体统一用sans,分辨率固定300 DPI,常用的上调配色、下调配色、不显著配色都定义成变量。这样后续你改任何一个图时,只需要改config.R,所有脚本的风格自动跟着更新。
# config.R BASE_SIZE <- 14 FIG_DPI <- 300 COL_UP <- "#C0392B" COL_DOWN <- "#2874A6" COL_NS <- "#B0B0B0" FONT_FAMILY <- "sans"之后的脚本里,所有硬编码的theme_classic(base_size=14)、dpi=300都可以替换成引用这些常量。好处是当你准备投稿BMC系列需要把全图字体调到16号时,只需改BASE_SIZE一处。
6.2 用我自己的 theme_scipub() 兜底
我拿到这套资源后的习惯做法是,把它的公共主题函数复制一份,改成自己的theme_scipub(),里面默认开启legend.position = "right"、panel.grid = element_blank()、axis.line = element_line(linewidth=0.8)。然后对自己所有新图强制走这个主题。这样做最大的好处是,你课题组里所有图风格强制统一,不会出现一篇文章里三张图三种背景网格的情况。
6.3 输出自检清单
最后贴上我每次出图前必过一遍的清单:
| 检查项 | 检查方法 |
|---|---|
| 图例顺序是否和论文分组顺序一致 | levels(data$Group) |
| 所有坐标轴标签是否包含单位 | 目测 |
| 字体统一 | 导出的PDF在300%下是否有锯齿 |
| 样本点是否被坐标轴截断 | 检查coord_cartesian而非limits |
| 颜色是否色盲友好 | 转成灰度图看对比度 |
| DPI是否300 | 用R读回PDF检查页面尺寸与实际像素 |
从那以后,我每次拿到新的绘图脚本,都会强制走一遍这套流程——先看依赖,再跑原图,改配置,最后过自检清单。照着这套做,50套图跑下来,基本不会再在投稿前夜被审稿人打回来重画。希望帮到你。
本文还有配套的精品资源,点击获取