ARTICLE · 1012401
「R绘图模板」非平衡分组柱状图:当各组样本数不一样时,如何画出漂亮的带误差棒和显著性标记的图?


📚 好书推荐:我和丁思源老师联合机械工业出版社编著的《R语言科研绘图》已正式上线!这是一本真正写给科研人的R语言绘图实战书,涵盖ggplot2、lattice等主流绘图库,从基础到进阶,从热图到网络图,从传统绘图到AI辅助(DeepSeek),你论文里需要的图,基本都能找到。需要的小伙伴可以直接在各大电商平台搜索 “R语言科研绘图 机械工业出版社” 即可购买。
在科研绘图中,柱状图是最常用的图形之一。但当你的分组数据出现“非平衡”——也就是每个大组下面的子组数量不一样时,普通的柱状图就会“翻车”:柱子宽度不一致、误差棒对不上、显著性标记位置偏移……
今天,我将用一套完整的R代码,带你解决这个难题。不仅会画出整齐的非平衡分组柱状图,还会添加误差棒和显著性字母标记,让你的图表既专业又美观。
一、什么时候会遇到“非平衡分组柱状图”?
想象一下这样的实验设计:
GroupA 下面有2个子组(Subgroup1、Subgroup2)
GroupB 下面有3个子组
GroupC 下面有2个子组
GroupD 下面有4个子组
这就是典型的“非平衡分组”——每个大组下面的子组数量不一样。如果用普通的 geom_col() 加 position_dodge(),会出现以下问题:
所以,我们需要一套“进阶方案”。
二、数据准备与预处理
2.1 加载所需R包
library(ggplot2) # 绘图核心包library(dplyr) # 数据操作library(ggh4x) # ggplot2扩展,用于坐标轴细节library(agricolae) # 用于LSD多重比较
2.2 加载数据# 数据格式:Group(大组)、Subgroup(子组)、Value(数值)df <- read.table("data.txt", header = TRUE, check.names = FALSE, sep = "\t")
# 数据格式:Group(大组)、Subgroup(子组)、Value(数值)df <- read.table("data.txt", header = TRUE, check.names = FALSE, sep = "\t")2.3 计算均值与标准差
# 按大组和子组计算均值和标准差df_summary <- df %>%group_by(Group, Subgroup) %>%summarise(Mean = mean(Value),SD = sd(Value),.groups = "drop")
2.4 显著性计算(LSD多重比较)
这里需要对每个大组内部的子组进行多重比较。由于有多个大组,我们需要循环处理:
# GroupAvariance <- aov(Value ~ Subgroup, data = df[df$Group == "GroupA", ])MC <- LSD.test(variance, "Subgroup", p.adj = "bonferroni")data_sg_GroupA <- MC$groupsdata_sg_GroupA$Subgroup <- rownames(data_sg_GroupA)data_sg_GroupA$Group <- "GroupA"# GroupBvariance <- aov(Value ~ Subgroup, data = df[df$Group == "GroupB", ])MC <- LSD.test(variance, "Subgroup", p.adj = "bonferroni")data_sg_GroupB <- MC$groupsdata_sg_GroupB$Subgroup <- rownames(data_sg_GroupB)data_sg_GroupB$Group <- "GroupB"# GroupCvariance <- aov(Value ~ Subgroup, data = df[df$Group == "GroupC", ])MC <- LSD.test(variance, "Subgroup", p.adj = "bonferroni")data_sg_GroupC <- MC$groupsdata_sg_GroupC$Subgroup <- rownames(data_sg_GroupC)data_sg_GroupC$Group <- "GroupC"# GroupDvariance <- aov(Value ~ Subgroup, data = df[df$Group == "GroupD", ])MC <- LSD.test(variance, "Subgroup", p.adj = "bonferroni")data_sg_GroupD <- MC$groupsdata_sg_GroupD$Subgroup <- rownames(data_sg_GroupD)data_sg_GroupD$Group <- "GroupD"# 整合所有结果data_sg <- rbind(data_sg_GroupA, data_sg_GroupB, data_sg_GroupC, data_sg_GroupD)# 将显著性结果合并到绘图数据中data_plot <- merge(df_summary, data_sg[-1], by = c("Group", "Subgroup"))
三、常规绘图方案及其问题
3.1 常规方案(不推荐)
ggplot(df_summary, aes(x = Group, y = Mean, fill = Subgroup)) +geom_errorbar(aes(ymin = Mean - SD, ymax = Mean + SD),position = position_dodge2(preserve = "single", width = 0.7)) +geom_col(position = position_dodge2(preserve = "single", width = 0.7)) +labs(title = "非平衡分组柱状图",x = "Group",y = "Value",fill = "Subgroup")
问题:虽然 position_dodge2(preserve = "single") 可以处理非平衡数据,但误差棒的宽度必须与柱子宽度一致,否则会出现误差棒与柱状图不匹配的现象。
解决方案:导出矢量图后,在AI或PS中手动缩短误差棒宽度。
四、进阶方案:借助数值列辅助绘图(推荐)
4.1 核心思路
为每个柱子分配一个数值型 x 坐标,而不是使用分类变量。这样就能完全控制每个柱子的位置和间距:
# 为每个柱子分配 x 坐标# 大组之间留0.5的间距data_plot$x <- c(1:2, 3.5:5.5, 7:8, 9.5:12.5)
坐标分配逻辑:
GroupA(2个子组):x = 1, 2
GroupB(3个子组):x = 3.5, 4.5, 5.5(与GroupA间隔0.5)
GroupC(2个子组):x = 7, 8(与GroupB间隔0.5)
GroupD(4个子组):x = 9.5, 10.5, 11.5, 12.5(与GroupC间隔0.5)
4.2 完整绘图代码
ggplot(data_plot, aes(x = x, y = Mean, fill = Subgroup)) +# 误差棒geom_errorbar(aes(ymin = Mean - SD, ymax = Mean + SD, color = Subgroup),width = 0.2, show.legend = FALSE) +# 柱子geom_col(width = 0.85) +# 坐标轴标签labs(x = NULL,y = "Value/mg",fill = NULL) +# 添加显著性字母标签geom_text(aes(y = Mean + SD + 5, label = groups, color = Subgroup),size = 4.5, show.legend = FALSE) +# x轴设置scale_x_continuous(expand = c(0, 0.2),breaks = c(1.5, 4.5, 7, 11), # 每个大组的中心位置labels = c("GroupA", "GroupB", "GroupC", "GroupD")) +# y轴设置scale_y_continuous(expand = c(0, 0),limits = c(0, 140),breaks = seq(0, 140, 30),guide = guide_axis(minor.ticks = TRUE)) +# 填充颜色scale_fill_manual(values = c("#db4a33", "#44b2ca", "#1c9a83", "#375184"),guide = guide_legend(keywidth = 2, keyheight = 1, order = 1)) +# 线条颜色scale_color_manual(values = c("#db4a33", "#44b2ca", "#1c9a83", "#375184")) +# 主题设置theme_bw() +theme(panel.grid = element_blank(),axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),legend.position = c(0.06, 0.85),legend.background = element_blank())

