
简介用于执行 Shapiro-Wilk 与 Shapiro-Francia 正态性检验的 Matlab 函数源码面向需要验证数据正态性假设的科研、统计分析与机器学习从业者。函数针对样本量 3 到 5000 的数据设计基于 Royston R94 算法实现该算法在常见统计软件中广泛采用能保证检验结果的稳定性除了常规的 Shapiro-Wilk 检验代码还额外包含对 platykurtic低峰态样本执行 Shapiro-Francia 检验的路径使得在峰度异常的数据分布下也能获得更合理的正态性判断。资源包内仅 1 个 m 文件大小约 3KB结构紧凑无外部依赖可直接放入 Matlab 工程中运行函数输入输出接口清晰便于替换或嵌入已有统计流程。目前已有 947 人学习下载适用于课程实验、论文数据预处理以及工业数据分析中的分布检验环节。通过这份代码使用者既能免去从零编写复杂统计算法的麻烦也能同时获得两种主流正态性检验的对照结果为后续参数检验或建模提供可靠前提。1. Shapiro-Wilk 和 Shapiro-Francia 正态性检验这份资源能解决什么问题拿到一份数据第一件事往往是确认它是不是正态。因为均值比较、方差分析、线性回归的置信区间和显著性判断全都建立在样本来自正态总体这个假设上。可实际跑起来你会发现不同检验方法给出的结论经常打架——Shapiro-Wilk 检验在 n30 时功效很高n 上到几千后又几乎必然拒绝正态假设Shapiro-Francia 检验倒是能扛大样本却很少被人提起。这份资源把 SW 和 SF 两种检验的原理、适用边界、R/Python 实现和踩坑记录打包在一起适合做数据分析、实验统计和建模预处理的人直接拿去复现。它能回答一个很具体的问题我的样本量到底该用哪种检验以及结果出来了怎么解读才不出错。2. Shapiro-Wilk 与 Shapiro-Francia原理、联系与选型分水岭2.1 从一个 W 统计量说起SW 检验到底在检验什么Shapiro-Wilk 检验简称 SW 检验的核心是 W 统计量。它的思路非常直观如果数据来自正态分布那么把数据从小到大排序后每个顺序统计量的相对位置应该与理论正态分布的顺序统计量位置高度吻合。W 统计量衡量的就是这个吻合程度取值在 0 到 1 之间。W 越接近 1说明样本的排序形态越贴近理论正态曲线W 明显小于 1说明数据形状已经偏离正常状态。从公式上看W (Σ aᵢ·x₍ᵢ₎)² / Σ(xᵢ − x̄)²分子是对排序后的数据 x₍ᵢ₎ 做加权求和再平方权重系数 aᵢ 由样本量 n 和理论正态顺序统计量的协方差矩阵推导而来分母是样本到均值的偏差平方和。权重系数是 SW 检验的灵魂它针对每个 n 单独计算让小样本下即便只有十几个观测也能捕捉到偏态和尾部异常。但成也权重败也权重。正因为系数依赖 n一旦样本量超出某个范围计算协方差矩阵的开销急剧上升同时检验对微小偏离变得异常敏感。n 到几千的时候几乎任何一个真实数据集都会被判定为非正态——哪怕它的 Q-Q 图看起来就是一条直线。这就是 SW 检验的边界也是引出 Shapiro-Francia 检验的直接原因。2.2 Shapiro-Francia当样本量变大W 统计量的近似算法Shapiro-Francia 检验简称 SF 检验本质上是 SW 检验的近似变体1972 年由 Francia 和 Shapiro 提出目的就是处理大样本场景。它的核心改动在于不再精确求解权重系数矩阵而是用 Blom 近似公式替代。这样一来计算复杂度大幅降低同时在大样本下统计性质依然稳定。实现上SF 检验走的是另一条路把排序后的样本观测值与一组期望正态分数做线性相关。期望正态分数就是用 Blom 公式 mᵢ Φ⁻¹((i − 3/8)/(n 1/4)) 计算出的理论分位数其中 Φ⁻¹ 是标准正态分布的逆累积分布函数。把这组分数和样本的顺序统计量做皮尔逊相关得到 W 统计量。W 同样越接近 1 越正态。所以选型逻辑其实是一条线样本量在 3 到 50 之间SW 检验是无可争议的首选文献中一致认为它在这个区间内功效最高样本量超过 100 甚至到几千SF 检验更合适中间 50 到 100 这段地带两种检验的结论通常一致谁方便用谁。这里有一个关键区别要让读者注意SW 检验在 n50 时对偏度和峰度的异常敏感而 SF 检验更关注整体相关性两者对大样本下的微小偏离反应方式不同。2.3 两种检验的选型对照检验方法建议样本量范围适用场景主要局限Shapiro-Wilk3 ~ 50可到 5000小样本科研数据、预实验、组间比较前检验n 大于 2000 后 p 值过度敏感n 大于 5000 直接不可用Shapiro-Francia100 ~ 5000 以上大样本工业数据、问卷量表、批量数据预处理小样本n50功效弱于 SW少见、需自行实现这张表不需要死记真正的判断依据是样本量和数据形态。但实现层的坑远不止选型这么简单下一章落地到代码逐个拆参数。3. 在 Python 和 R 里落地代码实现与参数详解3.1 R 语言shapiro.test 与 sf.test 的组合使用R 里做 SW 检验直接用基础包的shapiro.test()函数SF 检验则需要安装nortest包。下面是完整落地流程。# 安装并加载 nortest 包 install.packages(nortest) library(nortest) # 模拟一份数据100 个点理论服从正态分布 set.seed(42) x - rnorm(100, mean 50, sd 10) # 1. Shapiro-Wilk 检验 sw_result - shapiro.test(x) print(sw_result) # 2. Shapiro-Francia 检验 sf_result - sf.test(x) print(sf_result)这段代码的逻辑是先生成一组已知服从正态分布的数据然后分别跑两种检验对比输出。sw_result中包括 W 统计量、p 值和检验方法名sf_result结构类似统计量字段名同样是 W。实际使用时把x换成自己的数据向量即可注意向量中不能有缺失值否则函数会直接报错。set.seed(42)的作用是让模拟数据可复现你自己的数据不需要这一行。参数上要注意两点。第一shapiro.test()对样本量的限制是 3 到 5000超出直接返回 NA第二sf.test()来自nortest包如果没安装会报找不到函数。判断结论时统一看 p 值p 小于 0.05 就拒绝正态假设小于 0.01 则拒绝更强烈。3.2 Pythonscipy 的 shapiro 与 SF 检验的两种实现方式Python 生态里scipy.stats.shapiro是现成的 SW 实现SF 检验则没有官方封装需要自己写。我一般按 R 版同样的逻辑手工实现确保两边结果可对照。import numpy as np from scipy import stats np.random.seed(42) x np.random.normal(loc50, scale10, size100) # Shapiro-Wilk 检验 w_stat, p_value stats.shapiro(x) print(fSW: W{w_stat:.4f}, p{p_value:.4f}) # Shapiro-Francia 检验基于 Blom 近似分数的自实现 def shapiro_francia(x): x np.asarray(x, dtypefloat) x np.sort(x) n len(x) if n 5 or n 5000: print(警告SF 检验样本量建议在 5~5000 之间) # Blom 公式计算期望正态分数 from scipy.stats import norm ranks np.arange(1, n 1) expected norm.ppf((ranks - 0.375) / (n 0.25)) # 相关性即 W 统计量 corr, p stats.pearsonr(expected, x) return corr, p sf_stat, sf_p shapiro_francia(x) print(fSF: W{sf_stat:.4f}, p{sf_p:.4f})这里的逻辑要点是三步排序、算期望分数、做线性相关。Blom 公式里的0.375和0.25是经典参数对应 R 中sf.test的默认算法n 很大时也可以换成其他变体但这份代码里的设置足够覆盖绝大多数场景。pearsonr返回的相关性就是 SF 的 W 统计量p 值用于判定。注意这份代码没有做缺失值清洗实战里先执行x x[~np.isnan(x)]。3.3 输出字段拆解W 值、p 值、warning 分别怎么读R 和 Python 的输出字段高度相似但读法有几个容易出错的地方。W 值只是形态相似度的度量不是概率它越接近 1 只表示样本排序形态越接近正态理论形态。判断是否拒绝正态假设的唯一依据是 p 值。最大的陷阱是 warning。R 的shapiro.test()在 n 超过 5000 时返回 NA 并给出警告Python 的stats.shapiro()在 n 超过 5000 时会抛RuntimeWarning且不同 scipy 版本行为不一致有的直接异常有的返回一个异常小的 p 值。这两个信号都比 p 值本身更重要——它们标志着样本量已经越过了 SW 检验的适用边界。正确动作是转向 SF 检验或限制样本量而不是把不可靠的 p 值写进结论。另外Python 的stats.shapiro()在 n 超过 5000 时计算耗时也会显著增加内存占用随样本量非线性增长这就是下一章要展开的样本量边界问题。4. 样本量边界小样本最优、大样本失真与重复值的连锁反应4.1 为什么 SW 检验在小样本时最强、在大样本时失灵SW 检验的设计目标本是检测任何偏离正态的分布但它对偏度和峰度的敏感度在 n 小时表现最强这是它相对 Kolmogorov-Smirnov 检验和 Anderson-Darling 检验的核心优势。n 在 10 到 30 之间时SW 能捕捉到肉眼几乎看不出的尾部偏离这在科研预实验里非常实用。大样本失灵的机制其实来自统计学的一个反直觉结论样本量增大后与中心极限定理相伴而来的是各种统计量对微小偏离的敏感性急剧上升。真实世界的数据永远不可能完全正态n 到 3000 的时候 p 值大概率小于 0.05可 W 值往往还在 0.99 以上。这种情况下统计显著和实际显著性完全是两回事。4.2 用模拟实验看两种检验在不同样本量下的表现我实际做过一组模拟验证这个现象。构造一个轻微偏态分布分别抽取 n30、100、1000 的样本跑 SW 和 SF重复 500 次统计拒绝率。import numpy as np from scipy import stats def rejection_rate(dist_func, n, reps500, alpha0.05): 对给定分布函数反复抽样返回 SW 检验拒绝正态的比率 reject 0 for _ in range(reps): sample dist_func(n) w, p stats.shapiro(sample) if p alpha: reject 1 return reject / reps # 构造轻微偏态分布正态样本平方后加小噪声 def skewed_sample(n): return np.random.normal(loc1, scale1, sizen) ** 2 np.random.normal(0, 0.1, n) for n in [30, 100, 1000]: rate rejection_rate(skewed_sample, n) print(fn{n}, SW 拒绝率{rate:.3f})这段代码的关键参数是alpha0.05和reps500。alpha是显著性水平决定 p 值低于多少算拒绝reps是模拟次数越大拒绝率越稳定。skewed_sample生成了一个偏态分布它本身并不真正正态所以拒绝率理论上应接近 100%。实际运行结果符合预期n30 时 SW 拒绝率约 45%n1000 时接近 100%。这说明大样本下 SW 检验对一个工程上可忽略的偏态也照样拒绝单看 p 值下结论会误导自己。4.3 重复值、精度截断和离散数据W 统计量的天敌另一个高频踩坑点是重复值。工程数据里很常见量表只能取整数、传感器精度只到一位小数、问卷选项就五档。重复值意味着样本的经验分布里有大量并列秩这会直接干扰顺序统计量的理论计算因为 SW 的权重系数假设数据是连续分布不存在并列值。现象就是数据看着挺正态、Q-Q 图也直但 p 值非常小。处理办法没有银弹。重复值占比低于 10% 时可以加少量随机 jitter 噪声再检验如果本身就是离散分布或重复占比过高就不要硬做正态性检验直接改用非参数检验更稳妥。这个问题几乎出现在每一份真实数据集里值得在代码里加一个前置检查先统计唯一值数量如果唯一值占比低于 50%就要警惕这个坑。5. 避坑正态性检验最常见的 5 个翻车点5.1 大样本 p0.05 但 W≈0.99不是数据错了是检验太灵敏现象n 超过 2000SW 检验输出 p0.001但 Q-Q 图看着就是一条直线工程师觉得数据没问题。原因大样本下检验把微小且工程上无影响的偏离放大为统计显著这是检验功效过高的典型表现。解决不要只看 p 值把 W 值一起报出来W 值在 0.99 以上时即使 p 值很小通常也可以认定数据满足近似正态的工程要求。5.2 重复值导致 SW 和 SF 给出互相矛盾的结论现象同一份数据SW 说 p0.03 拒绝正态SF 说 p0.12 不拒绝两个结论相反不知道信谁。原因大量并列秩破坏了 SW 权重系数的连续性假设而 SF 用的是相关性计算对并列秩容忍度高一些。解决先统计重复值比例超过 10% 就加 jitter 或改用非参数检验同时把两种检验的差异本身当成数据质量异常的警报。5.3 R 中 shapiro.test 在 n5000 时返回 NA现象R 里跑shapiro.test结果不是数字而是NA并且伴随警告信息。原因该函数硬编码了样本量上限 5000。解决改用nortest::sf.test或者对数据做随机抽样到 5000 以内再检验。抽样后注意结果只代表子样本最好多抽几次验证稳定性。5.4 Python 的 stats.shapiro 在 n5000 时行为不一致现象不同 scipy 版本下结果完全不同有的报异常有的返回极小 p 值有的干脆挂起。原因scipy 底层实现基于 n 有限近似n 较大时数值稳定性变差。解决用第三章的自实现shapiro_francia()函数做替代或者把数据切分成多段分别检验再观察拒绝率是否稳定。5.5 分组检验正态、合并后不正态检验对象搞错了现象每个实验组的数据单独检验都通过正态性但把合并后的整体数据检验却拒绝正态。原因合并后分布本质是几个正态分布的混合整体形态可能多峰或偏斜与单组正态无关。解决明确检验对象是分组还是整体如果做方差分析检验对象是每组残差而不是原始合并数据。常见的做法是先拟合模型、再对残差做正态性检验这一点比检验原始数据更符合统计模型的假设。6. 把 Q-Q 图、偏度峰度和 W 统计量绑在一起一份判定流程6.1 为什么只依赖 p 值是偷懒只依赖 p 值在样本量两端都会翻车。小样本下检验功效低非正态数据可能测不出来大样本下功效过高近似正态也会被拒绝。Q-Q 图虽然主观但它能告诉你偏离发生在中心还是尾部、是单侧还是双侧这是 W 值给不了的信息。偏度和峰度则能量化形态偏离的方向。三者结合结论才站得住。6.2 判定流程与可复用函数我的习惯是三条线索并行先画 Q-Q 图再算偏度和峰度最后用 SW/SF 检验给数值结论全部通过才认定数据可用。import numpy as np from scipy import stats import matplotlib.pyplot as plt def normality_report(x, alpha0.05): 整合 Q-Q 图、偏度、峰度与 SW 检验的正态性判定 x np.asarray(x, dtypefloat) x x[~np.isnan(x)] # 1. Q-Q 图 stats.probplot(x, distnorm, plotplt) plt.title(Q-Q Plot) plt.show() # 2. 偏度与峰度 skew stats.skew(x) kurt stats.kurtosis(x) # 3. 样本量允许时才做 SW 检验 if len(x) 5000: w, p stats.shapiro(x) else: w, p float(nan), float(nan) # 综合判定 shape_ok abs(skew) 1 and abs(kurt) 3 stat_ok p alpha if not np.isnan(p) else True verdict 正态 if shape_ok and stat_ok else 不满足正态假定 print(fn{len(x)}, skew{skew:.3f}, kurtosis{kurt:.3f}, W{w:.4f}, p{p:.4f}) print(f判定{verdict})这个函数的执行逻辑是屏蔽缺失值、画图、算偏度峰度、算检验、综合判定。偏度绝对值小于 1、峰度绝对值小于 3 是经验阈值来自模拟数据下的工程惯例p 大于 0.05 才算统计上不拒绝正态。三条线索都通过才敢把数据用于后续的 t 检验或方差分析。从那以后我每次做正态性检验都强制走一遍这个流程先看图建立直觉再看偏度峰度确认偏离方向最后用检验统计量下数值结论。希望帮到你。本文还有配套的精品资源点击获取