无法在Matplotlib直方图上显示y轴概率

4

我有一些数据(pd Series),看起来是这样的(每日股票收益率,n = 555):

S = perf_manual.returns
S = S[~((S-S.mean()).abs()>3*S.std())]

2014-03-31 20:00:00    0.000000
2014-04-01 20:00:00    0.000000
2014-04-03 20:00:00   -0.001950
2014-04-04 20:00:00   -0.000538
2014-04-07 20:00:00    0.000764
2014-04-08 20:00:00    0.000803
2014-04-09 20:00:00    0.001961
2014-04-10 20:00:00    0.040530
2014-04-11 20:00:00   -0.032319
2014-04-14 20:00:00   -0.008512
2014-04-15 20:00:00   -0.034109
...

我希望从这个内容中生成一个概率分布图,使用以下方法:
print stats.normaltest(S)

n, bins, patches = plt.hist(S, 100, normed=1, facecolor='blue', alpha=0.75)
print np.sum(n * np.diff(bins))

(mu, sigma) = stats.norm.fit(S)
print mu, sigma
y = mlab.normpdf(bins, mu, sigma)
plt.grid(True)
l = plt.plot(bins, y, 'r', linewidth=2)

plt.xlim(-0.05,0.05)
plt.show()

我得到了以下内容:
NormaltestResult(statistic=66.587382579416982, pvalue=3.473230376732532e-15)
1.0
0.000495624926242 0.0118790391467

图表

我认为y轴是计数,但我想要概率。怎么做呢?我尝试了很多StackOverflow的答案,但还是无法解决。


你确定这些是计数吗?我猜它们是概率密度值,因为当你对其进行积分时,图形被归一化为1。你的x值范围只是非常小。 - jotasi
可能概率密度不是我的强项。我怎样才能将它们转换成百分比呢? - Joël
你想要什么百分比?对于每个箱子,数据在该箱子中的概率是多少?概率密度基本上意味着在某个x范围内密度的积分给出了该范围的概率。 - jotasi
数据在箱子中的概率。 - Joël
你看过 seaborn 吗?它有几个内置的复合图,可能包含你正在寻找的内容(一旦你弄清楚数据的含义)。 - cphlewis
@Joël 你可能需要澄清一下你的问题。因为你说你想要概率分布,这正是你自己所做的。但显然你想要一个点在一个箱子里的概率。虽然这是不同的东西! - jotasi
2个回答

10

使用plt.hist没有简单的方法(至少我不知道)可以这样做。但是您可以使用np.histogram简单地将数据分成bin,然后按照您想要的任何方式对数据进行归一化。如果我理解正确,您希望数据显示在给定的bin中查找点的概率,而不是概率分布。这意味着您必须缩放数据,使所有bin上的总和为1。这可以通过执行bin_probability = n / float(n.sum())来完成。

那么您将不再拥有正确归一化的概率分布函数(pdf),也就是说,区间上的积分不再是概率!这就是为什么必须重新调整您的mlab.normpdf以具有与直方图相同的规范性的原因。所需的因子正好是bin宽度,因为当您从正确归一化的bin pdf开始时,所有bin的总和乘以它们各自的宽度为1。现在,您只想将bin总和设为1。因此,缩放因子是bin宽度。

因此,最终代码应该类似于以下内容:

import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab

# Produce test data
S = np.random.normal(0, 0.01, size=1000)

# Histogram:
# Bin it
n, bin_edges = np.histogram(S, 100)
# Normalize it, so that every bins value gives the probability of that bin
bin_probability = n/float(n.sum())
# Get the mid points of every bin
bin_middles = (bin_edges[1:]+bin_edges[:-1])/2.
# Compute the bin-width
bin_width = bin_edges[1]-bin_edges[0]
# Plot the histogram as a bar plot
plt.bar(bin_middles, bin_probability, width=bin_width)

# Fit to normal distribution
(mu, sigma) = stats.norm.fit(S)
# The pdf should not normed anymore but scaled the same way as the data
y = mlab.normpdf(bin_middles, mu, sigma)*bin_width
l = plt.plot(bin_middles, y, 'r', linewidth=2)

plt.grid(True)
plt.xlim(-0.05,0.05)
plt.show()

最终生成的图片如下:

enter image description here


谢谢你解答我的疑惑,让我不再困惑 :) - Joël

6
jotasi的答案当然有效,但我想添加一个非常简单的技巧,通过直接调用hist来实现。
这个技巧是使用weights参数。默认情况下,你传递的每个数据点的权重都为1。然后,每个箱子的高度是落入该箱子的数据点的权重之和。相反,如果我们有n个数据点,我们可以将每个点的权重简单地设为1 / n。那么,掉落到某个桶中的点的权重之和也是在该桶中给定点的概率。
在您的情况下,只需将绘图行更改为:
n, bins, patches = plt.hist(S, weights=np.ones_like(S) / len(S),
                            facecolor='blue', alpha=0.75)

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