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

时间:2010-07-09 02:14:19

标签: python numpy statistics 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)

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

感谢。

16 个答案:

答案 0 :(得分:81)

如果你喜欢linspace并喜欢单行,你可以这样做:

plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=False))

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

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

即使有>O(1e6)个数据值,这对我也有用。 如果你真的需要缩小样本我会设置

x = np.sort(a)[::down_sampling_step]

修改以回复评论/修改我使用上述endpoint=Falsey的原因。以下是一些技术细节。

经验CDF通常正式定义为

CDF(x) = "number of samples <= x"/"number of samples"

为了与此正式定义完全匹配,您需要使用y = np.arange(1,len(x)+1)/float(len(x))以便我们获得 y = [1/N, 2/N ... 1]。该估计量是一个无偏估计量,将在无限样本Wikipedia ref.的极限内收敛到真正的CDF。

我倾向于使用y = [0, 1/N, 2/N ... (N-1)/N],因为(a)更容易编码/更具风格,(b)但仍然是正式合理的,因为人们总是可以CDF(x)1-CDF(x)交换收敛证明,以及(c)与上述(简单)下采样方法一起工作。

在某些特定情况下,定义

很有用
y = (arange(len(x))+0.5)/len(x)

介于这两个约定之间。实际上,这表示“有1/(2N)的机会值低于我在样本中看到的最低值,并且1/(2N)机会值大于最大值I'到目前为止看到了。

然而,对于大样本和合理分布,答案主体中给出的约定很容易编写,是真正CDF的无偏估计,并且与下采样方法一起使用。

答案 1 :(得分:69)

您可以使用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)

版本0.4 scicits.statsmodels已重命名为statsmodelsECDF现在位于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()

答案 2 :(得分:16)

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

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

其次,您需要重新调整结果,使最终值为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

进行直方图编码,然后生成每个bin中计数的累积和。所以结果的第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)

bin_edges[1:]是每个bin的上边缘。)

答案 3 :(得分:15)

您是否尝试过pyplot.hist的累积= True参数?

答案 4 :(得分:7)

基于戴夫回答的单行:

plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=False))

编辑:hans_meine在评论中也提出了这一点。

答案 5 :(得分:3)

你想用CDF做什么? 为了绘制它,这是一个开始。您可以尝试一些不同的值,如下所示:

from __future__ import division
import numpy as np
from scipy.stats import cumfreq
import pylab as plt

hi = 100.
a = np.arange(hi) ** 2
for nbins in ( 2, 20, 100 ):
    cf = cumfreq(a, nbins)  # bin values, lowerlimit, binsize, extrapoints
    w = hi / nbins
    x = np.linspace( w/2, hi - w/2, nbins )  # care
    # print x, cf
    plt.plot( x, cf[0], label=str(nbins) )

plt.legend()
plt.show()

Histogram 列出了各种箱子的规则,例如: num_bins ~ sqrt( len(a) )

(小字:这里有两件完全不同的东西,

  • 对原始数据进行分箱/直方图化
  • plot通过说出20个分箱值插入平滑曲线。

其中任何一个都可以解决“clumpy”的数据 或者有很长的尾巴,即使对于1d数据 - 2d,3d数据也变得越来越困难 也可以看看 Density_estimationusing scipy gaussian kernel density estimation )。

答案 6 :(得分:3)

我对AFoglia的方法进行了微不足道的补充,以使CDF正常化

n_counts,bin_edges = np.histogram(myarray,bins=11,normed=True) 
cdf = np.cumsum(n_counts)  # cdf not normalized, despite above
scale = 1.0/cdf[-1]
ncdf = scale * cdf

规范化histo使其积分统一,这意味着cdf不会被规范化。你必须自己扩展它。

答案 7 :(得分:3)

如果你想显示真实的真实ECDF(正如David B所说的是在n个数据点中每个都增加1 / n的阶跃函数),我的建议是编写代码为每个数据点生成两个“绘图”点:

a = array([...]) # your array of numbers
sorted=np.sort(a)
x2 = []
y2 = []
y = 0
for x in sorted: 
    x2.extend([x,x])
    y2.append(y)
    y += 1.0 / len(a)
    y2.append(y)
plt.plot(x2,y2)

通过这种方式,您将获得具有ECDF特征的n个步骤的绘图,这对于足够小以使步骤可见的数据集尤其好。此外,没有必要使用直方图进行任何分组(这可能会对绘制的ECDF产生偏差)。

答案 8 :(得分:3)

我们可以使用step中的matplotlib函数,这是一个逐步绘制的图,这是经验CDF的定义:

import numpy as np
from matplotlib import pyplot as plt

data = np.random.randn(11)

levels = np.linspace(0, 1, len(data) + 1)  # endpoint 1 is included by default
plt.step(sorted(list(data) + [max(data)]), levels)

手动添加了max(data)处的最终垂直线。否则,情节只会停留在1 - 1/len(data)级别。

