【问题标题】:How to perform a chi-squared goodness of fit test using scientific libraries in Python?如何使用 Python 中的科学库执行卡方拟合优度测试?
【发布时间】:2014-08-13 19:06:08
【问题描述】:

假设我有一些经验数据:

from scipy import stats
size = 10000
x = 10 * stats.expon.rvs(size=size) + 0.2 * np.random.uniform(size=size)

它呈指数分布(带有一些噪声),我想使用卡方拟合优度 (GoF) 测试来验证这一点。使用 Python 中的标准科学库(例如 scipy 或 statsmodels)以最少的手动步骤和假设来执行此操作的最简单方法是什么?

我可以为模型拟合:

param = stats.expon.fit(x)
plt.hist(x, normed=True, color='white', hatch='/')
plt.plot(grid, distr.pdf(np.linspace(0, 100, 10000), *param))

计算Kolmogorov-Smirnov test非常优雅。

>>> stats.kstest(x, lambda x : stats.expon.cdf(x, *param))
(0.0061000000000000004, 0.85077099515985011)

但是,我找不到计算卡方检验的好方法。

有一个chi-squared GoF function in statsmodel,但它假设一个离散分布(并且指数分布是连续的)。

official scipy.stats tutorial 仅涵盖自定义分布的情况,概率是通过摆弄许多表达式(npoints、npointsh、nbound、normbound)来构建的,所以我不太清楚如何为其他分布做这件事。 chisquare examples 假设已获得预期值和景深。

另外,我不是像already discussed here 那样寻找“手动”执行测试的方法,而是想知道如何应用其中一个可用的库函数。

【问题讨论】:

  • 据我所知,没有用于卡方测试的“官方”python 库函数,其中包括用于连续分布的分箱。如果我没记错的话,我会推荐使用 Anderson-Darling,scipy 的 anderson,它应该有更好的能力。
  • 好的,但据我所知,anderson implementation in SciPy 只支持 5 个发行版。
  • 是的,但是安德森支持您正在使用的指数分布。如果您估计分布的参数并且希望它适用于任何分布,那么您将返回卡方分箱,或引导另一个 gof 测试。
  • 您能否在答案中解释我将如何对我的示例执行分箱和卡方检验?我知道我需要使用 hstack 并组合 bin 以获得 >5 个数据点,但我不知道如何获取这些 bin 的概率数组。我正在尝试找到一个可用于任意数据的通用工作流程,并且我宁愿不局限于与安德森实现一样的少数分布。
  • 您使用 Kolmogorov-Smirnov 检验的方式在统计上是错误的,因为分布的参数是根据样本估计的。正确的做法是使用 Lilliefors 的测试:en.wikipedia.org/wiki/Lilliefors_test

标签: python scipy statsmodels goodness-of-fit


【解决方案1】:

我用 OpenTURNS 试过你的问题。 开头是一样的:

import numpy as np
from scipy import stats
size = 10000
x = 10 * stats.expon.rvs(size=size) + 0.2 * np.random.uniform(size=size)

如果您怀疑您的样本 x 来自指数分布,您可以使用 ot.ExponentialFactory() 来拟合参数:

import openturns as ot
sample = ot.Sample([[p] for p in x])
distribution = ot.ExponentialFactory().build(sample)

由于 Factory 需要一个 ot.Sample() 作为输入,我需要格式化 x 并将其重塑为 10.000 个维度为 1 的点。

现在让我们使用卡方检验评估此拟合:

result = ot.FittingTest.ChiSquared(sample, distribution, 0.01)
print('Exponential?', result.getBinaryQualityMeasure(), ', P-value=', result.getPValue())
>>> Exponential? True , P-value= 0.9275212544642293

很好!

当然,print(distribution) 会为您提供拟合参数:

>>> Exponential(lambda = 0.0982391, gamma = 0.0274607)

