3. 边缘数据预处理与清洗

任务说明

本案例针对边缘网关采集到的温度传感器原始数据流,实现一个轻量级的数据预处理模块,完成三项核心任务:去噪(剔除突变异常值)、缺失值填充(线性插值)和格式标准化(统一时间戳与数值精度)。处理后的数据可直接用于边缘侧的上层分析或转发至云端。

实现方案

使用Python标准库实现,不依赖第三方数据处理框架,便于在资源受限的边缘设备上直接运行。核心逻辑封装为Preprocessor类,输入为原始记录列表,输出为清洗后的标准化记录列表。

  • 去噪:采用滑动窗口中值滤波,窗口大小为5,能有效去除单点脉冲噪声,同时保留真实温度变化趋势。
  • 缺失值填充:检测时间戳间隔超过阈值(2秒)的缺失区间,使用前后有效值的线性插值进行填充。
  • 标准化:时间戳统一为Unix毫秒整数,温度值保留1位小数,输出字段顺序固定为timestamp, temperature。

完整代码

import json
from collections import deque
from typing import List, Dict, Tuple

class Preprocessor:
    def __init__(self, window_size: int = 5, max_gap_ms: int = 2000):
        self.window_size = window_size
        self.max_gap_ms = max_gap_ms

    def _median_filter(self, values: List[float]) -> List[float]:
        """滑动窗口中值滤波去噪"""
        result = []
        q = deque()
        for v in values:
            q.append(v)
            if len(q) > self.window_size:
                q.popleft()
            window = sorted(q)
            result.append(window[len(window) // 2])
        return result

    def _fill_missing(self, records: List[Tuple[int, float]]) -> List[Tuple[int, float]]:
        """基于时间间隔检测缺失,线性插值填充"""
        filled = []
        for i in range(len(records) - 1):
            ts_cur, val_cur = records[i]
            ts_next, val_next = records[i + 1]
            filled.append((ts_cur, val_cur))
            gap = ts_next - ts_cur
            if gap > self.max_gap_ms:
                # 计算缺失点数(最多填充10个点,防止异常大间隙)
                n_points = min(int(gap / self.max_gap_ms) - 1, 10)
                for j in range(1, n_points + 1):
                    ratio = j / (n_points + 1)
                    ts_interp = int(ts_cur + gap * ratio)
                    val_interp = round(val_cur + (val_next - val_cur) * ratio, 1)
                    filled.append((ts_interp, val_interp))
        filled.append(records[-1])
        return filled

    def process(self, raw_data: List[Dict]) -> List[Dict]:
        """主处理流程:去噪 → 缺失填充 → 标准化"""
        # 1. 按时间戳排序并提取数值
        raw_sorted = sorted(raw_data, key=lambda x: x['timestamp'])
        timestamps = [r['timestamp'] for r in raw_sorted]
        values = [float(r['value']) for r in raw_sorted]

        # 2. 中值滤波去噪
        denoised = self._median_filter(values)

        # 3. 重组为记录并填充缺失
        records = list(zip(timestamps, denoised))
        filled_records = self._fill_missing(records)

        # 4. 标准化输出
        result = []
        for ts, val in filled_records:
            result.append({
                'timestamp': int(ts),          # 毫秒整数
                'temperature': round(val, 1)   # 保留1位小数
            })
        return result

# ========== 最小可运行示例 ==========
if __name__ == '__main__':
    # 模拟传感器原始数据(含噪声、缺失、时间戳抖动)
    raw_input = [
        {"timestamp": 1000, "value": 22.5},
        {"timestamp": 1001, "value": 22.6},
        {"timestamp": 1002, "value": 45.2},    # 噪声尖峰
        {"timestamp": 1003, "value": 22.4},
        {"timestamp": 1004, "value": 22.7},
        {"timestamp": 1007, "value": 22.9},    # 缺失1005-1006
        {"timestamp": 1008, "value": 23.1},
        {"timestamp": 1009, "value": 23.0},
    ]

    pre = Preprocessor()
    cleaned = pre.process(raw_input)
    print(json.dumps(cleaned, indent=2))

运行与验证

运行方式:将代码保存为preprocess.py,在终端执行python3 preprocess.py。

预期输出:程序打印清洗后的JSON数组。关键验证点:

  • 时间戳1002处的噪声值45.2被中值滤波修正为22.5左右(窗口内中值);
  • 时间戳1005、1006被插值填充,温度值介于22.7和22.9之间线性过渡;
  • 所有时间戳为整数毫秒,温度值均为1位小数。

通过条件:输出中不存在原始噪声值45.2,缺失区间被合理填充,且每条记录字段完整、格式统一。若需处理其他传感器数据,只需调整输入JSON的字段名与process方法中的键名映射。