【问题标题】:Python Lognormal Probability PlotPython 对数正态概率图
【发布时间】:2017-01-12 20:48:33
【问题描述】:

我想在对数正态概率图上绘制数据的 cdf,如下所示:

我希望我的绘图上的坐标轴比例看起来像那样,只是翻转(概率在 x 轴上)。请注意,上面的 y 轴不仅仅是一个对数刻度。另外我不确定为什么上面的 x 轴重复 1-9 而不是 10-99 等,但忽略那部分。

这是我目前所拥有的。我正在使用 here 概述的方法制作 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')

现在我只需要一种方法来缩放我的 x 轴,就像上图中的 y 轴一样。

【问题讨论】:

  • 如何从当前代码中使 x 轴对数不是很明显吗? plt.gca().set_yscale('log') -> plt.gca().set_xscale('log')
  • x 尺度(或示例轴中的 y 尺度)不是对数的。为了清楚起见,我更改了示例轴图像。 “中间”概率值彼此接近,大/小概率值相距更远。它就像它的对数高达 0.5 和“反”对数从 0.5 到 1

标签: python matplotlib plot axes


【解决方案1】:

解决此问题的一种方法是使用对称对数刻度,称为symlog

Symlog 是一个对数图,它在 0 附近的某个范围内呈线性行为(其中正常的对数图将无限显示数十年),因此实际上可能出现跨越 0 的对数图。

Symlog 可以在 matplotlib 中使用ax.set_xscale('symlog', linthreshx=0.1) 设置,其中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()

【讨论】:

  • 事实证明,这实际上并不是我想要的。对数正态分布应在图表上显示为一条完美的直线。我对“到 0.5 的对数和从 0.5 到 1 的“反”对数”的解释一定是不正确的。
【解决方案2】:

我知道这有点晚了,但我遇到了类似的问题并解决了它,所以我想按照 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()

您可能还想看看我的lognorm demo

【讨论】:

    猜你喜欢
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    • 2012-08-23
    • 2011-05-24
    • 1970-01-01
    相关资源
    最近更新 更多