
1. K-S检验是什么为什么你需要它当你拿到一批数据时第一件事往往是看看这些数据长什么样。比如你想知道它们是不是正态分布的——这在统计分析中特别常见因为很多统计方法都假设数据服从正态分布。这时候K-S检验Kolmogorov-Smirnov检验就派上用场了。K-S检验是一种非参数检验方法不需要你对数据的分布做任何假设。它的核心思想很简单比较你手头数据的经验分布函数也就是数据实际表现出来的分布样子和你想要检验的理论分布函数比如标准正态分布之间的最大差距。如果这个差距太大超过了某个临界值那就可以认为你的数据不太可能来自这个理论分布。举个例子假设你是一名数据分析师公司给了你一批用户购买金额的数据老板想知道这些数据是否符合正态分布。你可能会先画个直方图看看但直方图有时候会受到分组方式的影响看起来不太准。这时候K-S检验就能给你一个更客观的判断。2. K-S检验的原理详解2.1 正态分布基础知识要理解K-S检验首先得了解正态分布。正态分布也叫高斯分布它的概率密度函数是那个著名的钟形曲线。标准正态分布是均值为0、标准差为1的正态分布记作N(0,1)。任何正态分布X~N(μ,σ²)都可以通过一个简单的变换变成标准正态分布Z(X-μ)/σ。这个Z值告诉我们原始数据点X距离均值μ有多少个标准差σ。正态分布的累积分布函数(CDF)表示随机变量取值小于等于某个值的概率。对于标准正态分布这个函数记作Φ(x)它的数学表达式看起来有点吓人但在实际应用中我们通常用统计软件或查表来计算。2.2 K-S检验的计算步骤K-S检验的具体计算过程可以分为以下几个步骤计算描述性统计量先算出你数据的均值μ和标准差σ。这两个参数会用来构建理论正态分布。排序数据把你的数据从小到大排列。假设你有n个数据点排序后记为x₁, x₂, ..., xₙ。计算经验分布函数对于第i个数据点xᵢ经验分布函数的值F(i)(i1)/n。这里用(i1)而不是i是为了避免在最大数据点处经验分布函数达到1。计算理论分布函数对每个xᵢ计算它在理论分布比如正态分布下的累积概率CDF(i)。对于正态分布这可以通过误差函数erf来计算。找最大差距计算每个点处经验分布和理论分布的绝对差|F(i)-CDF(i)|然后找出所有这些差中的最大值D。做出判断将D值与临界值比较临界值通常查表得到或者用近似公式1.36/√n。如果D大于临界值就拒绝原假设即认为数据不来自该分布。3. Python实现K-S检验3.1 使用scipy库快速实现在Python中用scipy.stats模块可以非常方便地进行K-S检验。下面是一个完整的例子import numpy as np from scipy import stats import matplotlib.pyplot as plt # 生成一些测试数据 - 这里混合了正态分布和均匀分布的数据 np.random.seed(42) normal_data np.random.normal(loc0, scale1, size500) uniform_data np.random.uniform(low-3, high3, size300) mixed_data np.concatenate([normal_data, uniform_data]) # 使用scipy的kstest函数进行检验 def ks_test_demo(data): # 标准化数据 data (data - np.mean(data)) / np.std(data) # 执行K-S检验检验是否符合标准正态分布 ks_statistic, p_value stats.kstest(data, norm) print(fK-S统计量: {ks_statistic:.4f}) print(fP值: {p_value:.4f}) # 绘制经验CDF和理论CDF的对比图 plt.figure(figsize(10, 6)) plt.plot(np.sort(data), np.linspace(0, 1, len(data), endpointFalse), label经验CDF) plt.plot(np.sort(data), stats.norm.cdf(np.sort(data)), label理论正态CDF) plt.title(经验分布与理论正态分布比较) plt.xlabel(数据值) plt.ylabel(累积概率) plt.legend() plt.show() # 根据p值做出判断 alpha 0.05 if p_value alpha: print(拒绝原假设数据不服从正态分布) else: print(无法拒绝原假设数据可能服从正态分布) # 对混合数据执行检验 ks_test_demo(mixed_data)这段代码做了以下几件事生成了一些测试数据混合了正态分布和均匀分布标准化数据减去均值除以标准差调用scipy的kstest函数进行K-S检验绘制经验分布和理论分布的对比图根据p值做出统计判断3.2 从零实现K-S检验为了更深入理解K-S检验我们也可以自己从头实现它import numpy as np from scipy.special import erf import matplotlib.pyplot as plt def ks_test_manual(data): # 计算均值和标准差 mu np.mean(data) sigma np.std(data) # 标准化数据并排序 data_sorted np.sort(data) n len(data_sorted) # 计算经验CDF ecdf np.arange(1, n1) / n # 计算理论CDF标准正态 z_scores (data_sorted - mu) / sigma theoretical_cdf 0.5 * (1 erf(z_scores / np.sqrt(2))) # 计算D统计量 D np.max(np.abs(ecdf - theoretical_cdf)) # 计算临界值近似 critical_value 1.36 / np.sqrt(n) # 绘制比较图 plt.figure(figsize(10, 6)) plt.plot(data_sorted, ecdf, label经验CDF) plt.plot(data_sorted, theoretical_cdf, label理论正态CDF) plt.title(fK-S检验: D {D:.4f}, 临界值 {critical_value:.4f}) plt.xlabel(数据值) plt.ylabel(累积概率) plt.legend() plt.show() print(f计算结果:) print(f样本量: {n}) print(f均值: {mu:.4f}) print(f标准差: {sigma:.4f}) print(fD统计量: {D:.4f}) print(f临界值(α0.05): {critical_value:.4f}) if D critical_value: print(结论: 拒绝原假设 (数据不服从正态分布)) else: print(结论: 无法拒绝原假设 (数据可能服从正态分布)) # 使用同样的混合数据测试 ks_test_manual(mixed_data)这个手动实现的版本让我们清楚地看到了K-S检验的每一步计算过程。特别是绘制CDF对比图的部分可以直观地看到两条曲线在哪里差距最大——这个最大差距就是D统计量。4. K-S检验的常见问题与陷阱4.1 样本量对检验结果的影响K-S检验对样本量比较敏感。当样本量很大时即使数据与理论分布只有很小的偏差也可能导致统计显著的结果即很小的p值。这是因为随着样本量增加检验的power功效提高了能够检测到更小的差异。举个例子# 生成接近正态但不是完全正态的数据 near_normal_data np.random.normal(loc0, scale1, size10000) * 1.01 # 对小样本和大样本分别进行检验 print(小样本(100个数据点):) ks_test_demo(near_normal_data[:100]) print(\n大样本(10000个数据点):) ks_test_demo(near_normal_data)运行这个例子你会发现对于100个数据点K-S检验可能认为数据足够正态但对于10000个同样的数据检验可能会拒绝正态性假设因为微小的差异在大量数据下变得统计显著。4.2 参数估计的影响当我们使用K-S检验检验数据是否来自某个特定分布时如果这个分布的参数如正态分布的μ和σ是从数据中估计得到的这会影响检验的结果。这种情况下标准的K-S临界值表就不太适用了因为估计参数引入了额外的不确定性。解决方法是使用Lilliefors检验它是K-S检验的一个变体专门针对参数从数据中估计的情况特别是正态分布和指数分布。在Python中可以用statsmodels库实现from statsmodels.stats.diagnostic import lilliefors # 使用Lilliefors检验正态性 def lilliefors_test(data): lf_stat, p_value lilliefors(data) print(fLilliefors统计量: {lf_stat:.4f}) print(fP值: {p_value:.4f}) alpha 0.05 if p_value alpha: print(拒绝原假设数据不服从正态分布) else: print(无法拒绝原假设数据可能服从正态分布) # 测试我们的混合数据 lilliefors_test(mixed_data)4.3 与其他正态性检验的比较除了K-S检验还有其他常用的正态性检验方法如Shapiro-Wilk检验、Anderson-Darling检验等。这些检验在不同情况下各有优劣Shapiro-Wilk检验对小样本数据n50特别有效但对大样本数据计算量较大。Anderson-Darling检验对分布的尾部差异更敏感适合检验极端值的影响。DAgostinos K²检验基于偏度和峰度对对称但非正态的分布如均匀分布比较敏感。在Python中可以这样实现这些检验from scipy import stats def compare_tests(data): print( 不同正态性检验比较 ) # K-S检验 ks_stat, ks_p stats.kstest((data-np.mean(data))/np.std(data), norm) print(fK-S检验: 统计量{ks_stat:.4f}, p值{ks_p:.4f}) # Shapiro-Wilk检验 shapiro_stat, shapiro_p stats.shapiro(data) print(fShapiro-Wilk检验: 统计量{shapiro_stat:.4f}, p值{shapiro_p:.4f}) # Anderson-Darling检验 anderson_result stats.anderson(data, distnorm) print(fAnderson-Darling检验: 统计量{anderson_result.statistic:.4f}) print(临界值:, anderson_result.critical_values) print(显著性水平:, anderson_result.significance_level) # 对结果进行解释 alpha 0.05 tests { K-S: ks_p, Shapiro-Wilk: shapiro_p, Anderson-Darling: anderson_result.statistic anderson_result.critical_values[2] } print(\n检验结果汇总:) for name, result in tests.items(): if name ! Anderson-Darling: print(f{name}: {拒绝正态性 if result alpha else 不拒绝正态性}) else: print(f{name}: {拒绝正态性 if result else 不拒绝正态性}) # 比较不同检验方法 compare_tests(mixed_data)在实际应用中通常会结合多种检验方法并辅以图形化工具如Q-Q图来综合判断数据的正态性。