1. 尾矿库浸润线监测数据异常识别与初判

案例任务与判据建立

某尾矿库在坝体 3 个断面布置了 9 支振弦式渗压计(测点编号如 P1-1 表示 1 号断面第 1 支),采集频率 1 次/小时,输出为测压管水位(单位 m,以坝基高程为基准)。本案例取其中 7 天(168 个采样点)的序列,任务是先做数据质量筛查,再对可疑段做渗流合理性初判,最终把异常归入“传感器故障”或“真实渗流响应”两类。

三类异常的定义与量化判据如下(阈值可按库区实际调整,此处为教学示例取值):

  • 数据中断:连续缺失 ≥ 3 个采样点,或单点时间戳间隔 > 2 倍采集周期。
  • 跳变:相邻两点差值 |Δh| > 0.30 m,且跳变后 3 点内未回落到原趋势带(±0.05 m)内。
  • 漂移:以 24 点滑动中位数拟合斜率,|k| > 0.02 m/h 且持续 ≥ 12 点,同时与库水位变化方向不一致。
判据中的“趋势带”用跳变前 6 点的中位数 ±0.05 m 定义,避免用均值被异常点污染。

异常识别与初判逻辑

筛查不是单纯看数值大小,而是把数据形态与物理合理性交叉验证。核心交叉项有三个:跳变是否伴随信号质量指标(如振弦频率、激励响应)异常;漂移方向是否与库水位升降同向;中断恢复后首值是否与中断前趋势衔接。下图给出从原始序列到初判结论的判读路径与三类异常的典型形态。

三类异常的序列形态(示意) ① 数据中断 缺失 ≥3 点 恢复后首值需与中断前趋势衔接 ② 跳变 |Δh| > 0.30 m 3 点内未回落至趋势带 ±0.05 m ③ 漂移 k > 0.02 m/h 持续 ≥12 点 方向与库水位变化不一致 → 疑传感器 初判判读路径 原始序列 + 时间戳 168 点 / 7 天 质量筛查 中断 / 跳变 / 漂移 物理合理性交叉 库水位 / 邻点 / 信号质量 是否伴随信号质量异常? 频率漂移 / 激励无响应 / 单点孤立 是 传感器故障 标记剔除 / 现场校验 否 是否与库水位同向且邻点同步? 滞后 2~6 h 内响应 是 真实渗流响应 加密观测 / 断面复核 否 存疑:需人工复核 淤堵 / 渗漏通道 / 基准漂移 示例判读(教学数据) 测点 异常类型 关键证据 初判 P1-2 中断 缺失 5 点,恢复首值衔接 通信中断,数据可用 P2-1 跳变 Δh=0.42 m,频率同步跳变 传感器故障 P3-2 漂移 k=0.03 m/h,库水位未升 存疑,需复核

关键判读要点

上图中最容易误判的是跳变与漂移。跳变若只出现在单点、且恢复后回到趋势带,多为瞬时干扰(如雷击、接线松动),可插值修复;若跳变后稳定停留在新水平,则要区分是传感器基准偏移还是渗流条件突变——此时看同断面邻点:邻点无同步变化,倾向传感器;邻点同步抬升,倾向真实渗流。

漂移的判别必须引入库水位。若测点水位缓慢上升而库水位同期下降,物理上不合理(除非有额外补给),优先怀疑传感器基准漂移或测管淤堵导致的“假水位”。反之,若漂移方向与库水位一致、幅度在合理响应范围内,则可能是正常渗流调整,不应草率判为故障。

初判结论只是“疑似”,不能替代现场校验。对判为传感器故障的测点,应记录故障时段并在后续分析中剔除或标记,避免污染浸润线形态绘制。

可执行筛查脚本(Python)

下面给出可直接运行的筛查代码,输入为 CSV(列:ts, point_id, level),输出每个测点的异常标记与初判类别。阈值集中在 CFG 中,便于按库区调整。

import pandas as pd
import numpy as np

CFG = dict(gap_pts=3, jump=0.30, band=0.05, drift_k=0.02, drift_len=12)

def screen(df, cfg=CFG):
    df = df.sort_values(["point_id", "ts"]).copy()
    out = []
    for pid, g in df.groupby("point_id"):
        g = g.reset_index(drop=True)
        h = g["level"].astype(float)
        # 1) 中断:时间间隔 > 2 倍周期 或 连续缺失
        dt = g["ts"].diff().dt.total_seconds() / 3600
        gap = dt > 2
        # 2) 跳变:相邻差超限且 3 点内未回落
        d = h.diff().abs()
        jump = pd.Series(False, index=g.index)
        for i in np.where(d > cfg["jump"])[0]:
            base = h.iloc[max(0, i-6):i].median()
            nxt = h.iloc[i:i+4]
            if len(nxt) and (nxt - base).abs().min() > cfg["band"]:
                jump.iloc[i] = True
        # 3) 漂移:24 点滑动中位数斜率
        med = h.rolling(24, min_periods=12).median()
        k = med.diff() / dt.replace(0, np.nan)
        drift = (k.abs() > cfg["drift_k"]).rolling(cfg["drift_len"]).sum() >= cfg["drift_len"]
        out.append(pd.DataFrame({
            "point_id": pid, "ts": g["ts"], "level": h,
            "gap": gap, "jump": jump, "drift": drift.fillna(False)
        }))
    return pd.concat(out, ignore_index=True)

# 初判:跳变/漂移且无邻点同步 → 疑传感器;有同步 → 疑真实响应
def verdict(res, sync_pts):
    res["verdict"] = "正常"
    res.loc[res["gap"], "verdict"] = "通信中断"
    bad = res["jump"] | res["drift"]
    res.loc[bad & ~res["point_id"].isin(sync_pts), "verdict"] = "疑传感器故障"
    res.loc[bad & res["point_id"].isin(sync_pts), "verdict"] = "疑真实渗流响应"
    return res

运行方式:res = screen(pd.read_csv("levels.csv", parse_dates=["ts"])),再调用 verdict(res, sync_pts={"P3-1","P3-3"})。验证方法:对已知注入的模拟异常段(如人为置空 5 点、叠加 0.4 m 阶跃、叠加 0.03 m/h 斜坡),检查 gap/jump/drift 三列是否在对应位置置 True,且正常段保持 False。

初判记录卡(可直接填写)

字段填写内容判断依据
测点编号如 P2-1断面-序号
异常时段起止时间戳筛查脚本输出
异常类型中断 / 跳变 / 漂移对应判据阈值
信号质量频率、激励响应是否异常原始信号核查
邻点同步性同断面邻点是否同向变化断面多点对比
库水位关系同向 / 反向 / 无响应库水位序列对照
初判结论传感器故障 / 真实渗流 / 存疑交叉验证结果
后续动作剔除、加密观测、现场校验结论对应处置

完成本案例后,应能对任一测点序列给出“异常类型 + 证据链 + 初判类别 + 处置建议”的完整记录,并明确哪些结论仍需现场校验确认。