2. 进水水质异常日的统计判定与标记

为什么异常日判定不能只看单点超标

进水COD和氨氮本身波动很大,若用固定阈值(如COD>500 mg/L)判定异常,会把正常的高浓度日误报,也会漏掉“相对自身历史偏高”的异常。本节以某厂连续60天进水数据为教学示例,用两种统计准则判定异常日:3σ准则(参数法,假设近似正态)与箱线图准则(IQR法,非参数、抗离群)。两者对同一批数据的标记差异,正是后续工艺核查的线索。

判定前先明确:异常日按“日”为单位,取当日进水COD、氨氮的日均值;两种准则分别对每个指标独立判定,任一指标异常即该日进入候选清单,再按指标来源分类。
同一指标、两种准则的判定区间对比(以COD日均值为例) COD (mg/L) 3σ 准则 μ−3σ 以下 μ+3σ 以上 μ ± 3σ 正常带(约99.7%) μ−3σ μ+3σ 箱线图准则 < Q1−1.5IQR > Q3+1.5IQR Q1−1.5IQR ~ Q3+1.5IQR 正常带 下须 上须 3σ 更“宽” 受均值与标准差拉动,个别极端值会 抬高σ,使正常带变宽,漏报轻度异常。 箱线图更“稳” 基于四分位数,不受极端值影响, 对偏态分布更敏感,易多标“相对偏高”日。 标记结果差异分类(供工艺核查) 两法一致异常 优先核查,可信度高 仅箱线图标记 偏态/轻度偏高,复核 仅3σ标记 极端值拉动,重点核实 均未标记 正常日

两种准则的计算口径

设某指标60个日均值为 x₁…xₙ,n=60。

  • 3σ准则:μ 为均值,σ 为样本标准差(分母 n−1)。判定异常条件为 |xᵢ−μ| > 3σ。正态假设下双侧误报率约0.3%。
  • 箱线图准则:Q1、Q3 为25%、75%分位数,IQR=Q3−Q1。判定异常条件为 xᵢ < Q1−1.5·IQR 或 xᵢ > Q3+1.5·IQR。不假设分布,抗离群。

两法均对COD、氨氮分别判定,再取并集形成“候选异常日”,并记录每个异常日是被哪种准则、哪个指标标记的。

可运行的判定与对比代码

以下Python脚本自带示例数据生成(固定随机种子,可复现),完成统计量计算、两法标记、差异分类与清单输出。示例数据为教学假设,非实测。

import numpy as np
import pandas as pd

rng = np.random.default_rng(42)
n = 60
dates = pd.date_range("2024-03-01", periods=n, freq="D")

# 教学示例数据:基础正态 + 人为注入的异常日
cod = rng.normal(320, 45, n)
nh3 = rng.normal(28, 6, n)
cod[[12, 33, 47]] = [520, 180, 610]   # 高/低/极端高
nh3[[12, 25, 47]] = [52, 9, 61]

df = pd.DataFrame({"date": dates, "COD": cod.round(1), "NH3": nh3.round(1)})

def sigma_flag(s, k=3.0):
    mu, sd = s.mean(), s.std(ddof=1)
    return (s - mu).abs() > k * sd, mu, sd

def box_flag(s, k=1.5):
    q1, q3 = s.quantile(0.25), s.quantile(0.75)
    iqr = q3 - q1
    lo, hi = q1 - k * iqr, q3 + k * iqr
    return (s < lo) | (s > hi), lo, hi

rows = []
for col in ["COD", "NH3"]:
    s_flag, mu, sd = sigma_flag(df[col])
    b_flag, lo, hi = box_flag(df[col])
    df[f"{col}_3s"] = s_flag
    df[f"{col}_box"] = b_flag
    print(f"{col}: μ={mu:.1f}, σ={sd:.1f}, 3σ带=[{mu-3*sd:.1f},{mu+3*sd:.1f}]")
    print(f"{col}: Q1-1.5IQR={lo:.1f}, Q3+1.5IQR={hi:.1f}")

df["any_3s"] = df[["COD_3s", "NH3_3s"]].any(axis=1)
df["any_box"] = df[["COD_box", "NH3_box"]].any(axis=1)

def classify(r):
    if r.any_3s and r.any_box:
        return "两法一致"
    if r.any_box:
        return "仅箱线图"
    if r.any_3s:
        return "仅3σ"
    return "正常"

df["判定"] = df.apply(classify, axis=1)
abnormal = df[df["判定"] != "正常"].copy()
print(abnormal[["date", "COD", "NH3", "判定"]].to_string(index=False))
print("异常日总数:", len(abnormal))

结果判读与清单使用

运行后先看两法各自的正常带边界:若3σ带明显宽于箱线图须界,说明数据存在极端值拉高σ,此时“仅3σ标记”的日子要重点核实是否为真实冲击;“仅箱线图标记”的日子多为偏态分布下的相对偏高,需结合当日流量、SS、pH复核是否为稀释或偷排。清单按“判定”列排序,优先处理“两法一致”的日期。

判定类别含义核查动作
两法一致两准则均判异常优先调取当日流量、SS、pH、在线仪表状态
仅箱线图相对历史偏高/偏低复核是否偏态、季节性变化或轻度冲击
仅3σ极端值拉动核实原始数据、采样与仪表,排除误报
3σ准则对极端值敏感:注入的610 mg/L会把σ抬高,可能使其他真实异常日落入正常带。因此两法结果必须并列呈现,不能只取其一。若数据明显偏态,应优先采用箱线图准则或先做对数变换。

异常日清单(示例输出结构)

清单字段建议固定为:日期、COD、NH3、判定类别、触发指标、触发准则,便于后续工艺核查逐条闭环。示例中注入的异常日应能被两法分别捕获,具体条数以实际运行输出为准。

date        COD    NH3  判定
2024-03-13  520.0  52.0 两法一致
2024-03-26   ...    9.0  仅箱线图
2024-04-03  180.0  ...   仅3σ
2024-04-17  610.0  61.0 两法一致

将清单导出为CSV后,与工艺运行记录(曝气量、回流比、加药量)按日期关联,即可判断异常是进水冲击还是工艺自身波动,为下一例的关联分析提供标注基础。