在ggplot2中添加基于相对频率的透明直方图轮廓
在ggplot2中绘制无内部竖线的相对频率直方图轮廓
问题背景
需要对比两个分布:一个用填充直方图展示,另一个用无内部竖线的直方图轮廓展示,且均为相对频率直方图(y轴使用density * width)。尝试多种方法均存在问题:
geom_histogram(fill=NA)会生成内部竖线,干扰可读性geom_freqpoly生成锯齿状线条,不符合平条形轮廓的需求stat_bin(geom="step")端点不与y=0连接,且两端不对齐x=0/x=1边界geom_density过度平滑丢失分布细节,边界不对齐,且密度与相对频率的匹配逻辑不清晰
解决方案
方法1:叠加直方图(简单高效)
通过叠加两层geom_histogram实现:先绘制白色填充的直方图(覆盖内部竖线),再绘制仅带黑色边框的直方图,保留外层轮廓。
library(ggplot2) library(ggpubr) set.seed(22) # 创建模拟数据 flavor <- data.frame( color = sample(c('red', 'blue', 'green'), 1500, TRUE), flavor = sample.int(n = 100, size = 1500, replace = TRUE) / 100 ) flavor2 <- data.frame( color = sample(c('red', 'blue', 'green'), 1500, TRUE), flavor = (sample.int(n = 100, size = 1500, replace = TRUE) / 100) ^ 2 ) # 绘图代码 ggplot() + # 第一个分布:填充直方图 geom_histogram(data = flavor, aes(x = flavor, y = after_stat(density * width)), bins = 34, alpha = 0.5, fill = "#619CFF") + # 先绘制白色填充的直方图,覆盖内部竖线 geom_histogram(data = flavor2, aes(x = flavor, y = after_stat(density * width)), bins = 34, fill = "white", color = NA) + # 再绘制仅带边框的直方图,保留轮廓 geom_histogram(data = flavor2, aes(x = flavor, y = after_stat(density * width)), bins = 34, fill = NA, color = "black", size = 0.7) + facet_wrap(~color) + labs(y = "相对频率", x = "Flavor Value") + theme_bw()
方法2:手动绘制轮廓(更灵活)
先提取分箱统计数据,再用geom_polygon绘制完整的直方图轮廓,确保边界对齐x=0/x=1,且端点与y=0连接。
library(ggplot2) library(dplyr) # 复用上面的模拟数据... # 定义函数提取分箱统计数据 get_bin_stats <- function(data, var, bins = 34) { data %>% group_by(color) %>% group_map(function(.df, .grp) { # 用stat_bin计算分箱数据 p <- ggplot(.df, aes(x = {{var}})) + stat_bin(bins = bins, aes(y = after_stat(density * width)), geom = "blank") layer_data(p) %>% mutate(color = .grp$color) %>% select(xmin, xmax, y, color) }) %>% bind_rows() } # 获取flavor2的分箱数据 flavor2_bins <- get_bin_stats(flavor2, flavor, bins = 34) # 绘图 ggplot() + # 填充直方图 geom_histogram(data = flavor, aes(x = flavor, y = after_stat(density * width)), bins = 34, alpha = 0.5, fill = "#619CFF") + # 用geom_polygon绘制轮廓:连接分箱左右端点+底部0点 geom_polygon(data = flavor2_bins, aes(x = c(xmin, rev(xmax)), y = c(y, rep(0, n()))), color = "black", fill = NA, group = color) + facet_wrap(~color) + labs(y = "相对频率", x = "Flavor Value") + theme_bw()
效果说明
两种方法都能生成无内部竖线的直方图轮廓,且保持相对频率的正确性:
- 方法1操作简单,无需额外数据处理,适合快速绘图
- 方法2更灵活,可自定义轮廓的连接方式,适合需要调整细节的场景
内容的提问来源于stack exchange,提问作者Brandon
相关产品推荐
相关产品推荐

