累积分布图python

Posted

技术标签:

【中文标题】累积分布图python【英文标题】:cumulative distribution plots python 【发布时间】:2013-03-02 17:58:20 【问题描述】:

我正在使用 python 做一个项目,其中有两个数据数组。我们称它们为 pcpnc。我需要在同一张图上绘制这两者的累积分布。对于 pc,它应该是一个小于图,即在 (x,y) 处,pc 中的 y 点的值必须小于 x。对于 pnc,它是一个大于图,即在 (x,y) 处,pnc 中的 y 点的值必须大于 x。

我尝试过使用直方图函数 - pyplot.hist。有没有更好更简单的方法来做我想做的事?此外,它必须在 x 轴上以对数刻度绘制。

【问题讨论】:

如果您展示了迄今为止的尝试,将会有所帮助 - 示例输入数据、所需的输出等...否则这将被视为“显示代码”问题 为了扩展 Jon 的评论,人们非常更乐意帮助您修复您拥有的代码,而不是从头开始生成代码。无论您的代码有多么错误和无功能,都要展示它并解释 a) 您期望它做什么以及 b) 它当前正在做什么。 【参考方案1】:

你很亲密。您不应该将 plt.hist 用作 numpy.histogram,它会同时提供值和 bin,而不是您可以轻松绘制累积值:

import numpy as np
import matplotlib.pyplot as plt

# some fake data
data = np.random.randn(1000)
# evaluate the histogram
values, base = np.histogram(data, bins=40)
#evaluate the cumulative
cumulative = np.cumsum(values)
# plot the cumulative function
plt.plot(base[:-1], cumulative, c='blue')
#plot the survival function
plt.plot(base[:-1], len(data)-cumulative, c='green')

plt.show()

【讨论】:

仅供参考,您忘记在 cumsum 之前包含 np,因为您的 np.histogram 命令暗示需要。 @ehsteve 固定答案。 使用直方图既不必要又不精确。 @EOL 但对于大型阵列是必需的,否则您将耗尽内存。 确实,但我认为这不是问题的特殊情况,这更多是关于如何获得累积分布,而不是在大数组的情况下进行,大约。 【参考方案2】:

使用直方图确实不必要地繁重且不精确(分箱使数据模糊):您可以对所有 x 值进行排序:每个值的索引是较小值的数量。这个更短更简单的解决方案如下所示:

import numpy as np
import matplotlib.pyplot as plt

# Some fake data:
data = np.random.randn(1000)

sorted_data = np.sort(data)  # Or data.sort(), if data can be modified

# Cumulative counts:
plt.step(sorted_data, np.arange(sorted_data.size))  # From 0 to the number of data points-1
plt.step(sorted_data[::-1], np.arange(sorted_data.size))  # From the number of data points-1 to 0

plt.show()

此外,更合适的绘图样式确实是 plt.step() 而不是 plt.plot(),因为数据位于离散位置。

结果是:

您可以看到它比 EnricoGiampieri 的答案的输出更加参差不齐,但这个是真正的直方图(而不是它的近似、模糊版本)。

PS:正如 SebastianRaschka 所指出的,最后一点应该理想地显示总计数(而不是总计数 1)。这可以通过以下方式实现:

plt.step(np.concatenate([sorted_data, sorted_data[[-1]]]),
         np.arange(sorted_data.size+1))
plt.step(np.concatenate([sorted_data[::-1], sorted_data[[0]]]),
         np.arange(sorted_data.size+1))

data 中有很多点,如果不缩放,效果是不可见的,但是当数据只包含几个点时,总计数的最后一点很重要。

【讨论】:

但是对于大型数组,您希望使用直方图方法,因为它几乎不需要太多内存。 plt.step 方法让我的 6000 万个元素数组出现内存错误。 同意。我不确定问题出在plt.step 上,还是这个确切的方法使用的内存可能是数组内存的 3 倍,或者两者兼而有之…… 我同意: plt.step 可能是绘制“计数”更合适的方法。一个问题:您不必使用plt.step(sorted_data, np.arange(1, data.size+1)) 来获得正确的计数吗? @SebastianRaschka:好点。你是对的。一个完美的解决方案将添加最后一点。这可以通过复制最后一个横坐标并在最后一个纵坐标处添加总计数 (5) 来完成。我更新了答案,谢谢! 感谢您的更新。你的解决方法看起来肯定比我的好:)【参考方案3】:

在与@EOL 进行结论性讨论后,我想发布我的解决方案(左上角),使用随机高斯样本作为摘要:

import numpy as np
import matplotlib.pyplot as plt
from math import ceil, floor, sqrt

def pdf(x, mu=0, sigma=1):
    """
    Calculates the normal distribution's probability density 
    function (PDF).  

    """
    term1 = 1.0 / ( sqrt(2*np.pi) * sigma )
    term2 = np.exp( -0.5 * ( (x-mu)/sigma )**2 )
    return term1 * term2


