格拉布斯准则详解:异常检测与离群值判定的统计方法
2026/9/23 16:41:31 网站建设 项目流程

简介:面向数学建模竞赛(美赛)及数据分析场景的异常值检测参考实现,基于格拉布斯准则完成数据预处理,帮助参赛者快速识别样本中的极端值。资源包共3个文件,包含MATLAB源代码、自动保存备份及txt说明文档,整体仅1KB,结构精简。其中.m脚本体现核心算法,.asv为编辑历史备份,txt可辅助理解调用逻辑与判定参数。目前已有106人学习下载。代码覆盖数据读取、均值/标准差计算、临界值查询、G值比对与异常值处理等关键环节,并预留参数调整位置,便于结合不同赛题数据灵活使用。掌握该实现,可加深对正态分布下异常检测统计原理的理解,提高数据清洗环节的效率与模型稳健性,适合正在备赛或学习数模基础的同学参考。

1. 格拉布斯准则:异常检测里最"讲理"的一票

做数据清洗的人多少都遇到过这种场面:一组测量数据里冒出一个离谱的峰值,业务方坚持说这是真实值不能删,你凭直觉觉得它是异常,但拿不出统计依据。格拉布斯准则(Grubbs' Test)就是给这种争执准备的——它不靠拍脑袋,而是用一个明确的统计量判断"这个点是不是离群值",并给出置信度。简单说,它先假设数据服从近似正态分布,然后计算每个样本点偏离均值的程度,和临界值比大小,超过就判异常。这套方法在理化检测、计量校准、设备点检数据清洗里用得非常多,适合单变量、样本量适中(一般3到30之间)、分布近似正态的场景。本文后面会给出可在本地直接跑通的代码,你不需要懂推导,跟着步骤改改路径和列名就能用。

2. 格拉布斯准则判定逻辑:为什么它是单变量异常检测的优先选择

2.1 判定公式与临界值来源

格拉布斯准则的核心是用t分布推导出来的临界值。对每个样本计算统计量 ( G_i = \frac{|x_i - \bar{x}|}{s} ),其中 \bar{x} 是样本均值,s 是样本标准差。这个G值代表"这个点偏离中心几个标准差"。但它不是简单地和3σ比,而是和由样本量n、显著性水平α计算出的临界值 ( G_{crit} ) 比。临界值来源于学生化残差的极值分布,公式为 ( G_{crit} = \frac{n-1}{\sqrt{n}} \sqrt{\frac{t^2_{α/(2n), n-2}}{n-2 + t^2_{α/(2n), n-2}}} )。

这个公式中的关键点是 α/(2n)——它做了 Bonferroni 校正,因为你要同时检查n个点,每个点都有可能是最大的那个,所以要除以n避免多重比较带来的虚高误判。常见取 α=0.05,代表95%置信水平。样本量不同,临界值也不同:n=10时临界值大约2.29,n=20时约2.71,n=30时约2.91。这就是为什么不能拿一个固定阈值硬套所有数据。

2.2 为什么在理化检测场景里它比3σ规则更可靠

3σ规则在工业里很流行,但它有一个先天缺陷:均值和标准差本身会被异常点"污染"。一个极端大的离群值会把均值拉偏、把标准差撑大,导致这个点自己把σ抬高,最后算出来它可能没超过3σ,被放过了。统计学上管这个叫"掩蔽效应"。格拉布斯准则用t分布推导临界值,就是为了缓解这个问题——虽然它也不能完全消除掩蔽效应,但比固定3σ的耐抗性好很多。

另一个优势是它有明确的置信度输出。当你向业务方解释"这个点判异常了",你可以直接说"在95%置信水平下,这个点被判为离群值",而不是含糊地说"它偏离了平均值很多"。在质量体系审核或计量校准报告里,这种可追溯的判据很重要,审计的人会问"你的异常判定标准是什么",格拉布斯准则给了你一个能写进SOP的答案。这是它被很多行业标准推荐的原因。

2.3 适用边界:正态性假设的前提约束

使用格拉布斯准则的前提是数据近似正态分布。它会检出一个离群值,但如果你的数据本身是偏态分布(比如反应时间、故障间隔这类右偏数据),这个判据的结果就不可靠了。偏态分布的尾部天然就长,格拉布斯会把正常的长尾值误判为离群值。常见做法是先做正态性检验,比如用Shapiro-Wilk检验或偏度峰度检查,确认数据基本符合正态假设后再用Grubbs。如果不满足正态,就需要先做Box-Cox变换,或用非参数方法(如Tukey的箱线图法、MAD法)代替。

还要注意,Grubbs一次只能检出一个离群值。如果你怀疑数据里有多个异常,不能用一次检验全找出来,必须用"剔除-重检"的迭代策略,也就是每轮找出最大的G值,剔除它,然后对剩余样本重新计算均值和标准差,再检查下一轮。代码里需要加循环来控制这个过程。

3. 拿到"格拉布斯准则判断异常数据代码.rar"后:先跑通最小示例再改数据

3.1 解压与目录结构:代码包里一般有什么

这类rar通常包含一个主脚本、一个示例数据文件,可能还有一个说明文档。常见主脚本是Python写的(.py)或Excel里嵌的VBA模块,示例数据一般是CSV或xlsx。解压后先别急着跑,花两分钟看目录结构:主脚本一般依赖numpy、scipy或pandas——如果只有纯Python + math库的实现,说明作者刻意降低了依赖门槛,跑起来更省心。

提示:解压先查毒,rar里的宏文件(VBA)有潜在风险,公司电脑尤其要注意。

3.2 用Python跑通最小示例:环境检查与命令

把主脚本和示例数据放在同一目录,先确认Python环境和依赖库可用:

python -c "import numpy, pandas, scipy; print('deps ok')"

如果报ModuleNotFoundError,按缺什么装什么:

pip install numpy pandas scipy

然后直接运行主脚本,常见写法是:

python grubbs_detect.py --input sample_data.csv --column value --alpha 0.05

如果脚本支持命令行参数会有--help:

python grubbs_detect.py --help

看到 Usage 信息说明脚本能正常加载。接下来就是把示例数据换成你自己的数据。注意示例数据通常只有几十行,结构是两列(编号、数值),如果你的Excel表带表头、带多列,需要先用pandas把目标列提取出来:

import pandas as pd df = pd.read_excel("your_file.xlsx", sheet_name="Sheet1") col = df["measurement"].dropna().values # 去掉空值 print(len(col), col[:5])

这段代码的作用是把Excel里目标列读出来并去掉NaN。Grubbs检验对缺失值敏感,空值必须剔除;但要注意如果空值占比超过20%,说明数据采集本身有问题,直接判异意义不大,应先解决数据质量问题。读取后打印长度和前5个值,确认数据量级是否符合预期,防止读错列。

3.3 核心判异函数:一个可直接粘贴的实现

如果你拿到的rar里脚本轮子太重,或者想彻底搞懂原理再自己写,下面这份最小实现可以直接用,它和rar里常见实现的核心逻辑一致:

import numpy as np from scipy import stats def grubbs_test(data, alpha=0.05): """ 单次Grubbs检验,返回最可疑点及其判定结果 data: 一维数组,已剔除NaN alpha: 显著性水平,默认0.05 """ arr = np.asarray(data, dtype=float) n = len(arr) if n < 3: return None, None, "样本量不足3,无法检验" mean = np.mean(arr) std = np.std(arr, ddof=1) # 注意用样本标准差 if std == 0: return None, None, "标准差为0,数据无波动" G = np.abs(arr - mean) / std max_idx = np.argmax(G) G_max = G[max_idx] # 查t分布临界值,自由度n-2,ppf是百分位点函数 t_crit = stats.t.ppf(1 - alpha / (2 * n), n - 2) G_crit = ((n - 1) / np.sqrt(n)) * np.sqrt( t_crit**2 / (n - 2 + t_crit**2) ) is_outlier = G_max > G_crit return max_idx, arr[max_idx], is_outlier

这个函数是Grubbs检验的标准实现。有几个参数必须注意:ddof=1是样本标准差,如果用默认的ddof=0,标准差会被低估,G值被高估,误判率上升,这是最容易翻车的细节。stats.t.ppf(1 - alpha / (2 * n), n - 2)计算的是双侧t临界值,除以2n是Bonferroni校正,样本量n越大,校正越严,临界值越高。返回的max_idx是最可疑点的位置,方便你回溯到原始数据查它是哪条记录。

3.4 参数选择:alpha取多少,迭代几轮

alpha的取值看业务场景。日常数据清洗常用0.05;如果你是做计量校准,可能更严格取0.01;如果是探索性分析,0.1也可以接受,但误判会增多。至于迭代轮数:一般建议每轮剔除一个点后重新检验,直到没有离群点为止。实际项目中我会设置一个上限,比如最多剔除样本量的10%,防止把正常数据剃光——尤其当数据本身来自重尾分布时,迭代次数多了会把尾巴全砍掉。

4. 批量处理与工程化:从单列判异到自动化清洗管线

4.1 多条数据列的批量循环

实际工作中数据往往是几十个测点,每个测点一列,一个字段一个字段跑太费劲。常见做法是写一个循环把每列都过一遍,汇总成一份异常清单:

df = pd.read_excel("sensor_data.xlsx") result_rows = [] for col_name in df.columns: if df[col_name].dtype not in ["float64", "int64"]: continue # 跳过非数值列 data = df[col_name].dropna().values if len(data) < 3: continue for round_idx in range(10): # 最多迭代10轮 idx, val, is_out = grubbs_test(data, alpha=0.05) if not is_out: break # 找到原始行号:data已剔除NaN,需要映射回原df的位置 raw_idx = df[col_name].dropna().index[idx] result_rows.append({ "column": col_name, "row": raw_idx, "value": val, "round": round_idx + 1 }) data = np.delete(data, idx) # 剔除后进入下一轮 outlier_df = pd.DataFrame(result_rows) outlier_df.to_csv("outlier_report.csv", index=False)

这段代码有几点设计意图:轮数上限设10是防止无限循环;df[col_name].dropna().index[idx]这行是把剔除NaN后的位置映射回原始Excel行号,不映射的话你只知道"第几个数据异常",不知道"Excel第几行异常",后续没法找业务方核对;列名循环前先判断dtype,跳过时间戳和文本列。把这套循环封装成函数后,丢给调度系统每天跑一次就实现了自动化判异。

4.2 与Excel文件对接:读xlsx与写报告

如果你的数据还在Excel里,最省事的对接方式是用pandas读、写。但要注意大文件性能和格式兼容性,读5000行以上建议指定engine="openpyxl";写报告时可以把每个测点单独放一个sheet,方便人工复核。下面是一个输出报告的示例:

with pd.ExcelWriter("outlier_report.xlsx", engine="openpyxl") as writer: outlier_df.to_excel(writer, sheet_name="全部异常", index=False) # 按列生成汇总 sheet summary = outlier_df.groupby("column").size().reset_index(name="异常数") summary.to_excel(writer, sheet_name="汇总", index=False)

ExcelWriter而不是to_excel直接写,好处是可以把多张表放进同一个工作簿。汇总sheet的存在很有必要:业务方只看总数,不关心每个具体值;你拿汇总表汇报,拿明细表核对,两张表分开效率更高。

4.3 rar里常见代码做得不好的地方:少了一个"复核"模式

很多共享出来的判异代码只输出结论,不输出可视化,这是一个很大缺口。只给"第27行是异常"这句话,业务方很难快速确认。我一般会在代码后面补一段快速可视化,把原始序列和异常点标在图上:

import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(12, 4)) ax.plot(df[col_name].values, "o-", ms=3) outlier_idx = outlier_df[outlier_df["column"]==col_name]["row"].values ax.scatter(outlier_idx, df[col_name].iloc[outlier_idx], c="red", s=40, label="outlier") ax.set_title(f"{col_name} 异常点标注") ax.legend() plt.show()

