如何用ggplot2制作按类别着色且基于整体密度的密度直方图
解决方案:基于整体数据计算密度的分组着色直方图
要让直方图的密度基于整个数据集而非分组后的数据,你需要手动计算密度(替代默认按组计算的density统计量),确保分母使用全局总样本数而非组内样本数。结合stage()函数可以清晰实现这一点,它允许你在统计变换前后分别指定变量逻辑:
library(ggplot2) # 数据预处理 d <- diamonds priceBreaks <- c(0,5000,10000,20000) d$priceCat <- cut(diamonds$price, breaks = priceBreaks, labels = c('low','med','high')) # 生成目标直方图 ggplot(d) + geom_histogram( aes( x = price, # 使用stage:原始x用于分箱,统计后用全局总样本数计算密度 y = stage( x = price, after_stat = count / (nrow(d) * binwidth) ), fill = priceCat ), breaks = seq(0,20000,by=1000), color = "white" # 可选:添加组间分隔线,提升可读性 ) + labs(y = "Density")
原理说明
- 默认的
after_stat(density)会按fill分组计算:每个组的密度 = 组内bin计数 / (组内样本数 × 组距),导致各组密度之和大于1。 - 手动计算时,用
nrow(d)(整个数据集的总样本数)替代组内样本数,得到的是整体数据的密度:每个bin的密度 = bin计数 / (全局总样本数 × 组距),所有bin的密度之和为1。
stage()函数的作用
这里stage()的核心是分离"原始数据映射"和"统计后计算"两个阶段:
x = price:指定分箱时使用原始的price变量(基于整个数据集的范围分箱,不受分组影响);after_stat = ...:在统计完成(生成count和binwidth等统计量)后,用全局总样本数计算密度,确保密度基于整体分布。
你也可以不用stage(),直接在after_stat()里写计算式,效果一致:
ggplot(d) + geom_histogram( aes(x = price, y = after_stat(count / (nrow(d) * binwidth)), fill = priceCat), breaks = seq(0,20000,by=1000) )
内容的提问来源于stack exchange,提问作者S. Robinson
相关产品推荐
相关产品推荐

