在 R 与 SciPy 中拟合对数正态分布

机器算法验证 r Python 麻木的 scipy
2022-03-11 13:15:39

我已经使用 R 和一组数据拟合了一个对数正态模型。结果参数是:

meanlog = 4.2991610 
sdlog = 0.5511349

我想将此模型转移到我以前从未使用过的 Scipy。使用 Scipy,我能够得到 1 和 3.1626716539637488e+90 的形状和比例——非常不同的数字。我也尝试使用 meanlog 和 sdlog 的 exp,但继续得到奇怪的图表。

我已经阅读了所有关于 scipy 的文档,但仍然对形状和比例参数在这种情况下的含义感到困惑。自己编写函数是否有意义?不过,这似乎很容易出错,因为我是 scipy 的新手。

SCIPY 对数正态(蓝色)与 R 对数正态(红色): Scipy 对数正态(蓝色)与 R 对数正态(红色)

关于采取什么方向的任何想法?顺便说一句,这些数据非常适合 R 模型,所以如果它看起来像 Python 中的其他东西,请随时分享。

谢谢!

更新:

我正在运行 Scipy 0.11

这是数据的一个子集。实际样本为 38k+,平均值为 81.53627:

子集:

x
[60, 170, 137, 138, 81, 140, 78, 46, 1, 168, 138, 148, 145, 35, 82, 126, 66, 147, 88, 106, 80, 54, 83, 13, 102, 54, 134, 34]
numpy.mean(x)
99.071428571428569

或者:

我正在研究捕获pdf的功能:

def lognoral(x, mu, sigma):
    a = 1 / (x * sigma * numpy.sqrt(2 * numpy.pi) )
    b = - (numpy.log(x) - mu) ^ 2 / (2 * sigma ^ 2)
    p = a * numpy.exp(b)
    return p

但是,这给了我以下数字(我尝试了几个,以防我混淆了 sdlog 和 meanlog 的含义):

>>> lognormal(54,4.2991610, 0.5511349)
0.6994656085799437
 >>> lognormal(54,numpy.exp(4.2991610), 0.5511349)
0.9846125119455129
>>> lognormal(54,numpy.exp(4.2991610), numpy.exp(0.5511349))
0.9302407837304372

有什么想法吗?

更新:

重新运行“UPQuark”的建议:

形状、位置、比例(1.0、50.03445923295007、19.074457156766517)

然而,图表的形状非常相似,峰值出现在 21 附近。

4个回答

我努力通过源代码,对 scipy lognormal 例程进行了以下解释。

xlocscaleLognormal(σ)

其中是“形状”参数。 σ

scipy参数和R参数等价如下:

loc - 没有等价物,它会从您的数据中减去,因此 0 成为数据范围的下确界。

scale -,其中是变量对数的平均值。(拟合时,通常您会使用数据对数的样本均值。)expμμ

shape - 变量对数的标准偏差。

我分别调用lognorm.pdf(x, 0.55, 0, numpy.exp(4.29))了参数为 (x, shape, loc, scale) 的位置,并生成了以下值:

xpdf

10 0.000106

20 0.002275

30 0.006552

40 0.009979

50 0.114557

60 0.113479

70 0.103327

80 0.008941

90 0.007494

100 0.006155

这似乎与您的 R 曲线非常匹配。

SciPy 中的对数正态分布适合 SciPy 中所有分布的通用框架。它们都有一个 scale 和 location 关键字(如果没有明确提供,则默认为 0 和 1)。这使得所有分布都可以从它们的归一化规范转移和缩放,这对分布的统计有明显的影响。分布通常也有一个或多个“形状”参数(尽管有些,如正态分布,不需要任何额外的参数)。

虽然这种通用方法很好地统一了所有分布,但对于对数正态,由于其他包定义参数的方式,它可能会造成一些混乱。尽管如此,如果您的意思是 log(基础分布的平均值)和 sdlog(基础分布的标准差),匹配任何对数正态分布仍然非常简单。

首先,确保将 location 参数设置为 0。然后,将 shape 参数设置为 sdlog 的值。最后,将 scale 参数设置为 math.exp(meanlog)。因此, rv = scipy.stats.lognorm(0.5511349, scale=math.exp(4.2991610)) 将创建一个分布对象,其 pdf 与您的 R 生成曲线完全匹配。作为 x = numpy.linspace(0,180,1000); plot(x, rv.pdf(x)) 将验证。

基本上,SciPy 对数正态分布是标准对数正态分布的推广,它在将位置参数设置为 0 时与标准完全匹配。

使用 .fit 方法拟合数据时,您还可以使用关键字 f0..fn、floc 和 fshape 来固定任何形状、位置和/或比例参数,并且仅适合其他变量。对于对数正态分布,这非常有用,因为通常您知道位置参数应该固定为 0。因此, scipy.stats.lognorm.fit(dataset, floc=0) 将始终将位置参数返回为 0,并且只会改变另一个形状和比例参数。

Scipy 对数正态拟合返回形状、位置和比例。我刚刚在一组样本价格数据上运行了以下命令:

shape, loc, scale = st.lognorm.fit(d_in["price"])

这给了我合理的估计值 1.0、0.09、0.86,当你绘制它时,你应该考虑所有三个参数。

形状参数是基础正态分布的标准偏差,尺度是正态平均值的指数。

希望这可以帮助。

似乎 Scipy 中的对数正态分布与 R 中的分布不同,或者通常与我熟悉的分布不同。约翰 D 库克谈到了这一点: http: //www.johndcook.com/blog/2010/02/03/statistical-distributions-in-scipy/ http://www.johndcook.com/distributions_scipy.html

但是,我还没有找到任何关于如何在 Python 中使用对数正态密度函数的结论。如果有人想对此进行补充,请随时。

到目前为止,我的解决方案是使用在 0 到 180(不包括)评估的对数正态 pdf,并在 python 脚本中用作字典。