如何在Python的matplotlib中绘制累积分布函数(CDF)?

32

我有一个名为d的无序列表,看起来像这样:

[0.0000, 123.9877,0.0000,9870.9876, ...]

我只是想使用Python中的Matplotlib根据这个列表绘制一个累积分布函数图表。但不知道是否有可用的函数。

d = []
d_sorted = []
for line in fd.readlines():
    (addr, videoid, userag, usertp, timeinterval) = line.split()
    d.append(float(timeinterval))

d_sorted = sorted(d)

class discrete_cdf:
    def __init__(data):
        self._data = data # must be sorted
        self._data_len = float(len(data))

    def __call__(point):
        return (len(self._data[:bisect_left(self._data, point)]) / 
               self._data_len)

cdf = discrete_cdf(d_sorted)
xvalues = range(0, max(d_sorted))
yvalues = [cdf(point) for point in xvalues]
plt.plot(xvalues, yvalues)
现在我正在使用这段代码,但错误消息是:
Traceback (most recent call last):
File "hitratioparea_0117.py", line 43, in <module>
cdf = discrete_cdf(d_sorted)
TypeError: __init__() takes exactly 1 argument (2 given)

2
像这里显示的那样(第三个图)? - chl
@chl 是的,类似那样的东西。 - manxing
1
你的错误“init() takes exactly 1 argument (2 given)”来自于你的类方法“__init__”应该接受它本身“def init(self, data)”。 - Hooked
7个回答

46

我知道我来晚了。但是,如果你只想为你的图形获取累积分布函数而不是为将来的计算获取它,那么有一个更简单的方法:

plt.hist(put_data_here, normed=True, cumulative=True, label='CDF',
         histtype='step', alpha=0.8, color='k')

例如,

plt.hist(dataset, bins=bins, normed=True, cumulative=True, label='CDF DATA', 
         histtype='step', alpha=0.55, color='purple')
# bins and (lognormal / normal) datasets are pre-defined

编辑:这个示例可能更有帮助。


1
这在大数值情况下可能有效。对于小数值,CDF 的垂直部分会错位。例如,尝试数据x = pd.Series([1,2,2,7,7])。这是因为直方图是一组“胖”矩形的集合。 - Jeffrey Benjamin Brown
12
2018年更新:normed已被弃用,推荐使用density - Scott Gigante
7
我不太喜欢曲线末端的下降。根据我的理解,累积分布函数应该以1结束。有没有简单的方法可以消除这种下降?剪掉图形的右边缘是不会有帮助的,因为我在同一个图中有多个具有不同下降的累积分布函数。 - CGFoX
@CGFoX,我也遇到了同样的情况。你如何裁剪图形的右侧边缘呢? - Ahmed Al-haddad
谢谢。与sns.kdeplot()相比,这非常快。 - crash
更好地使用内置函数。 - Impulsleistung

41

如上所述,来自 numpycumsum 函数效果很好。请确保您的数据是一个正确的概率密度函数(即总和为 1),否则累积分布函数将不会以单位结束,正如它应该的那样。以下是一个最小工作示例:

import numpy as np
from pylab import *

# Create some test data
dx = 0.01
X  = np.arange(-2, 2, dx)
Y  = np.exp(-X ** 2)

# Normalize the data to a proper PDF
Y /= (dx * Y).sum()

# Compute the CDF
CY = np.cumsum(Y * dx)

# Plot both
plot(X, Y)
plot(X, CY, 'r--')

show()

输入图像描述


由于我们正常化Y(其中Y /=(dx * Y)。sum()),以创建一个PDF,难道Y.sum()也不应该等于1而不是100吗? - fixxxer
@fixxxer Y.sum() 的后归一化结果不应该为1,因为如果我们改变步长,总和会发生变化。应该为整个域上的积分,即 $\int_{-2}^{2} f(x) dx = 1$。技术上 归一化应该是 Y /= np.trapz(Y,X),但由于我们使用的是等间距步长,它们本质上是相同的东西。 - Hooked
3
我只有测量值数组Y,如何确定我的X?我是否仍然将dx=0.01 - CGFoX

9
numpy函数用于计算累加和的cumsum在这里可能很有用。
In [1]: from numpy import cumsum
In [2]: cumsum([.2, .2, .2, .2, .2])
Out[2]: array([ 0.2,  0.4,  0.6,  0.8,  1. ])

8
现在,您可以使用seabornkdeplot 函数,将 cumulative 参数设为True,生成一个CDF。
import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns

X1 = np.arange(100)
X2 = (X1 ** 2) / 100
sns.kdeplot(data = X1, cumulative = True, label = "X1")
sns.kdeplot(data = X2, cumulative = True, label = "X2")
plt.legend()
plt.show()

enter image description here


6
请注意,这绘制的是CDF的平滑*估计值,而不是实际数据值的步骤。您可以看到,绘制的x值延伸到0以下,即使最小数据值为0。但这指引我使用Seaborn直接完成:sns.ecdfplot(),它绘制了实际的阶梯值。https://seaborn.pydata.org/generated/seaborn.ecdfplot.html - ELNJ

5

对于任意一组值 x:

def cdf(x, plot=True, *args, **kwargs):
    x, y = sorted(x), np.arange(len(x)) / len(x)
    return plt.plot(x, y, *args, **kwargs) if plot else (x, y)

((如果你是新手python开发者,*args 和 **kwargs 可以让你在不显式声明和管理参数的情况下传递参数和命名参数))


如何在同一图中绘制两个数据集的CDF? - Farhood Hosseinpour

0

对我来说最好用的是pandas的quantile函数。

假设我有71个参与者。每个参与者都有一定数量的中断。我想计算参与者的#interruptions的CDF图。目标是能够告诉我们有多少百分比的参与者至少有30次干预。

step=0.05
indices = np.arange(0,1+step,step)
num_interruptions_per_participant = [32,70,52,52,39,20,37,31,60,57,31,71,24,23,38,4,77,37,79,43,63,43,75,13
,45,31,57,28,61,29,30,52,65,11,76,37,65,28,33,73,65,43,50,33,45,40,50,44
,33,49,24,69,55,47,22,45,54,11,30,13,32,52,31,50,10,46,10,25,47,51,83]

CDF = pd.DataFrame({'dummy':num_interruptions_per_participant})['dummy'].quantile(indices)


plt.plot(CDF,indices,linewidth=9, label='#interventions', color='blue')

enter image description here

根据图表,近25%的参与者使用不到30次干预。

您可以将此统计数据用于进一步的分析。例如,在我的情况下,为了满足需要进行留一主题评估的最低样本要求,我需要每个参与者至少进行30次干预。累积分布函数告诉我,我有25%的参与者存在问题。


-4
import matplotlib.pyplot as plt
X=sorted(data)
Y=[]
l=len(X)
Y.append(float(1)/l)
for i in range(2,l+1):
    Y.append(float(1)/l+Y[i-2])
plt.plot(X,Y,color=c,marker='o',label='xyz')

我想这样做就可以了,有关程序请参考http://www.youtube.com/watch?v=vcoCVVs0fRI


1.] 这段代码本身甚至无法运行(c是什么?)。 2.] 更重要的是,这不是累积分布函数(CDF),只是数据加上自身。尝试使用一些样本数据来查看差异。 - Hooked

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