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 级别,若出现上千则很可能是单位或符号问题)。
结果判读与常见问题
- 时间戳解析失败:多为格式不符(如
2024/06/01 8:00缺前导零)。先用reader.fieldnames与首行样本核对格式,再调整strptime模板。 - 位移量级异常:若 max 达到数百以上,检查文件单位是否为 m 而非 mm,或符号约定是否相反;不要直接进入预警阈值判断。
- 测点记录数不一致:说明存在漏采或测点中途加入,需在后续时间对齐阶段处理,本例只需在摘要中如实报告。
- quality 偏低:本例仅统计,不剔除;低质量记录应在清洗阶段按规则处理,避免在入口处误删有效数据。
完成上述步骤后,records 即为结构统一、类型明确的数据入口,可直接交给后续的时间序列对齐与异常清洗环节使用。