在ggplot2的geom_histogram中添加dnorm曲线遇问题求助
解决ggplot绘制标准化变量直方图+dnorm曲线的问题
问题1:首次运行曲线过宽
这是因为直方图默认以计数(count)为y轴,而dnorm输出的是密度值,两者尺度差异极大,导致曲线被过度拉伸。解决方法很直接:
- 将直方图的y轴切换为密度,在
geom_histogram()中设置aes(y = ..density..),让其与dnorm的输出尺度匹配,曲线就会恢复正常比例。 - 示例代码:
library(ggplot2) # 先清理缺失值 df_clean <- df[is.finite(df$oes_scale), ] ggplot(data = df_clean, aes(x = oes_scale)) + geom_histogram(aes(y = ..density..), bins = 30, fill = "lightblue", color = "black") + stat_function( fun = dnorm, args = list(mean = mean(df_clean$oes_scale), sd = sd(df_clean$oes_scale)), color = "red", size = 1 )
问题2:相对频率模式下添加stat_function报错
报错原因
你遇到的「density * 0.35」非数值运算问题,核心是直方图用了相对频率(比如y = ..count../sum(..count..)),而stat_function默认输出密度值,两者的y轴尺度不兼容;警告里的6行非有限值,是数据中存在NA/NaN/Inf,会干扰后续运算。
解决步骤
- 先清理数据:提前移除含非有限值的行,从根源避免运算异常:
df_clean <- df[is.finite(df$oes_scale), ]
- 对齐相对频率与dnorm的尺度:相对频率总和为1,密度曲线下面积也为1,但直方图组距会影响相对频率的数值。需要把
dnorm的结果乘以组距,让曲线和相对频率的y轴匹配:- 先确定组距,要么手动指定,要么从直方图中提取:
# 方式1:手动指定组距 bin_width <- diff(range(df_clean$oes_scale)) / 30 # 方式2:从已绘制的直方图中提取实际组距 p_temp <- ggplot(df_clean, aes(x=oes_scale)) + geom_histogram(bins=30) bin_width <- ggplot_build(p_temp)$data[[1]]$width[1]- 再绘制带匹配曲线的直方图:
ggplot(data = df_clean, aes(x = oes_scale)) + geom_histogram(aes(y = ..count../sum(..count..)), binwidth = bin_width, fill = "lightblue", color = "black") + stat_function( fun = function(x) dnorm(x, mean = mean(df_clean$oes_scale), sd = sd(df_clean$oes_scale)) * bin_width, color = "red", size = 1 )
- 简化替代方案:如果不需要严格展示相对频率数值,直接用密度模式(
y=..density..)更省心——密度和相对频率的趋势完全一致,只是数值差一个组距倍数,不影响图形的对比展示,还能避免尺度匹配的麻烦。
内容的提问来源于stack exchange,提问作者hr1211
相关产品推荐
相关产品推荐

