ggplot2 中如何让均值和标准差文本靠近各箱线图的最小值?
数据小许
结论
将 mean_sd() 返回的纵坐标设为 min(y),标签就会根据各组的最小值定位。箱线图和文本使用相同的 position_dodge(),即可在水平方向上对齐。
如果希望文本位于最小值下方,可以设置 vjust = 1.1,并增加纵轴底部留白。
为什么所有文本都在同一高度?
原函数通过下面两处设置标签位置:
bottom_limit = max(df$Score) * 1.15
y = 0.95 * bottom_limit
这里使用的是外部对象 df$Score 的最大值。即使 stat_summary() 按组调用函数,每次得到的标签纵坐标仍然相同。
函数接收的 y 是当前分组的 Score 数据。原代码用它计算了均值和标准差,却没有用它确定文本位置。
示例中的数据对象名为 data.boxplot,函数引用的却是 df。如果没有单独定义 df,代码会报错;如果定义了,标签位置还可能受到另一份数据的影响。
修改代码
在创建好问题中的 data.boxplot 后,运行:
library(ggplot2)
mean_sd <- function(y) {
data.frame(
y = min(y),
label = paste0(
"M = ", round(mean(y), 2),
"\nSD = ", round(sd(y), 2)
)
)
}
dodge <- position_dodge(width = 0.8)
ggplot(
data.boxplot,
aes(
x = Component,
y = Score,
fill = Label,
group = interaction(Component, Label)
)
) +
geom_boxplot(
width = 0.7,
position = dodge,
outlier.shape = NA
) +
scale_fill_manual(values = c("#FF5F1F", "#1FC0FF")) +
stat_summary(
fun.data = mean_sd,
geom = "text",
position = dodge,
vjust = 1.1
) +
scale_y_continuous(
expand = expansion(mult = c(0.18, 0.05))
) +
theme_classic()
group = interaction(Component, Label) 明确指定分别计算每个组件中各组的统计量。因此,mean_sd() 会为每个箱线图返回对应的最小值、均值和标准差。
两个图层共用 dodge,使统计文本与对应箱线图的横向中心对齐。vjust = 1.1 将文本放在纵坐标锚点下方,纵轴底部的额外留白则为两行文本提供空间。
最小值与下须端点的区别
min(y) 是该组原始数据的最小值,箱线图的下须端点不一定等于这个值。存在低端离群点时,下须通常会高于最小值。
上面的代码按照问题要求,把标签定位在原始数据最小值附近。outlier.shape = NA 只隐藏离群点符号,计算标签时仍会使用这些数据。如果某组有很低的离群点,标签也会随之下移。
如果实际想让文本靠近下须端点,就需要根据箱线图的统计规则计算该端点,不能直接用 min(y) 代替。
调整时需要注意什么?
vjust调整文本相对锚点的位置,不会改变统计值。如果需要明确的数据单位间距,可以将函数中的坐标改成y = min(y) - 3;其中3是示例间距,可按数据尺度调整。- 底部留白
0.18是起始设置。字体大小和图形高度都会影响所需空间,文本被裁切时可以适当增大。 - 示例数据没有缺失值。实际数据含有缺失值时,应明确清理方式,并对均值、标准差和最小值采用一致的处理规则。只有一个有效观测值的分组无法得到通常意义上的样本标准差。
备注:内容仅供参考。
