如何在Python中的matplotlib中绘制经验cdf?

61 python statistics numpy matplotlib scipy

如何在Python中的matplotlib中绘制数组数组的经验CDF?我正在寻找pylab的"hist"函数的cdf模拟.

我能想到的一件事是:

from scipy.stats import cumfreq
a = array([...]) # my array of numbers
num_bins =  20
b = cumfreq(a, num_bins)
plt.plot(b)
Run Code Online (Sandbox Code Playgroud)

这是正确的吗?有更简单/更好的方法吗?

谢谢.

Dav*_*ave 87

如果您喜欢linspace并喜欢单行,您可以:

plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=False))
Run Code Online (Sandbox Code Playgroud)

鉴于我的口味,我几乎总是这样做:

# a is the data array
x = np.sort(a)
y = np.arange(len(x))/float(len(x))
plt.plot(x, y)
Run Code Online (Sandbox Code Playgroud)

即使有>O(1e6)数据值,这对我有用.如果你真的需要减样我会设置

x = np.sort(a)[::down_sampling_step]
Run Code Online (Sandbox Code Playgroud)

编辑以回复评论/编辑我使用的原因endpoint=Falsey上面定义的内容.以下是一些技术细节.

经验CDF通常被正式定义为

CDF(x) = "number of samples <= x"/"number of samples"
Run Code Online (Sandbox Code Playgroud)

为了完全匹配这个正式的定义,你需要使用,y = np.arange(1,len(x)+1)/float(len(x))以便我们得到 y = [1/N, 2/N ... 1].这个估计器是一个无偏估计器,它将在无限样本的限制下收敛到真正的CDF 维基百科参考..

我倾向于使用y = [0, 1/N, 2/N ... (N-1)/N],因为(一)更容易代码/更多idomatic,(B),但仍是形式上合理的,因为人们总是可以交换CDF(x)1-CDF(x)在收敛性证明,以及(c)可与上面描述的(容易)采样方法.

在某些特定情况下,定义是有用的

y = (arange(len(x))+0.5)/len(x)
Run Code Online (Sandbox Code Playgroud)

这是两个公约之间的中间人.实际上,它说"有1/(2N)可能的值低于我在样本中看到的最低值,并且1/(2N)值的可能性大于我迄今为止看到的最大值.

然而,对于大样本,合理分布,在回答主体给出的约定是容易写,是真正的CDF的无偏估计,并与下采样方法的工作原理.

  • 这个答案应该得到更多的赞成,因为它是迄今为止唯一没有强加分档的答案.我只使用linspace简化了一点代码. (6认同)
  • @hans_meine 您的编辑,即“yvals=linspace(0,1,len(sorted))”,产生的“yvals”不是真实 CDF 的无偏估计量。 (2认同)

ars*_*ars 70

您可以使用ECDF从功能scikits.statsmodels库:

import numpy as np
import scikits.statsmodels as sm
import matplotlib.pyplot as plt

sample = np.random.uniform(0, 1, 50)
ecdf = sm.tools.ECDF(sample)

x = np.linspace(min(sample), max(sample))
y = ecdf(x)
plt.step(x, y)
Run Code Online (Sandbox Code Playgroud)

版本0.4 scicits.statsmodels被重命名为statsmodels.ECDF现在位于distributions模块中(statsmodels.tools.tools.ECDF折旧时).

import numpy as np
import statsmodels.api as sm # recommended import according to the docs
import matplotlib.pyplot as plt

sample = np.random.uniform(0, 1, 50)
ecdf = sm.distributions.ECDF(sample)

x = np.linspace(min(sample), max(sample))
y = ecdf(x)
plt.step(x, y)
plt.show()
Run Code Online (Sandbox Code Playgroud)

  • @bmu(和@Luca):棒极了 感谢您使用当前的statsmodel使代码最新! (2认同)
  • 这仍然通过“x = np.linspace(…)”强加了分箱。您可以使用“plt.step(ecdf.x,ecdf.y)”绕过此问题。 (2认同)

AFo*_*lia 17

这看起来(几乎)正是你想要的.两件事情:

首先,结果是四项的元组.第三是箱子的大小.第二个是最小仓的起点.第一个是每个箱子中或下方的点数.(最后一个是超出限制的点数,但由于您没有设置任何点,所有点都将被分箱.)

其次,您需要重新调整结果,使最终值为1,以遵循CDF的惯例,但是否则是正确的.

这是它在幕后的作用:

def cumfreq(a, numbins=10, defaultreallimits=None):
    # docstring omitted
    h,l,b,e = histogram(a,numbins,defaultreallimits)
    cumhist = np.cumsum(h*1, axis=0)
    return cumhist,l,b,e
Run Code Online (Sandbox Code Playgroud)

它进行直方图,然后产生每个箱中计数的累积和.所以结果的第i个值是小于或等于第i个bin的最大值的数组值的数量.因此,最终值只是初始数组的大小.

最后,要绘制它,您需要使用bin的初始值和bin大小来确定您需要的x轴值.

另一种选择是使用numpy.histogram哪个可以进行规范化并返回bin边缘.您需要自己完成结果计数的累计总和.

a = array([...]) # your array of numbers
num_bins = 20
counts, bin_edges = numpy.histogram(a, bins=num_bins, normed=True)
cdf = numpy.cumsum(counts)
pylab.plot(bin_edges[1:], cdf)
Run Code Online (Sandbox Code Playgroud)

(bin_edges[1:]是每个垃圾箱的上边缘.)

  • 快速说明一下:此代码实际上并没有为您提供经验CDF(在n个数据点的每一个中,步长函数增加1/n).相反,该代码基于基于直方图的PDF估计给出了CDF的估计.这种基于直方图的估计可以通过仔细/不正确地选择箱来操纵/偏置,因此它不像真实的ECDF那样表征真正的CDF. (24认同)
  • 我也不喜欢这个强加分档; 请参阅Dave的简短回答,它简单地使用`numpy.sort`来绘制CDF而不进行分箱. (3认同)

And*_*kov 15

你有没有尝试过pyplot.hist的cumulative = True参数?


1''*_*1'' 7

基于戴夫答案的单线程:

plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=False))
Run Code Online (Sandbox Code Playgroud)

编辑:hans_meine在评论中也提到了这一点.