R语言绘制对数正态密度曲线高度不符计算值且随x轴范围变化的问题
对数正态分布PDF绘图时峰值与手动计算值不符问题
问题描述
- 已确认分布参数:对数正态分布的对数均值为-0.4,对数标准差为2.5
- 手动验算结果:通过对数正态概率密度函数(PDF)、R内置的
dlnorm函数计算,当x = 0.001时,密度计算结果为5.389517:dlnorm(0.001, -0.4, 2.5) # 输出:5.389517 - 第一种绘图情况:使用R基础绘图的
curve函数,设置输入范围0到6时,呈现的密度曲线峰值仅略高于1.5,对应代码如下:curve(dlnorm(x, -.4, 2.5), xlim = c(0, 6), ylim = c(0, 6)) - 第二种绘图情况:将输入范围调整为0到1时,绘制出的曲线峰值接近4,对应代码如下:
curve(dlnorm(x, -.4, 2.5), xlim = c(0, 1), ylim = c(0, 6)) - 复现验证:使用
ggplot2绘制时存在完全相同的问题,对应代码如下:library(ggplot2) # x范围0-6的绘图 ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + stat_function(fun = function(x) dlnorm(x, -0.4, 2.5)) + xlim(0, 6) + ylim(0, 6) # x范围0-1的绘图 ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + stat_function(fun = function(x) dlnorm(x, -0.4, 2.5)) + xlim(0, 1) + ylim(0, 6) - 已验证绘制普通正态密度曲线时不会出现这类问题
- 核心疑问:为何调整x轴刻度范围时,对数正态密度的绘制高度会发生变化?且上述两种绘制方式都无法呈现
dlnorm计算得到的正确峰值高度?
原因与解决方案
核心原因1:该分布的理论峰值位置极度靠近x轴原点
对数正态分布PDF的极值点(峰值位置)可通过求导推导得到,公式为:
x_peak = exp(μ_log - σ_log²)
其中μ_log为对数均值,σ_log为对数标准差。代入本次的参数μ_log=-0.4,σ_log=2.5,计算得:
x_peak = exp(-0.4 - 2.5²) = exp(-6.65) ≈ 0.0013
也就是说,整个分布的全局峰值落在x≈0.0013的位置,和手动验算的x=0.001处的密度值位置完全重合。
核心原因2:默认绘图函数的采样步长过大,直接跳过了峰值所在的近0区域
R基础绘图的curve函数、ggplot2的stat_function默认仅生成101个等距分布的x采样点计算y值并连线:
- 当x范围设为0到6时,采样步长为
6/(101-1) = 0.06,所有采样点的序列为0, 0.06, 0.12, ..., 6,完全跳过了x在0到0.06之间的区域——这正是全局峰值所在的区间。此时看到的“仅略高于1.5”的峰值,是x>0.06区域内的局部密度最大值,和真正的全局峰值无关。 - 当x范围设为0到1时,采样步长为
1/(101-1)=0.01,采样点序列为0, 0.01, 0.02, ..., 1,依然跳过了x在0到0.01之间的区域(峰值就在x≈0.0013,刚好落在这个被跳过的区间里)。此时看到的“接近4”的峰值,是x=0.01位置对应的密度值(计算可得dlnorm(0.01, -0.4, 2.5)≈3.88),依然不是全局最大值。
注:普通正态分布不存在该问题,是因为正态分布的峰值位置通常和x范围的中心重合,等距采样必然会覆盖到峰值附近的点,不会出现整段区间被跳过的情况。
修正方法
要完整绘制出包含正确峰值的曲线,只需要大幅提升采样密度,缩小采样步长即可:
- 基础绘图修改示例:将
curve的采样点数从默认101提升到10000,步长会缩小到原来的1%,足以覆盖到x=0.001附近的峰值:curve(dlnorm(x, -.4, 2.5), xlim = c(0, 6), ylim = c(0, 6), n = 10000) - ggplot2修改示例:为
stat_function设置更大的采样点数:ggplot(data = data.frame(x = 0), mapping = aes(x = x)) + stat_function(fun = function(x) dlnorm(x, -0.4, 2.5), n = 10000) + xlim(0, 6) + ylim(0, 6)
修改后绘制的曲线会完整呈现出x≈0.0013位置、高度约5.39的峰值,和dlnorm的手动计算值完全一致。
内容的提问来源于stack exchange,提问作者LMc
相关产品推荐
相关产品推荐

