如何在 Python 中创建对数正态分布?

How do you create a logit-normal distribution in Python?

this post 之后,我尝试通过创建 LogitNormal class:

来创建对数正态分布
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import logit
from scipy.stats import norm, rv_continuous


class LogitNormal(rv_continuous):
    def _pdf(self, x, **kwargs):
        return norm.pdf(logit(x), **kwargs)/(x*(1-x))


class OtherLogitNormal:
    def pdf(self, x, **kwargs):
        return norm.pdf(logit(x), **kwargs)/(x*(1-x))


fig, ax = plt.subplots()
values = np.linspace(10e-10, 1-10e-10, 1000)
sigma, mu = 1.78, 0
ax.plot(
    values, LogitNormal().pdf(values, loc=mu, scale=sigma), label='subclassed'
)
ax.plot(
    values, OtherLogitNormal().pdf(values, loc=mu, scale=sigma),
    label='not subclassed'
)
ax.legend()
fig.show()

但是,LogitNormal class 没有产生预期的结果。当我不使用 subclass rv_continuous 时,它会起作用。这是为什么?我需要 subclassing 才能工作,因为我还需要它附带的其他方法,例如 rvs

顺便说一句,我在 Python 中创建自己的对数正态分布的唯一原因是因为我能找到的该分布的唯一实现来自 PyMC3 package and from the TensorFlow package,两者都是如果您只需要它们来实现一个功能,那么它会很重/矫枉过正。我已经尝试过 PyMC3,但显然它与 scipy 的效果不佳,我认为它总是崩溃。但那是完全不同的故事。

如果您查看 source code of the pdf method,您会注意到调用 _pdf 时没有 scaleloc 关键字参数。

   if np.any(cond):
        goodargs = argsreduce(cond, *((x,)+args+(scale,)))
        scale, goodargs = goodargs[-1], goodargs[:-1]
        place(output, cond, self._pdf(*goodargs) / scale)

这导致您覆盖的 _pdf 方法中的 kwargs 始终是一个空字典。

如果仔细观察代码,您还会注意到缩放和位置由 pdf 处理,而不是 _pdf

在您的情况下,_pdf 方法调用 norm.pdf,因此 locscale 参数必须以某种方式在 LogitNormal._pdf.[=30 中可用=]

例如,您可以在创建 LogitNormal 的实例时传递 scaleloc,并将值存储为 class 属性:

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import logit
from scipy.stats import norm, rv_continuous


class LogitNormal(rv_continuous):
    def __init__(self, scale=1, loc=0):
        super().__init__(self)
        self.scale = scale
        self.loc = loc

    def _pdf(self, x):
        return norm.pdf(logit(x), loc=self.loc, scale=self.scale)/(x*(1-x))


fig, ax = plt.subplots()
values = np.linspace(10e-10, 1-10e-10, 1000)
sigma, mu = 1.78, 0
ax.plot(
    values, LogitNormal(scale=sigma, loc=mu).pdf(values), label='subclassed'
)
ax.legend()
fig.show()