Scikit-learn χ²(卡方)统计量和相应的列联表

Posted

技术标签:

【中文标题】Scikit-learn χ²(卡方)统计量和相应的列联表【英文标题】:Scikit-learn χ² (chi-squared) statistic and corresponding contingency table 【发布时间】:2014-02-12 09:59:41 【问题描述】:

在 scikit-learn http://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.chi2.html 的卡方单变量特征选择函数的文档中,它指出

该分数可用于从 X 中选择具有最高 χ²(卡方)统计值的 n_features 特征,该统计必须包含布尔值或频率(例如,文档分类中的术语计数),相对于类。

我很难理解相应的列联表是什么样的,尤其是在频率特征的情况下。

例如,考虑以下具有布尔特征和目标的数据集:

import numpy as np

>>> X = np.random.randint(2, size=50).reshape(10, 5)
array([[1, 0, 0, 0, 1],
       [1, 1, 0, 1, 1],
       [1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1],
       [1, 0, 0, 0, 1],
       [1, 0, 1, 1, 1],
       [0, 1, 1, 0, 0],
       [1, 0, 1, 1, 1],
       [1, 1, 1, 1, 0]])

>>> y = np.random.randint(2, size=10)
array([1, 0, 0, 0, 1, 1, 1, 1, 0, 1])

要构造关于第一个特征的列联表,我们可以这样做(请原谅我的 PEP8 违规)

import scipy as sp

>>> contingency_table = sp.sparse.coo_matrix(
...    (np.ones_like(y), (X[:, 0], y)), 
...    shape=(np.unique(X[:, 0]).shape[0], np.unique(y).shape[0])).A
array([[1, 2],
       [3, 4]])

所以现在我可以计算卡方统计量及其 p 值

>>> sp.stats.chi2_contingency(contingency_table)
(0.17857142857142855,
 0.67260381744151676,
 1,
 array([[ 1.2,  1.8],
       [ 2.8,  4.2]]))

这应该与 scikit-learn 的 chi2 一致

from sklearn.feature_selection import chi2

>>> chi2_, pval = chi2(X, y)
>>> chi2_[0], pval[0]
(0.023809523809523787, 0.87737055606414338)

...不。我误解了什么吗?

此外,在频率的情况下,列联表是什么样的?我以为会是这样的

contingency_table = sp.sparse.coo_matrix(
    (np.ones_like(y), (X[:, 0], y)), 
    shape=(X[:, 0].max()+1, np.unique(y).shape[0])).A

但对应的预期频率表很可能有几个零元素。

编辑:

为了进一步澄清,请考虑第一个特征X[:, 0],即gender和目标y,例如handedness

由此我们得到交叉表

                Right-handed    Left-handed (!right-handed)
Male            1               2
Female (!male)  3               4

我们可以通过设置期望频率,使用卡方检验来评估两个比例之间差异的显着性

sklearn.feature_selection.chi2 直接执行此操作,无需显式计算表格,并使用等效于scipy.stats.chisquare 的更有效过程获得分数。

在明确列举了上面显示的表格后,我想在应用scipy.stats.chi2_contingency 时验证它是否与chi2 一致,但令我沮丧的是,事实并非如此。我想问一下为什么不是。

【问题讨论】:

有趣:最近一小时我一直在尝试解决一个非常相似的问题,但方向相反:从本教程youtube.com/watch?v=VskmMgXmkMQ中找到的第一个示例列联表中,验证对应的提供给sklearn.feature_selection.chi2 的 2d 布尔数据集(X=is_male, y=is_deposit)应该会产生相同的结果,并尝试通过研究源代码来理解为什么不是这样。 我发现您从陈述一个包含 5 个特征的数据集开始,然后仅使用第一个特征构建您的案例的其余部分,这让我感到困惑。我认为这就是让@larsmans 的答案在这种情况下令人困惑的原因,因为他正在回答一个不同的问题。 这很酷。看起来权变矩阵是一个 2x2 矩阵,并且您在生成目标时获得所有特征集的单个 p-val,而 feature_selection chi2 在生成目标时独立于其他特征显示每个特征的 p-val 【参考方案1】:

鉴于您的数据,

>>> X = array([[1, 0, 0, 0, 1],
...        [1, 1, 0, 1, 1],
...        [1, 0, 0, 0, 0],
...        [0, 0, 0, 0, 0],
...        [0, 0, 0, 0, 1],
...        [1, 0, 0, 0, 1],
...        [1, 0, 1, 1, 1],
...        [0, 1, 1, 0, 0],
...        [1, 0, 1, 1, 1],
...        [1, 1, 1, 1, 0]])
>>> y = array([1, 0, 0, 0, 1, 1, 1, 1, 0, 1])

