ggplot2箱线图显示中位数与计算值不符的技术问询
看起来你在处理215万条数据绘制分年份、分性别的SUM_MME_mg箱线图时碰到了棘手的问题:手动分组计算中位数后发现只有最后一年的最后一组中位数与众不同,但ggplot2生成的箱线图却显示所有组中位数完全一致,而且R连个报错或警告都没有,完全找不到问题根源,甚至不确定是箱线图内置计算还是median()函数的结果更准确对吧?
先把你提供的代码和数据样例整理一下,方便排查:
你用到的箱线图代码
# boxplot ggplot(dataset, aes(x=Year, y=SUM_MME_mg, fill=GenderPerson)) + geom_boxplot(outlier.shape = NA)+ ylim(0,850)
手动计算中位数的代码
pivot <- dataset %>% select(SUM_MME_mg,GenderPerson,Year )%>% group_by(Year, GenderPerson) %>% summarise(MedianValues = median(SUM_MME_mg,na.rm=TRUE))
数据样例
> dput(head(dataset[,c(1,7,10)])) structure(list(GenderPerson = c(2L, 1L, 2L, 2L, 2L, 2L), Year = c("2015", "2014", "2013", "2012", "2011", "2015"), SUM_MME_mg = c(416.16, 131.76, 790.56, 878.4, 878.4, 878.4)), row.names = c(NA, 6L), class = "data.frame")
接下来我给你几个最可能的排查方向,你可以逐一试试:
ylim()截断是最大嫌疑!
你设置了ylim(0,850),这个函数会直接删除所有超出y轴范围的数据,包括计算箱线图统计量时的样本!如果大部分组的中位数其实在850以上,被截断后,箱线图会把这些中位数强行显示在850的位置,看起来就全一致了。
解决方法:改用coord_cartesian(ylim = c(0,850)),这个函数只是缩放显示范围,不会删除数据,箱线图的统计计算还是基于完整数据集的。你也可以先去掉ylim()跑一遍图,看看中位数是不是真的一致。检查
Year的变量类型
从数据样例看Year是字符型(比如"2015"),虽然ggplot2能处理,但有时候分组统计会出现隐性的排序或分组错误。你可以把Year转成数值型或因子型再试:# 转数值型 dataset$Year <- as.numeric(dataset$Year) # 或者转因子(保证年份顺序正确) dataset$Year <- factor(dataset$Year, levels = sort(unique(dataset$Year)))直接对比箱线图的计算结果和你的手动结果
geom_boxplot()默认用的stat_boxplot()计算中位数的逻辑和median()是一致的,但你可以直接提取绘图时计算的中位数来对比:# 生成绘图对象并提取统计数据 plot_obj <- ggplot(dataset, aes(x=Year, y=SUM_MME_mg, fill=GenderPerson)) + geom_boxplot(outlier.shape = NA) plot_stats <- ggplot_build(plot_obj)$data[[1]] # 提取分组和中位数信息 plot_medians <- plot_stats %>% select(x, group, middle) %>% mutate(Year = levels(factor(dataset$Year))[x]) # 和你的pivot数据对比 print(plot_medians) print(pivot)这样就能清楚看到是计算结果真的一致,还是只是绘图显示的问题。
检查缺失值和分组完整性
虽然你加了na.rm=TRUE,但还是要确认GenderPerson和Year有没有缺失值,会不会导致分组合并:table(is.na(dataset$GenderPerson)) table(is.na(dataset$Year))另外也可以看一下
pivot的行数是不是等于「年份数×性别数」,确保分组没有出错。
内容的提问来源于stack exchange,提问作者Vesna

