柱状图加显著性星号:规范做法
跟完这篇,您能把一张分组数据画成一张规范的「均值柱 + 误差棒 + 显著性星号」图:柱高和误差棒各表示什么、星号来自哪种检验、怎么用几行代码把星号加上去,以及一个更重要的判断——什么时候这张柱状图根本不该画,该换成箱线图或散点图。
「均值柱配一对星号」是论文里最常见、也最容易画错的一张图。画错通常不在代码,而在没想清楚:误差棒到底代表什么、星号背后是哪种检验、这批数据适不适合用柱子概括。这篇按这三件事一步步走。
先备一张整洁数据表
无论最后画柱、画箱还是画点,起点都是同一种「整洁」格式:每行一个样本,一列放分组,一列放测量值。
| sample | group | value |
|---|---|---|
| s1 | Ctrl | 12.4 |
| s2 | Ctrl | 10.9 |
| s3 | Treat | 18.2 |
| s4 | Treat | 16.7 |
只要是这个形状,后面所有代码都能直接套。若您手上是「一组一列」的宽表,先转成上面这种长表再画。整洁数据的更多细节见 统计方法怎么选。
柱高与误差棒各画什么
一根「均值柱 + 误差棒」其实叠了两层信息:柱子的高度是各组的均值,误差棒是围绕均值的一段波动范围。
library(dplyr)
library(ggplot2)
# df:整洁长表,group 为分组列,value 为测量列
stat <- df %>%
group_by(group) %>%
summarise(
mean = mean(value),
sd = sd(value),
n = n(),
sem = sd / sqrt(n) # 均值标准误 = 标准差 ÷ 根号 n
)
ggplot(stat, aes(group, mean)) +
geom_col(width = 0.6, fill = "grey80") +
geom_errorbar(aes(ymin = mean - sd, ymax = mean + sd), width = 0.2) +
theme_classic()
误差棒可以画三种东西,含义完全不同,别混:
| 误差棒 | 表示什么 | 与样本量 n 的关系 |
|---|---|---|
| SD(标准差) | 数据本身有多散,反映个体间差异 | 与 n 无系统关系,样本再多也不会自动变短 |
| SEM(均值标准误) | 「样本均值」估计总体均值的精度 | = SD ÷ √n,n 越大越短 |
| 95% CI(置信区间) | 对均值的区间估计,约等于均值 ± 2×SEM | n 越大越窄 |
误差棒选 SD、SEM 还是 CI
选哪种误差棒,取决于您想让读者看什么,不取决于哪种画出来好看:
- 想说明「这批数据本身有多散」——用 SD。
- 想说明「我对这个均值估得有多准」——用 SEM 或 95% CI。
- 要做组间比较、让读者据图判断差异——95% CI 通常信息量最大。
一个常见的不诚实做法:为了让误差棒显得短、组间差异显得「更干净」,明明想表达离散却选了 SEM。 SEM 恒比 SD 小(除非 n=1),这样选是拿视觉效果换了信息的真实性。请按「想表达什么」来选,不是按「哪种短」来选。
无论选哪种,图注里都必须写清两件事:误差棒代表的是 SD、SEM 还是 CI,以及每组的 n。少了这两条,一根误差棒是读不懂的。
还有一条要记牢,能避开一大类误读:
误差棒重叠不代表不显著,误差棒不重叠也不代表显著。 是否显著由检验的 p 值决定,不能靠肉眼比误差棒长短。所以「显著性」得单独做检验,也就是下一节的星号。
星号对应哪种检验
星号本身不是一种检验,它只是把某个检验算出来的 p 值换成符号显示。最常用的对应惯例是:
| 符号 | p 值范围 |
|---|---|
ns | p ≥ 0.05(不显著) |
* | p < 0.05 |
** | p < 0.01 |
*** | p < 0.001 |
**** | p < 0.0001 |
这套对应是常用惯例、非铁律。四颗星(p<0.0001)是部分绘图软件的默认,不少期刊只用到三颗星。用几级、界怎么划,请在图注里写明。
关键在于:星号背后的 p 值必须来自和您数据匹配的检验。选错检验,星号画得再多也没意义。按「几组、是否配对、是否近似正态」分岔:
| 场景 | 常用检验 |
|---|---|
| 两组、独立、近似正态 | t 检验(方差不齐用 Welch 版) |
| 两组、配对、近似正态 | 配对 t 检验 |
| 两组、明显偏离正态 | Mann-Whitney U / Wilcoxon 符号秩(配对) |
| 三组及以上、近似正态 | 单因素方差分析(ANOVA)+ 事后两两比较(如 Tukey HSD) |
| 三组及以上、偏离正态 | Kruskal-Wallis + 事后比较(如 Dunn,含校正) |
三组以上别直接两两跑 t 检验。 每比一次都有一次犯假阳性的机会,比得越多假阳性越多。规范做法是先做 ANOVA(或 Kruskal-Wallis)这类总体检验,再做自带多重比较校正的事后检验。原理见 多重检验校正怎么选。检验怎么挑,见 t 检验还是非参数检验。
把星号加到图上
代码层面,ggpubr 能把检验和标注一步做完:stat_compare_means() 负责跑检验、画括号、标星号。
library(ggpubr)
# 指定要比较的组对;两组时只列一对即可
my_comparisons <- list(c("Ctrl", "Treat"))
ggplot(df, aes(group, value)) +
stat_summary(fun = mean, geom = "col",
width = 0.6, fill = "grey85") + # 柱高=均值
stat_summary(fun.data = mean_se, geom = "errorbar",
width = 0.2) + # 误差棒=均值 ± 标准误
geom_jitter(width = 0.1, size = 1, alpha = 0.6) + # 把每个原始数据点也画出来
stat_compare_means(comparisons = my_comparisons,
method = "t.test", # 两组用 t 检验
label = "p.signif") + # 显示星号而非 p 数值
theme_classic()
几个要点:
label = "p.signif"显示星号;想直接标 p 值改成label = "p.format"。method要和上一节的判断一致:偏离正态改"wilcox.test",三组以上的总体 p 用method = "anova"或"kruskal.test"。stat_compare_means()对您列出的多对比较是各自独立跑的,默认不跨比较校正。 一次列了好几对时,请回到 ANOVA + 事后检验的路子,或自行对 p 值做校正,别把一堆未校正的星号直接堆上去。
注意上面已经用 geom_jitter() 把每个数据点叠在柱子上了——这不是装饰,而是下一节的重点。
什么时候别用柱状图
这是全篇最该记住的一节。柱状图适合表示「计数、比例、频次」这类从 0 起算、有明确总量的数据;用它去概括一批连续测量值,常常会骗人。
问题在于:一根「均值柱 + 误差棒」只压缩出两个数(均值、离散),而很多形状差别很大的数据,画出来的柱子几乎一模一样。同一根柱子背后,可能是均匀分布,也可能是两簇分开的双峰、一个离群点拉高的均值、或两组样本量悬殊。柱子把这些都藏起来了。已有被广泛引用的方法学讨论专门指出这一点:小样本的连续数据,应当把每个原始点都画出来,而不是只画柱和误差棒。
按数据类型选图:
| 数据 / 场景 | 更合适的图 |
|---|---|
| 计数、比例、频次(从 0 起的量) | 柱状图,合适 |
| 连续测量、每组样本不多 | 散点 / 抖动点,把每个点都画出来 |
| 连续测量、样本较多 | 箱线图 / 小提琴图(可叠加点) |
| 想同时看均值与离散 | 点 + 均值线 + 误差棒,别用实心柱 |
一个稳妥的折中,是在柱子(或箱子)上叠加原始点,既保留了熟悉的观感,又让读者看到真实分布——上一节的 geom_jitter() 正是干这个的。样本很小时(比如每组只有三五个),干脆别画柱,直接把点画出来、旁边标一条均值线即可。
常见错误
| 错误 | 后果 | 怎么避免 |
|---|---|---|
| 图注没写误差棒是 SD/SEM/CI | 读者无法判断误差棒含义,图不可读 | 图注写清误差棒类型 + 每组 n |
| 为显得「差异干净」而选 SEM | 用视觉短棒掩盖真实离散 | 按想表达什么选,不按哪种短选 |
| 靠误差棒是否重叠判断显著 | 结论出错 | 显著与否看检验 p 值,不看误差棒 |
| 三组以上直接两两跑 t 检验 | 假阳性膨胀 | 先 ANOVA/Kruskal-Wallis,再做带校正的事后检验 |
| 小样本连续数据硬画柱 | 双峰、离群点、n 不等被藏起来 | 画散点/箱线,或在柱上叠加原始点 |
| 星号和检验对不上(如配对当独立) | p 值本身就错,星号无意义 | 先按设计选对检验,再谈星号 |
在平台上跑一遍
不想写 R 代码也能得到同样规范的图:在 百沐一下 传入一张整洁数据表(每行一个样本、一列分组、一列测量值),用一句话说清「按组画均值柱、误差棒用标准误、两组做 t 检验并标星号」,它会自动核对检验前提、跑出结果、把星号画上去,并附可复现的 R 代码;若数据更适合箱线或散点,它也会提示您换。用法见 智能绘图使用指南。
相关
- t 检验还是非参数检验:怎么判 —— 星号背后那次检验该怎么选
- 统计方法怎么选 —— 几组、是否配对,先在这里定位方法
- 多重检验校正怎么选 —— 三组以上为什么不能直接两两比
- 智能绘图使用指南 —— 在平台上不写代码画同一张图