这张图的价值在于让业务方在10秒内确认"这个点是读数跳变还是真实波动"。很多数据被误删就是因为缺少这个人工复核环节。Grubbs判据是统计意义上的异常,不等于业务语义上的错误——必须让人眼确认后再决定剔除还是保留。这也是整个工程化管线里最容易被忽略的一层。

5. 避坑:格拉布斯准则在真实数据上的5个边缘场景

5.1 样本量太小,判据形同虚设

现象:n=3时跑出来的判定结果奇怪,比如最大点被剔除后,剩下的两点又有一个被剔除,三下五除二数据全没了。

原因:格拉布斯临界值在n=3时会很大,G值很容易超过临界值,检验灵敏度极低;n小于5时基本上没有实际意义,两个点的"异常"可能就是正常的随机波动。

解决:n<5时不使用Grubbs判据,直接人工研判。最稳妥的做法是积累到15个点以上再做统计判异,这段"数据积累期"可以用简单的范围检查兜底,低于下限或高于上限直接报警。

5.2 数据不是正态分布,误判一堆

现象:一批右偏数据跑完判异,尾部几十个点全被判异常,业务方说这些明明是正常波动。

原因:Grubbs假设正态分布。偏态数据的均值被长尾拉偏,标准差被撑大,判据失真。常见场景如故障间隔时间、化学反应时间都属于右偏分布。

