4. 缺失时间戳的规则网格重建
为什么需要规则网格
矿山传感器(如风速、CO、顶板压力)常因网关重启、无线丢包或采集器休眠而出现时间戳缺口。若直接对原始记录做滑动平均或差分,时间轴是不等距的,统计结果会被"时间密度"扭曲。规则网格重建的目标是:先确定一个标称采样周期 Δt,生成从起点到终点、步长恒为 Δt 的完整时间索引,再把原始记录按时间对齐到网格上,未命中的网格点标记为缺失。这样后续的插值、连续缺失段检测、频率校验才有统一的时间基准。
关键参数与对齐规则
- Δt(标称周期):由设备配置或前序频率校验得出,例如 60 s。它必须与原始数据的主间隔一致,否则会把正常采样误判为缺失。
- 网格起点 t0:取原始序列的最小时间戳,并向下取整到 Δt 的整数倍,保证网格与"整分钟/整小时"对齐,便于跨设备合并。
- 网格终点 t1:取原始序列的最大时间戳,向上取整到 Δt 的整数倍。
- 对齐容差 tol:原始时间戳很少精确落在网格点上(存在毫秒级抖动),因此采用"最近网格点 + 容差"匹配,tol 一般取 Δt 的 10%~25%。
- 冲突处理:若多条原始记录落入同一网格点(抖动导致),保留与网格点时间差最小的一条,其余丢弃并计数。
Δt 的选择直接决定"缺失"的定义。若 Δt 取 30 s 而设备实际 60 s 采样,则每个真实点之间都会插入一个假缺失,后续连续缺失段检测会全线报警。因此 Δt 应来自设备配置或前序频率校验的结论,而不是拍脑袋。
用 pandas 实现网格重建
下面代码可直接运行,输入是一段带抖动和缺口的示例数据,输出规则网格 DataFrame 与缺失统计。关键点在于:reindex 之前必须先把原始时间戳"吸附"到最近的网格点,否则抖动会让同一格点出现多条记录或全部落空。
import pandas as pd
import numpy as np
# 示例原始数据:时间戳有毫秒抖动,08:03、08:04 缺失
raw = pd.DataFrame({
"ts": pd.to_datetime([
"2024-06-01 08:00:03", "2024-06-01 08:01:01",
"2024-06-01 08:02:58", "2024-06-01 08:05:02",
"2024-06-01 08:06:00", "2024-06-01 08:07:59",
]),
"value": [3.2, 3.4, 3.1, 3.6, 3.5, 3.7],
})
DELTA = pd.Timedelta(seconds=60) # 标称周期
TOL = pd.Timedelta(seconds=12) # 对齐容差 = 0.2 * DELTA
# 1) 网格边界:向下/向上取整到 Δt 的整数倍
t0 = raw["ts"].min().floor("60s")
t1 = raw["ts"].max().ceil("60s")
grid = pd.date_range(t0, t1, freq=DELTA, name="ts")
# 2) 吸附:每个原始时间戳找最近的网格点
pos = (raw["ts"] - t0) / DELTA
nearest = t0 + (pos.round().astype(int) * DELTA)
offset = (raw["ts"] - nearest).abs()
# 3) 容差过滤 + 冲突去重(保留偏移最小者)
raw = raw.assign(grid_ts=nearest, offset=offset)
raw = raw[raw["offset"] <= TOL]
raw = raw.sort_values("offset").drop_duplicates("grid_ts", keep="first")
# 4) 对齐到规则网格,未命中即为 NaN
aligned = raw.set_index("grid_ts")["value"].reindex(grid)
# 5) 缺失标记与统计
aligned = aligned.rename("value").to_frame()
aligned["is_missing"] = aligned["value"].isna()
print(aligned)
print("缺失率:", aligned["is_missing"].mean().round(4))
print("缺失点数:", int(aligned["is_missing"].sum()), "/", len(aligned))
预期输出(示例数据可直接推出):网格共 9 个点(08:00~08:08),08:03 与 08:04 为 NaN,08:08 因无原始记录也为 NaN,缺失率 3/9 ≈ 0.3333。若把 t1 改为 raw["ts"].max().floor("60s"),则 08:08 不会被纳入网格,缺失率降为 2/9 ≈ 0.2222——这正是"尾部缺失"与"真实中断"需要区分的地方。
容差 tol 不能大于 Δt/2,否则相邻两个网格点会争抢同一条原始记录,导致吸附结果依赖排序而非真实时间。若原始数据抖动超过 Δt/2,说明设备时钟漂移严重,应先做时钟校正,而不是放大容差。
结果判读与验证
| 检查项 | 通过条件 | 不通过时的含义 |
|---|---|---|
| 网格长度 | = (t1 - t0)/Δt + 1 | 边界取整逻辑有误 |
| 吸附后记录数 | ≤ 原始记录数 | 出现重复吸附,需检查容差与去重 |
| 缺失率 | 与设备历史丢包率同量级 | Δt 选错或容差过小 |
| 尾部缺失 | 单独标记,不并入中断段 | 会把序列结束误报为通信故障 |
完成本步后,aligned 就是一张时间等距、带 is_missing 标记的标准表,可直接交给后续的连续缺失段检测与插值补全使用。