4.3 参数详解
data_plot$x | ||
geom_errorbar(width = 0.2) | ||
geom_col(width = 0.85) | ||
scale_x_continuous(breaks = ...) | ||
scale_y_continuous(expand = c(0,0)) | ||
guide_axis(minor.ticks = TRUE) | ||
legend.position = c(0.06, 0.85) |
五、图形解读
最终得到的图形具备以下特点:
柱子宽度一致:无论每个大组有几个子组,柱子宽度都相同
误差棒精准匹配:误差棒宽度独立设置,不会与柱子错位
组间距均匀:大组之间保持固定间距
显著性字母清晰:每个柱子顶部的字母标签指示组间差异
坐标轴标签准确:x轴刻度位于每个大组的中心位置
公众号:科研后花园原创教程,转载请注明出处
PS: 以上内容是小编个人学习代码笔记分享,仅供参考学习,欢迎大家一起交流学习。






「R绘图模板」Mantel test 进阶玩法2:多组变量与物种关系可视化(附R代码)!!!

「R绘图模板」Mantel test 进阶玩法:多组学整合分析可视化(附R代码)!!!

「R绘图模板」正三角相关性热图+聚类树:一张图看懂变量间的“朋友圈”!!!

「R绘图模板」存在-缺失热图:一张图看懂数据的“有”和“无”!!!

「R绘图模板」中性群落模型:解密微生物群落构建的“随机密码”!!!

「R绘图模板」RDA&dbRDA&CCA可视化!!!

「R绘图模板」Communications Earth & Environment | 组合图系列—曼哈顿图+Venn图展示富集OTU情况!!!

「R绘图模板」Science Advances | 组合图-并列柱状图+饼图+折线图!!!

「R绘图模板」J. Agric. Food Chem. | 分面组合图-字母标记显著性柱状图+传统显著性标记箱线图!!!

「R绘图模板」主成分分析(PCA)+各类型边缘图!!!

「R绘图模板」Field Crops Res. | 组合图系列—三元图+条形图展示微生物丰度信息!!!

「R绘图模板」Nat. Commun | 复杂tree注释—分支颜色+多层离散热图+分组条形图!!!

「R绘图模板」多色系热图+分组气泡图!!!

「R绘图模板」配对连线图+均值点及连线+显著性+嵌套分组!!!


