或者,我们可以使用where='post'选项step()

levels = np.linspace(1. / len(data), 1, len(data))
plt.step(sorted(data), levels, where='post')

在这种情况下,不绘制从零开始的初始垂直线。

答案 9 :(得分:2)

这是使用散景

```

from bokeh.plotting import figure, show
from statsmodels.distributions.empirical_distribution import ECDF
ecdf = ECDF(pd_series)
p = figure(title="tests", tools="save", background_fill_color="#E8DDCB")
p.line(ecdf.x,ecdf.y)
show(p)

```

答案 10 :(得分:2)

这是使用cumulative = True参数的seaborn中的单行程序。你去吧,

import seaborn as sns
sns.kdeplot(a, cumulative=True)

答案 11 :(得分:1)

(这是我对问题的回答的副本:Plotting CDF of a pandas series in python

CDF或累积分布函数图基本上是在X轴上具有排序值并且在Y轴上具有累积分布的图。因此,我将创建一个新系列,其中排序值为索引,累积分布为值。

首先创建一个示例系列:

<li>

对系列进行排序:

import pandas as pd
import numpy as np
ser = pd.Series(np.random.normal(size=100))

现在,在继续之前,再次追加最后一个(也是最大的)值。这一步对于小样本量非常重要,以获得无偏见的CDF:

ser = ser.order()

创建一个新系列,其中排序值为索引,累积分布为值

ser[len(ser)] = ser.iloc[-1]

最后,将该函数绘制为步骤:

cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)

答案 12 :(得分:1)

假设vals保存您的值,那么您可以按如下方式绘制CDF:

y = numpy.arange(0, 101)
x = numpy.percentile(vals, y)
plot(x, y)

要在0和1之间缩放,只需将y除以100.

答案 13 :(得分:0)

到目前为止,没有一个答案涵盖了我在这里登陆时想要的内容,即:

def empirical_cdf(x, data):
    "evaluate ecdf of data at points x"
    return np.mean(data[None, :] <= x[:, None], axis=1)

它在点x的数组处评估给定数据集的经验CDF,不必对其进行排序。没有中间分箱,也没有外部库。

对大x进行更好扩展的等效方法是对数据进行排序并使用np.searchsorted:

def empirical_cdf(x, data):
    "evaluate ecdf of data at points x"
    data = np.sort(data)
    return np.searchsorted(data, x)/float(data.size)

答案 14 :(得分:0)

在我看来,以前的方法都无法完全(严格)地绘制经验CDF,这是问询者最初提出的问题。我向任何迷失和同情的人发布我的求婚信。

我的建议有以下内容:1)它考虑了第一个表达式here中定义的经验CDF,即像AW Van der Waart的渐近统计(1998),2 )明确显示函数的阶跃行为,3)明确显示经验CDF通过显示标记来解决不连续性,从右开始是连续的,4)它将零和一的值从极点扩展到用户定义的边距。我希望它可以帮助某人:

def plot_cdf( data, xaxis = None, figsize = (20,10), line_style = 'b-',
ball_style = 'bo', xlabel = r"Random variable $X$", ylabel = "$N$-samples
empirical CDF $F_{X,N}(x)$" ):
     # Contribution of each data point to the empirical distribution
     weights = 1/data.size * np.ones_like( data )
     # CDF estimation
     cdf = np.cumsum( weights )
     # Plot central part of the CDF
     plt.figure( figsize = (20,10) )
     plt.step( np.sort( a ), cdf, line_style, where = 'post' )
     # Plot valid points at discontinuities
     plt.plot( np.sort( a ), cdf, ball_style )
     # Extract plot axis and extend outside the data range
     if not xaxis == None:
         (xmin, xmax, ymin, ymax) = plt.axis( )
         xmin = xaxis[0]
         xmax = xaxis[1]
         plt.axis( [xmin, xmax, ymin, ymax] )
     else:
         (xmin,xmax,_,_) = plt.axis()
         plt.plot( [xmin, a.min(), a.min()], np.zeros( 3 ), line_style )
     plt.plot( [a.max(), xmax], np.ones( 2 ), line_style )
     plt.xlabel( xlabel )
     plt.ylabel( ylabel )

答案 15 :(得分:0)

我为大型数据集评估cdf的操作-

  1. 查找唯一值

    unique_values = np.sort(pd.Series)

  2. 为数据集中的这些排序后的值和唯一值创建等级数组-

    ranks = np.arange(0,len(unique_values)-1 /(len(unique_values)-1)

  3. 绘制唯一值与排名

示例 以下代码从kaggle-

绘制了人口dataset的cdf
us_census_data = pd.read_csv('acs2015_census_tract_data.csv')

population = us_census_data['TotalPop'].dropna()

## sort the unique values using pandas unique function
unique_pop = np.sort(population.unique())
cdf = np.arange(0,len(unique_pop),step=1)/(len(unique_pop)-1)

## plotting    
plt.plot(unique_pop,cdf)
plt.show()

enter image description here