使用SciPy制作分位数-分位数图

111

如何使用Python创建QQ图?

假定您有一组大量的测量数据并使用某个绘图函数将XY值作为输入。该函数应该将测量的分位数与某个分布(正态、均匀分布等)对应的分位数进行绘制。

生成的图形可以让我们评估测量结果是否符合所假设的分布。

http://en.wikipedia.org/wiki/Quantile-quantile_plot

R和Matlab都提供了用于此目的的现成函数,但我想知道在Python中实现该功能的最干净的方法是什么。


2
你看过 probplot 吗?http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.probplot.html - Geoff
1
qqplot 和 probplots 带有许多选项:http://statsmodels.sourceforge.net/devel/graphics.html#goodness-of-fit-plots - Josef
9个回答

129

更新:正如网友所指出的,本回答是不正确的。Probplot与分位数-分位数图不同。在解释或传达分布关系之前,请查看这些评论和其他答案。

我认为scipy.stats.probplot可以满足你的需求。有关更多详细信息,请参见文档

import numpy as np 
import pylab 
import scipy.stats as stats

measurements = np.random.normal(loc = 20, scale = 5, size=100)   
stats.probplot(measurements, dist="norm", plot=pylab)
pylab.show()

结果

enter image description here


有时我看到一些在中间变窄,两端像喇叭的点状置信线。你能把这些“指导线”添加到图表中吗? - Norfeldt
25
好的,但这是一个概率图(将样本与理论分布进行比较)。QQ图则是比较两个样本。 http://www.itl.nist.gov/div898/handbook/eda/section3/qqplot.htm http://www.itl.nist.gov/div898/handbook/eda/section3/probplot.htm - Ricky Robinson
7
似乎许多来源(包括维基百科)与NIST手册相矛盾。几乎任何其他来源都指出QQ图的水平轴是理论分位数,而垂直轴是数据分位数。无论如何,这种区别在学术上是微不足道的:绘制样本本质上与使用经验分布函数相同。无论哪种方式,您都是将一个分布的分位数与另一个分布的分位数进行绘制。 - Peter
1
我同意@RickyRobinson的观点,这不是这个问题的正确答案。即使QQ图和概率图都是将一个分布的分位数与另一个分布进行比较,但它们是不同的。 - Florent
从文档中得知:"probplot 生成概率图,不应与 Q-Q 图或 P-P 图混淆。" - ady

70

使用 statsmodels.apiqqplot 是另一种选择:

非常基本的示例:

import numpy as np
import statsmodels.api as sm
import pylab

test = np.random.normal(0,1, 1000)

sm.qqplot(test, line='45')
pylab.show()

结果:

enter image description here

文档和更多示例可在此处查看。


2
@tommy.carstensen 这是有意将其从 scipy 分离到 statsmodels 中的。 - SARose
14
请注意,你的示例绘制了标准正态分布的线。为了获得一个标准化的线(按给定样本的标准偏差缩放并加上平均值),就像 @Geoff 的示例那样,你需要将 line 参数设置为 's' 而不是 '45'。 - Mike
我认为更应该将更多资源集中在单个统计软件包上。statsmodels 将是一个不错的选择。 - Ken T

24

12
这个 qqplot 实现似乎无法处理样本大小不同的情况,这很有趣,因为 Q-Q 图的一个重要优点是可以比较大小不同的样本。 - Robert Muil

7
我想到了这个方法,也许你可以改进它。尤其是我觉得生成分位数的方法有些繁琐。
你可以用np.random中的任何其他分布替换np.random.normal来将数据与其他分布进行比较。
#!/bin/python

import numpy as np

measurements = np.random.normal(loc = 20, scale = 5, size=100000)

def qq_plot(data, sample_size):
    qq = np.ones([sample_size, 2])
    np.random.shuffle(data)
    qq[:, 0] = np.sort(data[0:sample_size])
    qq[:, 1] = np.sort(np.random.normal(size = sample_size))
    return qq

print qq_plot(measurements, 1000)

为什么要从数据中选择一个随机样本大小的子集,而不是将理论分布中的随机变量与所有测量值进行比较? - pas-calc

4
为了增加Python和R世界中有关Q-Q图和概率图的混淆,这是SciPy手册所说的:
probplot生成一个概率图,不应与Q-Q或P-P图混淆。Statsmodels具有更广泛的功能类型,请参见statsmodels.api.ProbPlot。”
如果您尝试scipy.stats.probplot,您会发现它确实将数据集与理论分布进行比较。而另一方面,Q-Q图则将两个数据集(样本)进行比较。
R具有函数qqnormqqplotqqline。来自R帮助文档(版本3.6.3):

qqnorm 是一个通用函数,其默认方法生成 y 值的正态 QQ 图。 qqline 在“理论上”的分位数-分位数图中添加一条线,默认情况下为正常值,并通过 probs 分位数(默认情况下为第一和第三四分位数)。

qqplot 生成两个数据集的 QQ 图。

简而言之,R 的 qqnorm 提供了与 scipy.stats.probplot 相同的功能,其默认设置为 dist=norm。但是,他们称之为 qqnorm 并且它应该“生成正常 QQ 图”,这可能会让用户感到困惑。

最后,警告一句。这些图不能替代适当的统计测试,仅应用于说明目的。



2
您可以使用bokeh进行数据可视化。
from bokeh.plotting import figure, show
from scipy.stats import probplot
# pd_series is the series you want to plot
series1 = probplot(pd_series, dist="norm")
p1 = figure(title="Normal QQ-Plot", background_fill_color="#E8DDCB")
p1.scatter(series1[0][0],series1[0][1], fill_color="red")
show(p1)

1
import numpy as np 
import pylab 
import scipy.stats as stats
measurements = np.random.normal(loc = 20, scale = 5, size=100)   
stats.probplot(measurements, dist="norm", plot=pylab)
pylab.show()

这里的probplot绘制了测量值与正态分布之间的图形,其中dist="norm"指定了正态分布。

1

你的样本有多大?这里有另一种选项,可以使用OpenTURNS库测试数据是否符合任何分布。在下面的示例中,我从均匀分布中生成了一个包含100万个数字的样本x,并将其与正态分布进行了测试。 如果你将数据重新整形为x=[[x1],[x2],..,[xn]],就可以替换x。

import openturns as ot

x = ot.Uniform().getSample(1000000)
g = ot.VisualTest.DrawQQplot(x, ot.Normal())
g

在我的Jupyter笔记本上,我看到: enter image description here 如果您正在编写脚本,您可以更加规范地执行它。
from openturns.viewer import View`
import matplotlib.pyplot as plt
View(g)
plt.show()

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