1. 边坡雷达原始位移数据读取与字段解析

原始文件长什么样:先看清结构再动手

露天矿边坡雷达监测系统导出的原始位移文件,通常是带表头的文本表(CSV 或制表符分隔),一行代表一个测点在一个采样时刻的观测记录。不同厂商的列名、单位、时间格式差异很大,因此第一步不是急着算预警,而是把“文件里到底有什么”摸清楚。本例使用一份教学示例文件 slope_radar_raw.csv,其典型结构如下(示例数据,非实测):

timestamp,point_id,x_m,y_m,z_m,disp_mm,quality
2024-06-01 08:00:00,P-001,412356.20,2850114.80,1186.40,0.0,0.98
2024-06-01 08:00:00,P-002,412401.55,2850122.10,1191.75,0.0,0.96
2024-06-01 08:00:00,P-003,412447.10,2850130.60,1197.20,0.0,0.94
2024-06-01 08:10:00,P-001,412356.20,2850114.80,1186.40,1.2,0.97
2024-06-01 08:10:00,P-002,412401.55,2850122.10,1191.75,0.8,0.95
2024-06-01 08:10:00,P-003,412447.10,2850130.60,1197.20,1.5,0.93

关键字段含义:timestamp 为采样时刻(本地时间,无时区标记);point_id 为测点编号;x_m/y_m/z_m 为测点在矿区坐标系下的三维坐标(米);disp_mm 为沿雷达视线方向的累计位移(毫米,正值为朝向雷达/远离坡体方向,具体符号约定须以厂商说明为准);quality 为回波质量因子(0~1,越低越不可靠)。

字段解析的核心:类型、单位与缺失

读取阶段最容易埋雷的三件事是:时间戳被当成字符串(后续无法排序对齐)、数值列混入空值或非数字(统计时报错或静默变 NaN)、单位不统一(有的文件位移是 m,有的是 mm)。因此解析时要显式声明每列的类型与单位,并对缺失做计数而不是直接丢弃。

下面是一段可直接运行的 Python 实现,完成读取、字段识别、类型转换与统计摘要输出:

import csv
from datetime import datetime
from collections import defaultdict

RAW_PATH = "slope_radar_raw.csv"

# 字段映射:原始列名 -> 标准字段名
FIELD_MAP = {
    "timestamp": "ts",
    "point_id": "pid",
    "x_m": "x", "y_m": "y", "z_m": "z",
    "disp_mm": "disp", "quality": "q",
}
NUMERIC = {"x", "y", "z", "disp", "q"}

def parse_row(row):
    rec = {}
    for raw, std in FIELD_MAP.items():
        v = row.get(raw, "").strip()
        if std == "ts":
            rec[std] = datetime.strptime(v, "%Y-%m-%d %H:%M:%S") if v else None
        elif std in NUMERIC:
            try:
                rec[std] = float(v) if v != "" else None
            except ValueError:
                rec[std] = None
        else:
            rec[std] = v or None
    return rec

records, bad_rows = [], 0
with open(RAW_PATH, newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        rec = parse_row(row)
        if rec["ts"] is None or rec["pid"] is None:
            bad_rows += 1
            continue
        records.append(rec)

# 统计摘要
pids = sorted({r["pid"] for r in records})
times = [r["ts"] for r in records]
disp_vals = [r["disp"] for r in records if r["disp"] is not None]
missing = {k: sum(1 for r in records if r[k] is None) for k in NUMERIC}

print(f"有效记录: {len(records)}  丢弃行: {bad_rows}")
print(f"测点数: {len(pids)}  时间范围: {min(times)} ~ {max(times)}")
print(f"位移(mm): min={min(disp_vals):.2f} max={max(disp_vals):.2f} "
      f"mean={sum(disp_vals)/len(disp_vals):.2f}")
print("缺失计数:", missing)
print("每测点记录数:", {p: sum(1 for r in records if r['pid']==p) for p in pids})

运行后应得到类似输出(基于上述示例数据可直接推出):有效记录 6 条、测点 3 个、时间范围 08:00~08:10、位移 min=0.00 max=1.50 mean≈0.58、各数值列缺失为 0、每测点 2 条记录。若缺失计数非零或测点记录数不一致,说明文件存在空值或采样不齐,需在进入预警前处理。

字段识别不要硬编码列序。用 csv.DictReader 按表头名取值,并在读取前先打印 reader.fieldnames 核对列名,可避免厂商改列序导致的错位。

从原始行到可用数据入口

解析完成后的 records 列表就是后续所有预警处理的统一入口:每条记录都带有已解析的时间对象、测点编号、坐标与位移值。判断“数据入口是否合格”的三个条件:时间戳全部可解析、测点编号无空、位移列无缺失且量级合理(本例为 0~2 mm 级别,若出现上千则很可能是单位或符号问题)。

原始位移文件 → 字段解析 → 统计摘要(数据入口) slope_radar_raw.csv timestamp point_id x_m / y_m / z_m disp_mm quality 表头 + 数据行(CSV) parse_row() 字段解析 ts → datetime pid → str x,y,z,disp,q → float 空值 → None(计数) 非数字 → None ts/pid 缺失 → 丢弃行 统计摘要 有效记录 / 丢弃行 测点数 / 时间范围 位移 min / max / mean 各数值列缺失计数 每测点记录数 → 合格则进入预警 字段类型与单位约定 timestamp datetime,本地时间 point_id 字符串,如 P-001 x_m / y_m / z_m float,单位 m disp_mm / quality float,mm / 0~1 合格判据:时间戳全部可解析 · 测点编号无空 · 位移无缺失且量级合理(本例 0~2 mm) 异常信号:缺失计数 > 0 · 测点记录数不一致 · 位移出现上千量级(疑单位/符号错误)

结果判读与常见问题

  • 时间戳解析失败:多为格式不符(如 2024/06/01 8:00 缺前导零)。先用 reader.fieldnames 与首行样本核对格式,再调整 strptime 模板。
  • 位移量级异常:若 max 达到数百以上,检查文件单位是否为 m 而非 mm,或符号约定是否相反;不要直接进入预警阈值判断。
  • 测点记录数不一致:说明存在漏采或测点中途加入,需在后续时间对齐阶段处理,本例只需在摘要中如实报告。
  • quality 偏低:本例仅统计,不剔除;低质量记录应在清洗阶段按规则处理,避免在入口处误删有效数据。

完成上述步骤后,records 即为结构统一、类型明确的数据入口,可直接交给后续的时间序列对齐与异常清洗环节使用。