使用geom_density绘制的密度图为何未触达X轴?
问题:密度图曲线超出x=1范围的修复方案
我正在绘制国会代表配对投票一致率的概率密度图,对比同党派与跨党派配对的投票一致率分布。为每位国会议员创建配对,追踪所有唱名投票并计算每对的一致率(范围0-1,1代表全一致)。但部分国会的数据绘图时,右侧曲线未触达X轴,甚至超出x=1的范围。
原代码:
h117$pdplot <- ggplot(data = h117$pairs_votes_proportions %>% filter(proportion_of_agreements < 1 & proportion_of_agreements > 0.00), aes( x = proportion_of_agreements, fill = pair_type)) + geom_density(adjust = 2, alpha = 0.4, size = 0.7) + scale_fill_grey(start = 0.1, end = 0.8) + labs(title = paste("House of Representatives 117;",length(unique(h117$votes$rollnumber)),"roll calls"), x = "Proportion of Agreements", y = "Density") + theme_minimal() + theme(legend.position = "none", plot.title = element_text(hjust = 0.5, face = "bold")) + xlim(0, 1)
问题原因
曲线超出x=1范围的核心原因是密度估计的平滑特性:geom_density默认采用高斯核做平滑拟合,当大量数据集中在接近1的区间时,算法会自然外推到x=1以外的区域。而xlim(0,1)只是生硬截断了显示范围,并没有调整密度计算的边界,导致曲线在截断处无法触达X轴。
修复方案
要彻底解决这个问题,需要同时限制密度计算的取值范围,并确保绘图边界贴合数据区间。修改后的代码如下:
h117$pdplot <- ggplot(data = h117$pairs_votes_proportions %>% filter(proportion_of_agreements < 1 & proportion_of_agreements > 0.00), aes( x = proportion_of_agreements, fill = pair_type)) + # 用stat_density替代geom_density,强制截断外推部分 stat_density( adjust = 2, alpha = 0.4, size = 0.7, trim = TRUE, # 仅在数据存在的范围内计算密度 geom = "density", # 双重保险:把x值强制限制在0-1区间内 aes(x = pmax(pmin(proportion_of_agreements, 1), 0)) ) + scale_fill_grey(start = 0.1, end = 0.8) + labs(title = paste("House of Representatives 117;",length(unique(h117$votes$rollnumber)),"roll calls"), x = "Proportion of Agreements", y = "Density") + theme_minimal() + theme(legend.position = "none", plot.title = element_text(hjust = 0.5, face = "bold")) + # 用scale_x_continuous替代xlim,让轴边界完全贴合0-1 scale_x_continuous(limits = c(0, 1), expand = c(0, 0))
关键修改点
trim = TRUE:强制密度曲线只在实际数据覆盖的区间绘制,禁止算法外推到数据范围之外pmax(pmin(proportion_of_agreements, 1), 0):确保所有x值被严格限制在0-1之间,避免极端值干扰拟合scale_x_continuous(limits = c(0,1), expand = c(0,0)):替代xlim,让X轴的起始/结束位置完全对齐0和1,曲线能完整贴合X轴边界,不会被截断
内容的提问来源于stack exchange,提问作者Cosmic Outlaw
相关产品推荐
相关产品推荐

