Python 对数正态概率图

3

我想在对数正态概率图上绘制数据的累积分布函数,如下所示:

enter image description here

我希望我的图表坐标轴的刻度看起来像这样,只是翻转过来(概率在x轴上)。请注意,上面的y轴不仅仅是对数刻度。此外,我不确定为什么上面的x轴重复了1-9而不是到10-99等等,但请忽略这一部分。
这是我目前的情况。我正在使用制作CDF的方法,如此处所述。
mu, sigma = 3., 1. # mean and standard deviation
data = np.random.lognormal(mu, sigma, 1000)

#Make CDF
dataSorted = np.sort(data)
dataCdf = np.linspace(0,1,len(dataSorted))

plt.plot(dataCdf, dataSorted)
plt.gca().set_yscale('log')
plt.xlabel('probability')
plt.ylabel('value')

enter image description here

现在我只需要一种方法来使我的x轴像上面图片中的y轴一样进行缩放。

1
从你现有的代码中,难道不很明显如何将x轴设置为对数坐标吗?plt.gca().set_yscale('log') -> plt.gca().set_xscale('log') - Chris Mueller
x轴比例尺(或示例坐标轴中的y轴比例尺)不是对数比例尺。我为了更清晰地表达,改变了示例坐标轴图像。“中间”概率值彼此接近,而大/小概率值则相距较远。就像它在0.5之前是对数比例尺,在0.5到1之间是“反向”的对数比例尺。 - hm8
2个回答

2
一种解决此问题的方法是使用对称对数刻度,称为 symlog
Symlog 是一种对数图,它在0附近的某个范围内呈线性行为(普通对数图将显示无限多个十年),使得可能出现穿过0的对数图。
可以使用 ax.set_xscale('symlog', linthreshx=0.1) 在 matplotlib 中设置 Symlog,其中 linthreshx 表示零点周围的线性范围。
由于在这种情况下我们希望图形的中心在0.5而不是0处,因此我们实际上可以绘制两个图形并将它们粘合在一起。 为了获得所需的结果,可以调整要显示的刻度线以及 linthreshx 参数。 以下是一个示例。
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker
mu, sigma = 3., 1. # mean and standard deviation
data = np.random.lognormal(mu, sigma, 1000)

#Make CDF
dataSorted = np.sort(data)
dataCdf = np.linspace(0,1,len(dataSorted))

fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True)
plt.subplots_adjust(wspace=0.00005)
ax1.plot(dataCdf[:len(dataCdf)/2], dataSorted[:len(dataCdf)/2])
ax2.plot(dataCdf[len(dataCdf)/2:]-1, dataSorted[len(dataCdf)/2:])

ax1.set_yscale('log')
ax2.set_yscale('log')

ax1.set_xscale('symlog', linthreshx=0.001)
ax2.set_xscale('symlog', linthreshx=0.001)

ax1.set_xlim([0.01, 0.5])
ax2.set_xlim([-0.5, -0.01])

ticks = np.array([0.01,0.1,  0.3])
ticks2 = ((1-ticks)[::-1])-1
ax1.set_xticks(ticks)
ax1.xaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax2.set_xticks(ticks2)
ax2.xaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax2.set_xticklabels(ticks2+1)

ax1.spines["right"].set_visible(False)
ax2.spines["left"].set_visible(False)
ax1.yaxis.set_ticks_position('left')
ax2.yaxis.set_ticks_position('right')

ax1.set_xlabel('probability')
ax1.set_ylabel('value')

plt.savefig(__file__+".png")
plt.show()

enter image description here


事实证明,这实际上不是我要寻找的内容。 在图表上,对数正态分布应该呈现为一条完美的直线。 我对“到0.5为对数,从0.5到1为“反比例”的解释可能是不正确的。 - hm8

0

我知道有点晚了,但我遇到了类似的问题并解决了它,所以我想分享一下解决方案,可以参考matplotlib文档中的custom scale example:

import numpy as np
import scipy.stats as stats
from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms
from matplotlib.ticker import Formatter, FixedLocator

class PPFScale(mscale.ScaleBase):
    name = 'ppf'

    def __init__(self, axis, **kwargs):
        mscale.ScaleBase.__init__(self)

    def get_transform(self):
        return self.PPFTransform()

    def set_default_locators_and_formatters(self, axis):
        class VarFormatter(Formatter):
            def __call__(self, x, pos=None):
                return f'{x}'[1:]

        axis.set_major_locator(FixedLocator(np.array([.001,.01,.1,.2,.3,.4,.5,.6,.7,.8,.9,.99,.999])))
        axis.set_major_formatter(VarFormatter())


    def limit_range_for_scale(self, vmin, vmax, minpos):
        return max(vmin, 1e-6), min(vmax, 1-1e-6)

    class PPFTransform(mtransforms.Transform):
        input_dims = output_dims = 1

        def ___init__(self, thresh):
            mtransforms.Transform.__init__(self)

        def transform_non_affine(self, a):
            return stats.norm.ppf(a)

        def inverted(self):
            return PPFScale.IPPFTransform()

    class IPPFTransform(mtransforms.Transform):
        input_dims = output_dims = 1

        def transform_non_affine(self, a):
            return stats.norm.cdf(a)

        def inverted(self):
            return PPFScale.PPFTransform()

mscale.register_scale(PPFScale)


if __name__ == '__main__':
    import matplotlib.pyplot as plt
    mu, sigma = 3., 1. # mean and standard deviation
    data = np.random.lognormal(mu, sigma, 10000)

    #Make CDF
    dataSorted = np.sort(data)
    dataCdf = np.linspace(0,1,len(dataSorted))

    plt.plot(dataCdf, dataSorted)
    plt.gca().set_xscale('ppf')
    plt.gca().set_yscale('log')
    plt.xlabel('probability')
    plt.ylabel('value')
    plt.xlim(0.001,0.999)
    plt.grid()
    plt.show()

output[2]

您可能也想看一下我的对数正态分布演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接