解决:跑Grubbs之前先做Shapiro-Wilk正态性检验,p<0.05就放弃Grubbs,转用Tukey箱线图法或MAD法。如果业务上必须用Grubbs,先对数据做log或Box-Cox变换,变换后判异,之后再把边界反变换回去。

5.3 多重离群值导致"掩蔽效应",一个异常掩盖另一个

现象:数据里有两个离群值,第一次检验只检出其中一个,剔除后再检,另一个排位突然上升、又被检出。但问题是这两个点方向相同时,可能一个都检不出来,因为两个大值互相把均值撑住、把标准差撑大。

原因:Grubbs单次检验只检一个离群值。当多个离群值同时存在且方向一致时,它们的叠加效应会改变均值和标准差,导致每个点的G值都不超过临界值。

解决:如果业务上预计异常率较高(超过1%),不要只用Grubbs,可以先用MAD或箱线图初筛可疑点,再用Grubbs确认——前者找候选,后者给统计依据,两者互补。这样既避免漏检,又有可汇报的判据。

5.4 标准差为0或接近0时直接崩溃

现象:数据是恒定值(比如传感器没变化),运行时报ZeroDivisionError,或者临界值过小导致一切正常点都被判异常。

原因:Grubbs的分母是标准差,标准差为0时除法无意义;即使不为0但很小(比如0.001),G值会被放大到巨大,所有点都变成"异常"。