【讨论】:

    【解决方案2】:

    为什么需要“验证”它是指数级的?你确定你需要统计测试吗?我几乎可以保证这最终不是指数级的,如果你有足够的数据,测试会很重要,这使得使用测试的逻辑相当强制。阅读此 CV 线程:Is normality testing 'essentially useless'?,或我的答案:Testing for heteroscedasticity with many observations,可能会对您有所帮助。

    通常最好使用 qq-plot 和/或 pp-plot(取决于您是否关心分布的尾部或中间的拟合,请参阅我的答案:PP-plots vs. QQ-plots)。关于如何在 Python SciPy 中制作 qq-plots 的信息可以在这个 SO 线程中找到:Quantile-Quantile plot using SciPy

    【讨论】:

    • 我不知道QQ绘图。我研究一下,谢谢。我的动机只是为了能够对数据集分布的确定性给出一些定量测量(比“查看直方图,它似乎呈指数级”更正式)。我认为适合度测试在这里可以帮助我,但我现在从你链接的讨论中看到它可能不是那么简单:)
    • 有一些方法可以量化两个分布的接近程度。统计 test 并不能完全告诉您,b/c p 值是该距离和您的 N 的函数。您可以使用 qq- 或 pp 中的点的相关性-plot(但请记住,r 将始终接近 1),您也可以使用 KL 之类的东西(实际上不是距离)。您还可以在 CV 上询问有关获得 b/t 2 dists 距离的定量测量的最佳方法的问题。结果会很复杂,取决于您的需要。
    • chisquare 为您提供距离度量,您也可以选择任何其他 gof 测试作为“距离度量”。但是,它不会告诉你太多。这些问题并非特定于 gof 测试。在所有假设检验中,您都必须担心小样本的功效太小,而大样本的功效太大。 statsmodels 具有计算卡方检验的效果大小和功效的功能,例如statsmodels.sourceforge.net/devel/generated/…
    • 如果您首先假设分布是指数分布在查看直方图之后,那么任何拟合优度检验的 p 值都会过于乐观。在任何情况下,他们都不会告诉您如何确定基础总体或过程是指数的,但是如果您得到一个非常低的 p 值,至少您有一个基础,认为指数假设是不正确的。
    • 感谢您的所有意见。和往常一样,我发现我还有很多事情要做。如果有一本书解释如何在真实的数据分析示例中使用这些 scipy/statsmodels 函数,那就太好了。当前的文档太少了,我无法理解所有功能。我会将任何其他与统计相关的问题发布到 CV。
    【解决方案3】:

    等概率箱的近似解:

    • 估计分布参数
    • 如果是 scipy.stats.distribution,则使用逆 cdf ppf 来获取规则概率网格的边,例如distribution.ppf(np.linspace(0, 1, n_bins + 1), *args)
    • 然后,使用 np.histogram 计算每个 bin 中的观察次数

    然后对频率使用卡方检验。

    另一种方法是从已排序数据的百分位数中查找 bin 边缘,并使用 cdf 查找实际概率。

    这只是近似值,因为卡方检验的理论假设参数是通过分箱数据的最大似然估计的。而且我不确定基于数据选择binedges是否会影响渐近分布。

    我很久没有研究这个问题了。 如果一个近似的解决方案不够好,那么我建议你在 stats.stackexchange 上提问。

    【讨论】:

    • Re:分箱是否会影响渐近分布,它几乎必须。不过,可能微不足道。对于分箱和使用卡方检验,这将是正确的答案。 +1
    • @Gung 这取决于渐近线的性质。我相信,如果您以允许最小预期 bin 计数变大的方式拟合切点,则渐近分布应该是卡方的。但渐近分布无关紧要:重要的是实际分布,很明显,根据数据建立切点会在该分布中引入任意变化(如果只是一点点)。跨度>
    • @user333700 您能否提供一个您提供的解决方案的示例。我试过这个:In: np.random.seed(453)In: data_1 = stats.norm.rvs(size=10000)In: loc, scale = stats.norm.fit(data_1)In: data_2 = stats.norm(loc, scale).rvs(size=10000)In: data_1_hist = np.histogram(data_1, bins=10)In: data_2_hist = np.histogram(data_2, bins=10)In: print stats.chisquare(data_2_hist[0], data_1_hist[0])Out: (statistic=564.43784612331842, pvalue=8.926608295951506e-116)。还有,distribution.ppf(np.linspace(0, 1, n_bins + 1), *args)应该怎么用?
    猜你喜欢
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 2014-03-06
    相关资源
    最近更新 更多