
spss 默认执行的是参数未知的正态性检验(如 lilliefors 修正版 ks 检验),而 scipy 的 `kstest` 默认检验数据是否服从**指定参数**(如标准正态分布)的分布,二者原假设和计算逻辑不同,导致统计量与 p 值显著差异。
在实际数据分析中,使用 Kolmogorov–Smirnov(KS)检验评估变量正态性时,若发现 SPSS 与 Python(如 SciPy)结果严重不一致(例如 SPSS 报告 KS 统计量为 0.190、p = 0.000,而 Python 返回 0.946、p ≈ 10⁻⁵⁵),这通常并非代码错误,而是源于检验前提的根本差异:
- ✅ SPSS 的“单样本 KS 检验”默认采用 Lilliefors 方法:即在零假设“数据来自某个正态分布”下,均值和标准差由样本估计,并使用专门校正后的临界值或蒙特卡洛模拟计算 p 值。这是一种更合理、更常用的正态性检验方式。
- ❌ SciPy 的 scipy.stats.kstest 默认执行经典 KS 检验:它严格检验“数据是否来自用户指定的精确分布”,例如 norm(loc=0, scale=1)(标准正态分布)。若直接将原始 age 或 log(age) 数据与标准正态 CDF 比较,由于样本均值远非 0、标准差远非 1,经验分布与理论分布必然严重偏离——导致统计量趋近于 1,p 值极小,该检验此时毫无统计意义。
正确做法:在 Python 中复现 SPSS 的 Lilliefors 风格检验
自 SciPy 1.8.0 起,推荐使用 scipy.stats.goodness_of_fit(需显式指定 statistic='ks')进行带参数估计的 KS 检验(即 Lilliefors 检验):
import numpy as np
import pandas as pd
from scipy import stats
# 构建数据(注意:原问题中列名误写为 'weight',实际应为 'height')
data = pd.DataFrame([
[8, 120], [8, 123], [8, 130], [8, 125], [10, 160], [9, 158], [8, 120], [7, 126],
[6, 98], [5, 97], [7, 115], [7, 120], [7, 118], [8, 117], [6, 97], [6, 99],
[9, 123], [10, 157], [10, 155], [9, 155], [9, 153], [5, 96], [7, 115], [6, 94],
[6, 94], [5, 87], [8, 117], [6, 96], [5, 97], [6, 91], [6, 88], [9, 149], [6, 94],
[8, 117], [10, 156], [10, 160], [6, 90], [6, 90], [7, 116], [5, 89], [6, 90], [7, 118], [10, 162]
], columns=['age', 'height'])
# 对 age 变量做 Lilliefors KS 检验(自动估计 mu/sigma)
res_age = stats.goodness_of_fit(stats.norm, data['age'], statistic='ks', random_state=42)
print(f"age — KS statistic: {res_age.statistic:.4f}, p-value: {res_age.pvalue:.4f}")
# 输出示例:KS statistic: 0.1902, p-value: 0.0002 (与 SPSS 高度一致)
# 同样检验 height(可选对数变换,但需保持与 SPSS 一致)
res_height = stats.goodness_of_fit(stats.norm, data['height'], statistic='ks', random_state=42)
print(f"height — KS statistic: {res_height.statistic:.4f}, p-value: {res_height.pvalue:.4f}")⚠️ 注意事项:goodness_of_fit 默认通过蒙特卡洛模拟计算 p 值(n_sim=10000),结果存在微小随机波动,建议固定 random_state 保证可重现性;若使用旧版 SciPy(
更优替代:优先使用 Shapiro-Wilk 检验
对于小样本(n
# 推荐首选
w_stat_age, w_p_age = stats.shapiro(data['age'])
print(f"Shapiro-Wilk for age: W={w_stat_age:.4f}, p={w_p_age:.4f}")
总结
| 工具 | 检验类型 | 参数估计? | 适用场景 |
|---|---|---|---|
| scipy.stats.kstest(data, 'norm') | 经典 KS | ❌(强制 loc=0, scale=1) | 仅当理论分布参数已知且精确时 |
| scipy.stats.goodness_of_fit(norm, data, 'ks') | Lilliefors KS | ✅(自动拟合 μ, σ) | 等效 SPSS 单样本 KS,推荐用于正态性初筛 |
| scipy.stats.shapiro(data) | Shapiro-Wilk | ✅(内置最优估计) | 小样本正态性金标准,统计力最强 |
因此,当追求与 SPSS 结果可比性时,请放弃直接使用 kstest 与标准正态比较,转而采用 goodness_of_fit(..., statistic='ks') 或更稳健的 shapiro()。理解检验背后的统计假设,比盲目复现数值更重要。
立即学习“Python免费学习笔记(深入)”;










