1. 卡方检验到底是一个什么样的检验
一直有人问我,做数据分析时,面对一堆计数数据,比如“男性用户里有多少人点了A按钮”“不同年龄段的人喜不喜欢这个包装”,到底该用什么方法来判断差异有没有统计学意义。我的回答通常是同一个:先试试卡方检验。卡方检验(Chi-square test)是统计分析里使用频率最高、适用范围较广的非参数检验方法之一,专门用来处理分类变量、频数表和列联表数据,判断观测到的频数和理论上应该出现的频数之间是否存在显著差异。
这个检验的好用之处在于,它对数据的要求相对宽松。你不需要像做t检验、ANOVA那样,先去验证数据是否服从正态分布,也不需要纠结方差齐不齐。只要数据是计数的、类别是互斥的,样本量大体够用,就可以拿它来检验。所以它特别适合做问卷分析、用户行为研究、医学临床数据、市场调研这类场景,几乎可以说是分类数据分析的默认选项。
这篇文章我就结合自己平时跑数据的经验,把这套方法彻底讲清楚。包括它为什么叫非参数检验、卡方统计量到底在算什么、实操中怎么计算、怎么用Python和SPSS快速出结果,以及哪些坑是最容易踩的。无论你是刚开始学统计的学生,还是工作中需要分析问卷和业务数据的从业者,照着这篇文章走一遍,应该都能把卡方检验用明白。
1.1 为什么它被归入“非参数检验”
很多初学者第一次看到“非参数检验”这个词,会觉得很玄。其实它的意思很简单:这类检验方法不依赖总体分布的具体形态,不针对某个特定的参数(比如总体均值μ、总体方差σ²)做推断。
对比一下就清楚了。t检验属于典型的参数检验,它的逻辑是假设样本来自一个正态分布的总体,然后用样本均值去推断总体均值。如果总体不是正态的,或者数据是计数型的,t检验的前提直接就崩了。而卡方检验处理的是频数数据,它关心的是“落在每个类别里的个数”和“理论上应该有多少个”是否吻合,整个过程根本不涉及均值、标准差这些参数。所以它被归入非参数检验,本质上是因为它对总体分布没有任何先验假设,只做基于频数的拟合优度判断。
还有一个很实际的原因:很多真实的分类数据,比如“是否购买”“满意/不满意”“男/女”,根本无法用均值来描述。你不能说“这批用户的平均性别是1.2”,所以这类数据的显著性检验必须走非参数路线,卡方检验几乎就是为这种场景量身定制的。理解了这一点,你就明白为什么卡方检验在交叉列表分析中的地位那么稳固。
1.2 卡方检验的两个主战场
卡方检验不是“一个”检验,它是一族检验方法。但在日常应用里,你真正会频繁用到的其实就两个场景。
第一个是拟合优度检验(Goodness of Fit),面单变量数据。它的核心问题是:某个变量的观测频数分布,和理论分布是否一致。举个例子,你怀疑一个骰子被做过手脚,于是掷了240次记录点数,如果骰子是公平的,每个点数理论上应该出现40次。但实际记录不一定正好是40,那这个偏差是随机波动还是真的有问题?这就是拟合优度检验要回答的问题。
第二个是独立性检验(Test of Independence),面双变量数据,也是大家最常遇到的场景。它研究的是两个分类变量之间是否存在关联,比如“性别”和“是否购买会员”有没有关系、“教育水平”和“阅读习惯”有没有关系。最后会把数据整理成一张行列表(也叫列联表),然后检验行变量和列变量是相互独立,还是存在某种依赖。
顺带提一句,还有第三个场景叫做“一致性检验”,用来判断多个总体在同一分类变量上的分布是否一致,比如两个城市的用户在不同手机品牌上的选择分布是否相同。它的计算方法和独立性检验几乎一样,很多教材里干脆不分那么细。你在实际工作中,只要先想清楚“我是想看一个变量是否符合某种分布”,还是“我想看两个变量是否相关”,你就知道自己该用哪一个了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 卡方统计量的计算逻辑,看懂公式才算入门
理解了应用场景,接下来得看公式。因为卡方检验的所有结论,归根结底都来自一个统计量的计算。这个统计量本身并不复杂,但如果你只看公式而不理解它在表达什么,一旦遇到稍微变形的问题就容易出错。
2.1 从公式看原理:偏差越大,越不靠谱
卡方统计量的基本公式长这样:
χ² = Σ (O - E)² / E
其中O是观测频数(Observed),就是你从数据里实际数出来的个数;E是期望频数(Expected),是在“原假设成立”的前提下,理论上这个单元格里应该出现的个数。求和是对表中所有单元格进行的。
这个公式其实表达了一个特别直觉的思想:把每个格子里的“实际值”和“期望值”的差距算出来,平方之后除以期望值,最后把所有格子汇总。为什么要平方?因为有的格子实际值比期望值大,有的比期望值小,直接相加会正负抵消,平方可以让正负偏差都变成正向贡献。为什么要除以期望值?因为同样偏差5个,放在期望值10的格子里和放在期望值500的格子里,意义是完全不同的。除以期望值相当于做了一个标准化,让偏差有统一的可比尺度。
我举个生活中的例子。你如果预期一家奶茶店周二能卖出100杯,结果实际卖了105杯,你不会觉得异常。但如果你预期它周二只能卖5杯,却实际卖了10杯,这个偏差就值得注意了。同样是差了5杯,分母不同,代表的异常程度完全不同。卡方公式里的除以E,做的就是这件事:给“偏差”按它本来的基数打一个比例分。
然后问题就来了:这个统计量多大才算“显著”?这就需要把它放到卡方分布里去比较。卡方分布的形状由自由度决定,自由度越大,分布越往右偏、越平缓。算出来的χ²值如果落在分布右尾的极端区域,对应p值小于0.05或0.01,就说明观测数据和原假设的预期相去甚远,原假设很可能不成立。
2.2 期望频数与自由度:两个最容易翻车的地方
期望频数是整个计算里最容易出错的地方。先明确它的逻辑:期望频数不是凭空设的,而是在“原假设成立”的前提下,理论上算出来的数值。
在拟合优度检验里,期望频数通常来自某个理论分布。比如检测骰子是否公平,原假设是每个点数出现概率都等于1/6,所以总掷数乘以1/6就是每个点数的期望频数。在独立性检验里,原假设是行变量与列变量无关,这时候某个单元格的期望频数就等于“所在行的合计乘以所在列的合计,再除以总样本量”。你可以这么理解:如果两个变量独立,那么一个样本既落在第i行又落在第j列的概率,就等于它落在第i行的概率乘以落在第j列的概率,而这两个概率各自的估计值就是行合计除以总数、列合计除以总数,乘起来再乘以总数,就得到:
E = (行合计 × 列合计) / 总样本量
这是一个看起来简单、却非常容易算错的地方。很多人喜欢凭感觉估一个期望值,结果算出来的卡方统计量完全不对。更稳妥的方式是,在Excel或Python里先把行列合计算好,再逐个单元格填公式,别手动心算。
自由度同样是关键。拟合优度检验的自由度是k-1,k是类别数。为什么减1?因为总频数是固定的,如果你知道了前k-1个类别的频数,第k个类别的频数就等于总数减去前面这些,不能再自由变动了。独立性检验的自由度则是(行数-1)×(列数-1)。还是同样的逻辑,行合计和列合计都固定时,行表中剩下的可自由变动的单元格数就是这么多。3×4的表格,自由度就是(3-1)×(4-1)=6。
很多人在计算时把自由度搞错,会导致查表或者看p值的时候对不上号。我的经验是,自由度这个东西最好写成公式贴在手边:拟合优度是类别数减1,独立性检验是行列各减1再相乘。记住了这个,基本不会错。
3. 完整实操:从数据到结论的每一步
讲完了原理,我们进入实操环节。我会用两组示例分别演示拟合优度检验和独立性检验的完整计算流程,再手把手教你用Python跑出来,最后简单说一下SPSS这种点鼠标工具的用法,保证你拿到手就能跟着操作。
3.1 单变量场景:掷骰子数据的拟合优度检验
假设我要检验一颗骰子是不是公平的。我掷了240次,记录每个点数出现的次数,结果如下:
| 点数 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 观测频数O | 35 | 48 | 41 | 36 | 42 | 38 |
如果骰子公平,每个点数出现的概率应该是1/6,240次里每个点数的期望频数E = 240×(1/6) = 40。
现在计算每个点数的 (O-E)²/E:
- 点数1:(35-40)²/40 = 25/40 = 0.625
- 点数2:(48-40)²/40 = 64/40 = 1.6
- 点数3:(41-40)²/40 = 1/40 = 0.025
- 点数4:(36-40)²/40 = 16/40 = 0.4
- 点数5:(42-40)²/40 = 4/40 = 0.1
- 点数6:(38-40)²/40 = 4/40 = 0.1
把这些加起来,χ² = 0.625 + 1.6 + 0.025 + 0.4 + 0.1 + 0.1 = 2.85。
自由度为6-1=5。查卡方分布表,显著性水平0.05、自由度5对应的临界值是11.07。2.85远小于11.07,p值大约在0.72左右,远大于0.05。结论是:没有充分证据说明这个骰子不公平,观测到的偏差很可能只是随机波动。整个过程手算都能完成,只要按流程来,基本不会出大问题。
3.2 双变量场景:性别与饮品偏好的独立性检验
接下来是独立性检验,也是实际业务中最常见的一类。假设我在一家咖啡店做了用户调研,想判断性别和“偏好咖啡还是茶”之间有没有关联。我收集了200份有效问卷,整理成一张2×2列联表:
| 偏好咖啡 | 偏好茶 | 行合计 | |
|---|---|---|---|
| 男性 | 60 | 40 | 100 |
| 女性 | 35 | 65 | 100 |
| 列合计 | 95 | 105 | 200 |
原假设是:性别和饮品偏好相互独立。在这个假设下,每个单元格的期望频数就用“行合计×列合计/总数”来算。
- 男性×咖啡:E = 100×95/200 = 47.5
- 男性×茶:E = 100×105/200 = 52.5
- 女性×咖啡:E = 100×95/200 = 47.5
- 女性×茶:E = 100×105/200 = 52.5
接着算每个格子的贡献:
- 男性×咖啡:(60-47.5)²/47.5 = 156.25/47.5 ≈ 3.289
- 男性×茶:(40-52.5)²/52.5 = 156.25/52.5 ≈ 2.976
- 女性×咖啡:(35-47.5)²/47.5 = 156.25/47.5 ≈ 3.289
- 女性×茶:(65-52.5)²/52.5 = 156.25/52.5 ≈ 2.976
χ² = 3.289 + 2.976 + 3.289 + 2.976 ≈ 12.53。
自由度为(2-1)×(2-1)=1。查表可知,自由度1、显著性水平0.05的临界值是3.84,0.001水平的临界值是10.83。12.53远大于这个值,p值小于0.001。结论很清晰:性别和饮品偏好之间存在显著关联,男性更倾向于咖啡,女性更倾向于茶。
这个例子也顺带说明了一个很容易被忽略的点:卡方检验本身只能告诉你“有关系”或“没关关系”,不能直接告诉你“关系长什么样”。你想知道具体是哪类人的偏好更突出,得去看每个格子的残差或者百分比分布,这个留在后面“常见问题”部分再细说。
3.3 用Python快速实现两种检验
手算两三次是为了理解原理,真正干活的时候当然要用工具。Python里SciPy库封装好了卡方检验的函数,用起来非常顺手。
拟合优度检验用scipy.stats.chisquare,直接把观测频数和期望频数传进去:
python复制from scipy.stats import chisquare
observed = [35, 48, 41, 36, 42, 38]
expected = [40, 40, 40, 40, 40, 40]
chi2_stat, p_value = chisquare(observed, f_exp=expected)
print(f"卡方统计量: {chi2_stat:.4f}")
print(f"p值: {p_value:.4f}")
输出结果会给出χ²=2.85,p值≈0.7237,和我们手算结果一致。注意,当你的期望频数是均匀分布(每个类别概率相同)时,f_exp可以不传,函数默认按均匀分布计算。但如果是非均匀的,比如你要检验是否符合“30%、20%、50%”的分布,就一定要传f_exp参数,格式可以是比例列表,函数内部会乘以总频数。
独立性检验用scipy.stats.chi2_contingency,传入的是列联表矩阵:
python复制import numpy as np
from scipy.stats import chi2_contingency
table = np.array([[60, 40],
[35, 65]])
chi2_stat, p_value, dof, expected = chi2_contingency(table, correction=False)
print(f"卡方统计量: {chi2_stat:.4f}")
print(f"p值: {p_value:.6f}")
print(f"自由度: {dof}")
print("期望频数矩阵:")
print(expected)
这里有个参数要特别提醒:correction。它控制的是“连续性校正”开关,默认值是True。对于2×2列联表,Yates连续性校正会在每个格子的偏差上减掉0.5再平方,这样能让卡方统计量更保守一些,避免因为离散数据而错误地拒绝原假设。在早期的统计教材里,当单元格期望频数低于5时是强制建议校正的。但现在的统计学界对这个问题有争议,后面我会专门展开说。如果你就是想在Python里复现手算的结果,可以先设correction=False;如果是正式报告且面对2×2表,我建议你把两种结果都看一眼,差异大的时候再想想用哪个更合理。
chi2_contingency还顺带返回期望频数矩阵,这很方便,你不需要自己手动算期望值,直接看矩阵里有没有小于5的单元格,用来排查下面章节会提到的小样本问题。
3.4 SPSS下拉菜单也能跑,没必要手算
如果你的日常工具是SPSS,操作就更简单了。数据录入时有三种常见格式:一是原始数据,每一行是一条记录,里面有一个“性别”列和一个“偏好”列;二是频数格式,每行代表一个单元格,包含分类值和一个“频数”变量。如果是频数格式,记得先通过“Data → Weight Cases”把频数变量设为权重,然后再进行分析。
点击菜单“Analyze → Descriptive Statistics → Crosstabs”,把“性别”选进行变量,“偏好”选入列变量。然后点击“Statistics”按钮,勾选“Chi-square”,其他选项像“Correlations”“Phi and Cramer's V”按需选择。点击“Cells”按钮,在“Counts”里可以勾选“Observed”“Expected”,这样输出表里会直接显示每个格子的观测频数和期望频数,方便你核对。点击OK之后,输出窗口会显示一张“Chi-Square Tests”表格,里面有好几行,你需要重点关注的是“Pearson Chi-Square”那一行,它的值、自由度和显著性(双侧)就是你的核心结果。
表格里还有一行叫做“Continuity Correction”,这是SPSS给2×2表自动加的Yates校正版本。如果案例数据是2×2表,建议把这一行也看上几眼,有时候校正前后的p值差异还挺明显,特别是在边缘显著的时候。
Excel其实也能做。基础做法是把行列合计算出来后,用CHISQ.TEST函数传入实际频数区域和期望频数区域,直接得到p值;如果要拿卡方统计量和自由度,可以用CHISQ.INV.RT函数反推,但操作上不如SPSS直观。总之工具选择很自由,你的需求是“看结论”还是“出报告”,决定你用哪个更顺手。
4. 卡方检验最容易踩的坑与排查技巧
卡方检验虽然简单,但我在实际项目和答疑里见过太多翻车案例。很多结论不是检验本身算错了,而是使用的场景不对或者忽略了一些关键前提。这一节我把最有价值的几个坑集中梳理一下,相当于一份速查表。
4.1 期望频数太小会导致结论不可信
卡方统计量的分布是近似服从卡方分布的。这句话的重点在“近似”二字。当样本量足够大、每个单元格的期望频数都不太小时,近似效果很好;但如果你某个格子的期望频数太小,近似效果就会变差,算出来的p值可能失真,结论也就不靠谱。
传统经验法则是:如果列联表里有20%以上的单元格期望频数小于5,或者存在任何一个期望频数小于1,那么卡方检验的结果就不可靠。这个规则在大多数教材里都写,实践中依然非常实用。2×2表由于总共只有4个格子,判断标准会更严格:只要有一个格子的期望频数小于5,就要警惕。
遇到这种情况怎么办?有几个常规处理方式:第一种,也是最常用的,是改用Fisher精确检验(Fisher's Exact Test)。它在小样本下不依赖近似分布,而是使用超几何分布精确计算概率,尤其适合2×2表。Python里可以用scipy.stats.fisher_exact,SPSS的Crosstabs里勾选“Exact”也可以实现。第二种,是合并相邻类别。如果某个类别样本太少,比如年龄段“80岁以上”只有3个人,可以考虑把它和“70-79岁”合并成一个“70岁及以上”的类别。但合并类别必须有业务逻辑依据,不能为了凑数强行合并。第三种,是增加样本量。听起来像废话,但在设计问卷或实验时提前估算样本量,确实能从根本上避开这个问题。我的建议是,处理问卷数据时,重点看一下期望频数矩阵而不是只盯着观测频数,问题往往比你想的隐蔽。
4.2 2×2表格里别忘了连续性校正
卡方分布是连续分布,而你的频数数据是离散的整数点。当样本量较小时,用连续分布去近似离散数据,会导致p值被系统性低估,也就是说你更容易得到“显著”的结论,但实际上可能并不显著。Yates连续性校正的思路,是在每个格子的偏差绝对值上先减去0.5再平方,向“不显著”方向拉一把,从而校正这种偏乐观的倾向。
对2×2表来说,是否使用连续性校正是一个经典的统计学争议。有的学者认为校正后过于保守,会让真实差异被掩盖;有的学者则坚持要校正,尤其在期望频数较低时。我的做法是:样本量很小(比如总样本量小于40,或者有期望频数小于5)时,直接把Fisher精确检验作为主要结果;当样本量一般但仍是2×2表时,我会上报Pearson卡方和校正后的结果各一份,让读者自己判断;如果总样本量很大,校正与否的差异微乎其微,就不用纠结,直接报告Pearson卡方。记住一个原则:连续性校正只适用于2×2表,超过2×2的表格不需要考虑它。
4.3 样本量一大,卡方检验“啥都显著”
这个问题在业务数据分析里太常见了。大样本条件下,卡方检验几乎对任何微小的偏差都极其敏感。你可能从几十万用户的数据里发现,男性用户和女性用户在某个按钮点击率上只差了0.3个百分点,卡方检验的结果却显示p值小于0.001,极其显著。但0.3个百分点的差距,在商业上真的有价值吗?大概率没有。
这就是统计学显著和实际显著的根本区别。卡方检验只问你“这个偏差是不是超过了随机波动的范围”,样本量越大,随机波动的范围越小,哪怕偏差再小,也可能被判定为“不随机”。所以当你手头有几万甚至几十万条数据时,不要在p值显著后就兴高采烈地下结论,先算一下效应量。对卡方检验来说,最常用的效应量是Cramér's V,公式是:
V = √(χ² / (n × min(r-1, c-1)))
其中n是总样本量,r是行数,c是列数,min取两者减一后的较小值。V的范围在0到1之间,一般认为0.1以下效应微弱,0.1到0.3为小效应,0.3到0.5为中等效应,大于0.5为大效应。这个指标和样本量无关,能真正反映关联的强度。Python里可以用scipy.stats.contingency.associations或者自己几行代码算出来,SPSS在交叉表的统计选项里也会给出Phi和Cramér's V值。我强烈建议,任何正式报告只要展示了卡方检验的p值,就同时写上Cramér's V,这才是完整的结论。
4.4 有“显著”还不够,得往下拆单元格
卡方检验显著,只能告诉你“行列变量之间有关系”,但具体是哪个类别组合贡献了最大偏差,它不会直接告诉你。很多时候你需要进一步定位。
解决的办法是看每个单元格对卡方统计量的贡献度,或者看标准化残差。标准化残差的计算方式是(O-E)/√E,也就是每个格子偏差与期望值标准差的比值。通常我们把标准化残差绝对值大于2作为“这个格子的观测频数与期望频数差异较大”的经验阈值。再看我前面那个性别与饮品偏好的例子,四个格子的标准化残差分别是:(60-47.5)/√47.5≈1.81、(40-52.5)/√52.5≈-1.72、(35-47.5)/√47.5≈-1.81、(65-52.5)/√52.5≈1.72。虽然已经达到了大约0.05的边际水平,但因为样本量有限,单个格子的残差还没有特别夸张。如果样本量放大很多,残差就会更明显。实操时,我会把标准化残差和行百分比放在一起看,这样就能很直白地讲出“男性明显偏向咖啡,女性明显偏向茶”这类结论。
如果不仅仅是2×2表,而是3×4甚至更大的表,多重比较的问题也要考虑。当你对多个子表逐对做卡方检验时,犯第一类错误(本来没差异却判断为有差异)的概率会累积。一种简单的处理方式是Bonferroni校正:如果你要做m次比较,那么每次比较的显著性水平就用0.05/m。比如三组两两比较共3次,那每次的显著性水平就要设在0.017左右,卡方临界值也会相应变大。这种方法容易理解、执行简单,代价是会让检验变得更保守。如果你对统计功效要求很高,可以进一步了解Benjamini-Hochberg的FDR控制方法,但大多数业务场景下Bonferroni已经够用了。
再补充一条很容易被忽略但很实用的心得:卡方检验对“类别”的定义很敏感。如果两个类别之间的界限本来就不清晰,比如“偶尔喝咖啡”和“经常喝咖啡”这种主观分类,换一个人来分类可能结果就变了。在做问卷设计时尽量把选项定义清楚,避免模糊选项。分类方式如果变了,整个卡方分析的结论可能都要推翻。
4.5 卡方检验不能说明因果,它只是相关关系的侦探
这一点我必须单独用一个小节强调,因为这是最容易被误解的地方。卡方检验得到一个显著结果,只说明两个变量之间存在统计关联,并不能告诉你“是谁导致了谁”。性别和饮品偏好相关,可能是性别带来的生理偏好,也可能是社会文化因素导致的差异,卡方检验本身对此无能为力。
在实际工作中,我见过不少人拿着一份显著结果,就写“饮用咖啡会导致消费金额上升”之类的商业报告,这已经超出了卡方检验能支持的结论范围。要判断因果关系,你需要的是随机对照实验、设计良好的纵向研究加上更复杂的因果推断方法。卡方检验的价值在于“发现线索”,它的定位是探索性分析工具,而不是定案工具。
另一个常见的相关误解是,当两个变量都取多个类别时,显著性结论不能告诉你“整体相关等于强相关”。一张非常大的表,即便卡方值高度显著,Cramér's V也可能只有0.08,实际关联弱得可怜。所以,显著性、效应量、样本量这三个指标永远要放在一起看,缺一不可。
末尾的几句实在话
关于卡方检验,我的经验是:它真的不难,但也没有很多人想的那么“无脑可用”。我最早自己用的时候,因为没留意期望频数过低的问题,直接拿一组小样本问卷数据跑了个3×4的卡方检验,出了一个看似非常漂亮的显著结果,后来被审稿人一句话点醒,回过去看期望频数矩阵才发现有四个格子小于1,整个结论都站不住脚。从那以后,我每跑一次卡方分析都会固定看三样东西:期望频数矩阵、标准化残差和效应量。建议你也把这个流程养成习惯,真的能少走很多弯路。另外,如果你在报表里要用卡方检验,最好把样本量、自由度、卡方值和p值全部写清楚,这是学术规范和业务报告的共同要求,缺一个都显得不专业。希望这篇内容对你有帮助,如果你在用的是别的工具或者遇到特别的数据形态,欢迎一起交流。
