4. 数据清洗与预处理流水线

测试数据在进入分析模型之前,几乎不可能直接可用。传感器掉线会产生缺失值,电磁干扰或量程溢出会产生异常值,多通道采集卡与总线记录仪的时间基准不一致会造成时间错位,不同设备输出的单位(如转速用 rpm 与 rad/s、温度用 ℃ 与 K)也会让后续计算失去可比性。本章要解决的核心问题,是把这些清洗动作组织成一条可重复、可追溯、可自动运行的预处理流水线,而不是每次拿到新数据都手工修补。

一、先定义清洗契约,再写清洗代码

自动化流水线的第一步不是写函数,而是明确“清洗契约”:输入数据的列名、时间列格式、采样率、单位约定,以及清洗后必须满足的约束。例如约定时间列统一为 datetime 类型、按升序排列、无重复时间戳;转速统一为 rad/s;温度统一为 ℃;缺失值用 NaN 表示而非 0。契约一旦确定,后续每个清洗函数都只做一件事,并通过断言检查输出是否满足约束。

在 MATLAB 中,推荐把原始数据读入 timetable,因为 timetable 天然携带时间轴,支持按时间对齐、重采样和同步,比裸矩阵更适合测试数据。下面是一个典型的读入与契约检查片段:

raw = readtimetable("motor_run01.csv");
raw.Properties.DimensionNames{1} = 'Time';
assert(issorted(raw.Time), '时间列必须升序');
assert(~any(ismissing(raw.Time)), '时间列不允许缺失');
vars = ["Speed_rpm","Torque_Nm","Temp_C","Current_A"];
raw = raw(:, vars);

二、缺失值:先判断类型,再选择策略

缺失值不能一律删除或一律插补。要先区分三种情况:随机单点缺失(如偶发丢包)、连续段缺失(如传感器掉线数秒)、整列缺失(如某通道未接线)。随机单点可用线性插值或样条插值;连续段缺失若超过阈值(例如超过 0.5 s 或超过 5 个采样点),插值会制造虚假趋势,应标记为无效段并排除出统计;整列缺失则应直接报错,提示检查接线或配置。

MATLAB 提供 fillmissing 与 ismissing 组合使用。对 timetable 可按时间插值:

missRatio = sum(ismissing(raw))./height(raw);
rawFilled = fillmissing(raw, "linear", "SamplePoints", raw.Time);
% 对连续缺失超过 5 点的通道,回退为 NaN 并记录
for v = vars
    idx = isnan(raw.(v));
    seg = diff([0; idx; 0]);
    starts = find(seg == 1); ends = find(seg == -1) - 1;
    longSeg = (ends - starts + 1) > 5;
    for k = find(longSeg)'
        rawFilled.(v)(starts(k):ends(k)) = NaN;
    end
end

清洗后应输出一份缺失报告:每个通道的缺失比例、最长连续缺失长度、被标记为无效的段数。这份报告是后续判断数据是否可用的依据。

三、异常值:物理边界 + 统计边界双轨判断

异常值检测建议采用“双轨制”。第一轨是物理边界:转速不可能为负、温度不可能超过传感器量程、电流不可能超过保险丝额定值。这类越界直接判为无效。第二轨是统计边界:用滑动窗口的中位数与 MAD(绝对中位差)判断局部突变,避免全局阈值被工况变化掩盖。对转速信号,可设 |x - median| > 6×1.4826×MAD 为异常。

检测到异常后不要直接删除,而是先替换为 NaN,再回到缺失值流程统一处理,这样异常与缺失共用同一套插补与报告逻辑,流水线更简洁。下面给出滑动 MAD 的实现:

win = 51; k = 6;
med = movmedian(rawFilled.Speed_rpm, win, "omitnan");
mad = movmad(rawFilled.Speed_rpm, win, "omitnan");
isOut = abs(rawFilled.Speed_rpm - med) > k * 1.4826 * mad;
rawFilled.Speed_rpm(isOut) = NaN;

四、重复采样与时间对齐

重复采样有两种:时间戳完全重复(同一时刻写入两次)和时间戳过密(采样率高于目标分析率)。前者用 unique 或 retime 去重,保留均值或首值;后者用 retime 重采样到统一网格。多设备数据则用 synchronize 按时间轴对齐,并指定插值方式与容差。

% 去重:同一时间戳取均值
rawFilled = retime(rawFilled, "secondly", "mean");
% 多源对齐:以 10 ms 网格同步,线性插值,容差 20 ms
grid = (rawFilled.Time(1):milliseconds(10):rawFilled.Time(end))';
aligned = synchronize(rawFilled, grid, "linear", "Tolerance", milliseconds(20));

对齐后必须检查时间轴是否单调、是否有大段 NaN 产生。若某通道在容差内无数据,synchronize 会填入 NaN,此时应结合缺失报告判断是否可接受。

五、单位换算:集中映射,避免散落

单位换算最容易出错的地方是“到处乘系数”。正确做法是建立一张单位映射表,在流水线末端统一执行。例如转速 rpm → rad/s 乘 2π/60,温度 K → ℃ 减 273.15,扭矩 kgf·m → N·m 乘 9.80665。换算后更新列名与单位元数据,防止后续误用。

unitMap = struct( ...
    "Speed_rpm",  @(x) x * 2*pi/60, ...
    "Temp_C",     @(x) x - 273.15, ...
    "Torque_Nm",  @(x) x * 9.80665);
aligned.Speed_rads = unitMap.Speed_rpm(aligned.Speed_rpm);
aligned = removevars(aligned, "Speed_rpm");

六、把五步串成一条可复用流水线

将上述步骤封装为函数 cleanPipeline(raw, cfg),cfg 中集中存放采样率、缺失阈值、MAD 倍数、单位映射等参数。每次运行输出清洗后的 timetable 与一份清洗报告(缺失比例、异常点数、去重条数、对齐后 NaN 比例、单位换算记录)。报告可用 writetable 落盘,作为自动化报告平台的数据质量页输入。

测试数据清洗与预处理流水线 输入:多通道 timetable | 输出:对齐、统一单位、带质量报告的干净数据 原始数据 CSV / MDF / 总线记录 ① 缺失值处理 单点→线性插值 连续段→标记无效 整列缺失→报错 ② 异常值检测 物理边界越界 滑动 MAD 突变 替换为 NaN 复用① ③ 重复采样 时间戳去重 retime 重采样 统一目标网格 ④ 时间对齐 synchronize 多源 线性插值 + 容差 检查单调与 NaN ⑤ 单位换算 rpm → rad/s K → ℃ kgf·m → N·m 干净数据 timetable 对齐 单位统一 可进入分析 异常置 NaN 后复用缺失流程 清洗质量报告(随流水线自动输出) 通道 缺失比例 异常点数 去重条数 对齐后 NaN Speed 0.8% 12 3 0.2% Torque 2.1% 27 3 1.5% Temp 0.3% 4 3 0.1%

这条流水线的价值在于:每个环节都有明确的判断依据和输出记录,异常与缺失共用同一套处理逻辑,单位换算集中管理,时间对齐有容差可查。当测试数据批次变化时,只需调整 cfg 中的阈值与映射,而不必重写清洗代码。清洗报告则直接成为自动化报告平台中“数据质量”页面的数据源,让分析结论建立在可追溯的数据基础上。