这是feature_selection.chi2 计算的:

>>> Y = np.vstack([1 - y, y])
>>> observed = np.dot(Y, X)
>>> observed
array([[3, 1, 1, 2, 2],
       [4, 2, 3, 2, 4]])

这些是每个类观察到的特征频率,即列联表。那么期望值:

>>> feature_count = X.sum(axis=0)
>>> class_prob = Y.mean(axis=1)
>>> expected = np.dot(feature_count.reshape(-1, 1), class_prob.reshape(1, -1)).T
>>> expected
array([[ 2.8,  1.2,  1.6,  1.6,  2.4],
       [ 4.2,  1.8,  2.4,  2.4,  3.6]])

最后,它运行一个 χ² 检验:

>>> from scipy.stats import chisquare
>>> score, pval = chisquare(observed, expected)
>>> score
array([ 0.02380952,  0.05555556,  0.375     ,  0.16666667,  0.11111111])
>>> pval
array([ 0.87737056,  0.81366372,  0.54029137,  0.6830914 ,  0.73888268])

分数是相关的部分:它们用于按判别力对特征进行排序。请注意,您将获得一个分数和一个 p每个特征

【讨论】:

感谢您的回答。我认为我在提出这个问题方面做得并不好,因此,我们可能不在同一个页面上。从源代码(一种更有效但可读性较差的变体)和文档中可以清楚地看到您所说的。我所追求的是“R x C 表” s.t. chi2_contingency 将产生与 sklearn.feature_selection.chi2 相同的结果。 您展示的示例可以解释为将此类表分解为要传递给scipy.stats.chisquare 的数组,这将产生相同的结果。但是,我有兴趣获得原始的 R x C 表。 @larsmans:对于标准列联表分析,您的 expected 数组不正确。预期频率数组必须与观察频率数组具有相同的边际总和(即行和列总和)。 @WarrenWeckesser chisquare docstring 建议不然,你是说错了吗? 对不起,我应该写一个更长的评论(或者根本没有评论!)。您的expectedsklearn.feature_selection.chi2 进行的计算是正确的。正如您所说,它没有对observed 列联表进行单一的 chi2 测试(当我说它对于“标准列联表分析”不正确时,我正在考虑这个测试)。它也没有对每列进行 2x2 列联表分析。我认为后一点是@tiao问题的本质。【参考方案2】:

考虑X 的列xsklearn.feature_selection.chi2 测试是否 x 为 1 的 y 值的频率与 y 的频率一致 全体人口。 (@larsman 的回答显示了如何使用 numpy 和 scipy 重现计算。)这与标准的 2x2 列联表不同 分析xy。在 2x2 列联表分析中,y 的频率 其中x 为 0 也有助于测试。

假设我们为xy 形成列联表:

    | y=0  y=1
----+---------
x=0 |  a    b
x=1 |  c    d

设 n = a + b + c + d。这是样本数(即与 len(x) 和 len(y) 相同)。

令 nx = c + d。这是1x 中出现的次数。

令 py1 = (b + d)/n。这是 y 为 1 的全部人口的比例。

sklearn.feature_selection.chi2 使用预期的对 [c, d] 执行 chi2 测试 值 [(1-py1)*nx, py1*nx]。这与标准列联表不同 分析 2x2 表。

这是一个极端的例子。假设 xy 的 2x2 列联表是

    |  y=0  y=1
----+----------
x=0 |   8    8
x=1 |  20  188

sklearn 计算得出的 chi2 分数为 1.58,p 值为 0.208。

scipy.stats.chi2_contingency 的列联表分析得出 chi2 得分为 18.6,p 值为 1.60e-5。

【讨论】:

这与 2x2 表的标准列联表分析不同。 这是我问题的本质;这不是标准的列联表分析吗?如果不是,它到底应该是什么(链接到文章/论文?)我认为这应该在文档中明确说明。

以上是关于Scikit-learn χ²(卡方)统计量和相应的列联表的主要内容,如果未能解决你的问题,请参考以下文章

请教高手如何在SPSS上做趋势卡方检验

SAS统计初学1-卡方检验

卡方分布

2×c列联表|多组比例简式|卡方检验|χ2检验与连续型资料假设检验

基于卡方分箱的评分卡建模

卡方检验详解分析与实例