解决:在代码里加先决判断:std < 1e-8 时直接跳过该列。这不是"优化",是安全的底线。数据波动本身为0意味着没有可被判异的离群值——起码统计意义上没有。

5.5 把一次检验结果当成最终结论,删除数据不留痕

现象:直接执行删除语句。

原因:统计判异只是"建议标记",不是"事实错误"。Grubbs只能告诉你"在95%置信水平下这个点显著偏离中心",不能告诉你它是传感器故障、抄表错误还是真实罕见事件。

解决:判异结果统一标记status列(normal/outlier),人工复核后再决定是否进入清洗步骤。删除前备份原始数据。代码里至少留一句outlier_df.to_csv("outlier_candidates.csv"),给后续核对留个底。删除之前先备份,是数据工作里最基本的自救手段。

6. 把格拉布斯检验做成Excel里的可复用模板:手动挡也能跑

很多同事并不写Python,但你做完判异后,他们在Excel里也要能自己复算。把Grubbs检验转化成Excel公式,可以解决"代码在你这跑得通,换个人就归零"的窘境。

假设数据在A2:A31(n=30),B列为每个点的G值,操作如下:

步骤操作公式
1计算均值=AVERAGE(A2:A31)
2计算样本标准差=STDEV.S(A2:A31)
3计算每个点的G值=ABS(A2-$E$1)/$E$2(E1存均值,E2存标准差)
4计算临界值=((COUNT(A2:A31)-1)/SQRT(COUNT(A2:A31)))*SQRT(T.INV(1-0.05/(2*COUNT(A2:A31)), COUNT(A2:A31)-2)^2/(COUNT(A2:A31)-2+T.INV(1-0.05/(2*COUNT(A2:A31)), COUNT(A2:A31)-2)^2))
5判定=IF(MAX(B2:B31)>E3, "存在异常", "无异常")

这套公式的好处是双击就能用,不需要装Python环境。它和Python代码用的是同一套t分布临界值公式,算出的结果一致,可以作为代码运行的交叉验证。

我的习惯是:Python批量筛选异常点,Excel模板复算最终判定并记录存档——双轨并行,两边对得上才写入正式报告。这个习惯让我避免过一次惨痛教训:一次分析设备数据时,我把传感器正常波动误判成异常,直接删掉了一百多个采样点,事后复盘时发现那其实是有规律的微振动信号。从那以后,我不管代码跑得多顺,都会保留原始数据并做一次可视化复核。

Grubbs准则给你的是一个可落地的判定标准,不是自动清洗的免检金牌。实际用的时候永远给自己留一个"复查"的出口,确认无误再删。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询