1. 缺失值误判为异常值:阈值清洗导致有效数据被删的排障
问题定位:固定阈值把合法极值判成异常
某环境监测数据集的 PM2.5 小时浓度列在清洗后行数骤减约 18%,被删记录集中在夜间与沙尘时段。原清洗脚本使用固定阈值规则:PM2.5 > 150 即判为异常并整行删除。问题在于该阈值把两类完全不同的情况混为一谈:一类是传感器故障产生的离群值(如 999、-1、0 等哨兵值),另一类是沙尘或重污染过程中真实出现的高浓度值(180~420 μg/m³)。前者应删除或标记,后者属于有效观测,删除会直接破坏污染过程分析和日均值统计。
排障的核心不是调阈值大小,而是先区分“缺失/无效”与“合法极值”两种语义,再让清洗规则按语义分别处理。下面用分布对比与业务边界核查两条线索交叉定位。
用分布对比锁定误删区间
先对原始列做分位数与直方图检查,而不是直接看最大值。示例数据(教学假设)中,PM2.5 的 P50≈42、P95≈118、P99≈210、最大值 999。若阈值取 150,则 P95 到 P99 之间的 180~210 区间会被整段删除,而这正是重污染过程的真实浓度。判断依据是:被删样本在时间上连续、在空间上多站点同步升高,且与气象沙尘过程对应,说明它们是有效观测而非随机噪声。
同时检查被删样本的取值形态:若大量记录恰好等于 999、-1、-999 等固定值,或与前后时刻出现无过渡的跳变,则属于哨兵值/无效值。两类样本混在同一阈值下被删除,是本次排障的关键发现。
修正规则:标记缺失而非删除
修正思路是把“删除”改为“标记”,并让阈值只作用于哨兵值识别,不再承担极值裁剪职责。以下为可直接运行的 Python 示例,使用 pandas 对示例数据执行修正前后对比。
import pandas as pd
import numpy as np
# 构造教学示例数据:正常值 + 合法极值 + 哨兵值
np.random.seed(7)
normal = np.random.normal(45, 20, 800).clip(1, 140)
legit_extreme = np.linspace(160, 420, 60) # 沙尘过程合法极值
sentinel = np.array([999, -1, -999] * 10) # 哨兵值
pm = np.concatenate([normal, legit_extreme, sentinel])
df = pd.DataFrame({"pm25": pm})
# 错误规则:固定阈值直接删除
wrong = df[df["pm25"] <= 150].copy()
# 修正规则:哨兵值标记为缺失,合法极值保留
SENTINEL = {999, -1, -999}
df["pm25_clean"] = df["pm25"].where(~df["pm25"].isin(SENTINEL))
df["qc_flag"] = np.where(df["pm25"].isin(SENTINEL), "missing", "valid")
print("原始行数:", len(df))
print("错误规则后行数:", len(wrong))
print("修正后行数:", len(df))
print("标记缺失数:", (df["qc_flag"] == "missing").sum())
print("保留的合法极值数:", ((df["pm25"] > 150) & (df["qc_flag"] == "valid")).sum())
预期输出(由上述示例数据直接推出):原始 890 行,错误规则后约 860 行(删掉 60 个合法极值中的大部分及哨兵值),修正后仍为 890 行,标记缺失 30 个,保留合法极值 60 个。行数不再因极值被误删而减少,缺失语义由 qc_flag 显式表达。
验证与通过条件
- 行数守恒:清洗前后行数差应等于哨兵值/无效值数量,不应包含合法极值。
- 统计口径一致:对
pm25_clean求日均值时,缺失值自动跳过,日均值与原始有效观测的偏差应小于 1%(示例数据下可直接核对)。 - 过程连续性:沙尘时段 180~420 的曲线在清洗后仍连续可查,不被截断。
- 标记可追溯:
qc_flag能区分 missing 与 valid,后续聚合可按标记过滤,避免再次误删。