# Drawing sample date poi
##################################################

# Random Gaussian data (mean=0, stdev=5)
data1 = np.random.normal(loc=0, scale=5.0, size=30)
data2 = np.random.normal(loc=2, scale=7.0, size=30)
data1.sort(), data2.sort()

min_val = floor(min(data1+data2))
max_val = ceil(max(data1+data2))

##################################################




fig = plt.gcf()
fig.set_size_inches(12,11)

# Cumulative distributions, stepwise:
plt.subplot(2,2,1)
plt.step(np.concatenate([data1, data1[[-1]]]), np.arange(data1.size+1), label='$\mu=0, \sigma=5$')
plt.step(np.concatenate([data2, data2[[-1]]]), np.arange(data2.size+1), label='$\mu=2, \sigma=7$') 

plt.title('30 samples from a random Gaussian distribution (cumulative)')
plt.ylabel('Count')
plt.xlabel('X-value')
plt.legend(loc='upper left')
plt.xlim([min_val, max_val])
plt.ylim([0, data1.size+1])
plt.grid()

# Cumulative distributions, smooth:
plt.subplot(2,2,2)

plt.plot(np.concatenate([data1, data1[[-1]]]), np.arange(data1.size+1), label='$\mu=0, \sigma=5$')
plt.plot(np.concatenate([data2, data2[[-1]]]), np.arange(data2.size+1), label='$\mu=2, \sigma=7$') 

plt.title('30 samples from a random Gaussian (cumulative)')
plt.ylabel('Count')
plt.xlabel('X-value')
plt.legend(loc='upper left')
plt.xlim([min_val, max_val])
plt.ylim([0, data1.size+1])
plt.grid()


# Probability densities of the sample points function
plt.subplot(2,2,3)

pdf1 = pdf(data1, mu=0, sigma=5)
pdf2 = pdf(data2, mu=2, sigma=7)
plt.plot(data1, pdf1, label='$\mu=0, \sigma=5$')
plt.plot(data2, pdf2, label='$\mu=2, \sigma=7$')

plt.title('30 samples from a random Gaussian')
plt.legend(loc='upper left')
plt.xlabel('X-value')
plt.ylabel('probability density')
plt.xlim([min_val, max_val])
plt.grid()


# Probability density function
plt.subplot(2,2,4)

x = np.arange(min_val, max_val, 0.05)

pdf1 = pdf(x, mu=0, sigma=5)
pdf2 = pdf(x, mu=2, sigma=7)
plt.plot(x, pdf1, label='$\mu=0, \sigma=5$')
plt.plot(x, pdf2, label='$\mu=2, \sigma=7$')

plt.title('PDFs of Gaussian distributions')
plt.legend(loc='upper left')
plt.xlabel('X-value')
plt.ylabel('probability density')
plt.xlim([min_val, max_val])
plt.grid()

plt.show()

【讨论】:

【参考方案4】:

为了增加自己对社区的贡献,在这里分享一下我绘制直方图的函数。这就是我理解问题的方式,同时绘制直方图和累积直方图:

def hist(data, bins, title, labels, range = None):
  fig = plt.figure(figsize=(15, 8))
  ax = plt.axes()
  plt.ylabel("Proportion")
  values, base, _ = plt.hist( data  , bins = bins, normed=True, alpha = 0.5, color = "green", range = range, label = "Histogram")
  ax_bis = ax.twinx()
  values = np.append(values,0)
  ax_bis.plot( base, np.cumsum(values)/ np.cumsum(values)[-1], color='darkorange', marker='o', linestyle='-', markersize = 1, label = "Cumulative Histogram" )
  plt.xlabel(labels)
  plt.ylabel("Proportion")
  plt.title(title)
  ax_bis.legend();
  ax.legend();
  plt.show()
  return

如果有人想知道它的外观,请看一下(已激活 seaborn):

另外,关于双格(白线),我一直在努力获得漂亮的双格。这里有一个有趣的方法来规避这个问题:How to put grid lines from the secondary axis behind the primary plot?

【讨论】:

如果你期望你的数组中有负值,你可能想要取绝对值...否则累积直方图看起来会消失【参考方案5】:

生成此图的最简单方法是使用seaborn

import seaborn as sns  
sns.ecdfplot()

这里是documentation

【讨论】:

以上是关于累积分布图python的主要内容,如果未能解决你的问题,请参考以下文章

如何在python中计算正态累积分布函数的倒数?

在 Python 中计算累积分布函数 (CDF)

python 来自图像直方图的累积分布函数

如何计算累积正态分布?

Matplotlib实例教程 | 句子长度累积分布函数图

频率分布直方图和累积频率分布图