2、数据预处理基础:时间戳对齐、异常值检测与缺失值插补

各位同学,欢迎来到数据预处理这一章。说实话,我在嵌入式气象站项目里踩过最多的坑,不是传感器选型,也不是通信协议,而是——数据预处理。你想想看,传感器采集回来的原始数据,就像刚从泥里挖出来的土豆,不洗干净根本没法下锅。

这一章我们聚焦三个核心问题:时间戳对齐异常值检测缺失值插补。这三个环节做好了,后面的融合算法才能跑得稳。我个人习惯把预处理叫做「数据清洗三部曲」,少一步都不行。

2.1 时间戳对齐:让不同传感器「说同一种语言」

先问一个问题:风速传感器每1秒采集一次,温湿度传感器每2秒采集一次,气压传感器每5秒采集一次。它们的数据到了融合模块,时间点对不上,怎么办?

这就是时间戳对齐要解决的问题。说白了,就是要把不同采样率的数据,统一到同一个时间基准上。

2.1.1 常见对齐策略

我在项目中用过三种方法,各有适用场景:

方法 原理 适用场景
最近邻对齐 取时间差最小的采样点 采样率接近、允许微小偏差
线性插值对齐 在两个采样点之间线性估算 采样率差异大、需要平滑过渡
重采样对齐 统一到固定频率(如1Hz) 多传感器融合、固定时间窗口

我个人最常用的是重采样对齐。举个例子,风速是10Hz,温湿度是1Hz,我会把温湿度数据通过线性插值,也变成10Hz。这样所有数据都在同一个时间轴上,后续处理就简单了。

核心原则:对齐后的时间戳,必须保证数据完整性。不要为了对齐而丢掉有效数据。

2.1.2 代码实现:重采样对齐

下面这段代码,是我在某个气象站项目里实际用过的。它能把不同采样率的数据统一到1Hz:

import pandas as pd
import numpy as np

def resample_alignment(data_dict, target_freq='1S'):
    """
    将多个传感器数据重采样到统一频率
    data_dict: {'sensor_name': DataFrame with datetime index}
    target_freq: 目标频率,如 '1S' 表示1秒
    """
    aligned_data = {}
    for name, df in data_dict.items():
        # 先确保时间索引是datetime类型
        df.index = pd.to_datetime(df.index)
        # 重采样并线性插值
        resampled = df.resample(target_freq).interpolate(method='linear')
        aligned_data[name] = resampled
    return aligned_data

# 使用示例
wind_data = {'time': ['2024-01-01 00:00:00', '2024-01-01 00:00:01'], 'speed': [3.2, 3.5]}
temp_data = {'time': ['2024-01-01 00:00:00', '2024-01-01 00:00:02'], 'temp': [22.1, 22.3]}
# 实际使用时需要构建DataFrame并设置时间索引

我的经验:重采样时注意边界处理。如果目标时间点落在数据范围之外,建议用前向填充或后向填充,不要用插值——插值会「无中生有」,产生虚假数据。

2.2 异常值检测:揪出那些「离谱」的数据

传感器不是完美的。电磁干扰、通信丢包、传感器老化,都会产生异常值。我记得有一次,风速传感器突然跳出一个99.9m/s的值——这都赶上12级台风了,可当时明明是个大晴天。

异常值检测,就是要把这些「离谱」的数据找出来。我常用的方法有两种:3σ原则IQR方法

2.2.1 3σ原则:适合正态分布的数据

3σ原则基于正态分布假设。简单说,如果数据服从正态分布,那么99.7%的数据会落在均值±3个标准差范围内。落在范围外的,就是异常值。

公式很简单:

  • 上界 = μ + 3σ
  • 下界 = μ - 3σ
  • 超出上下界的,标记为异常

但要注意——3σ原则对异常值本身很敏感。如果数据里已经混入了几个很大的异常值,均值和标准差都会被拉偏,导致检测失效。我遇到过这种情况,后来改用「迭代3σ」:先剔除明显异常,再重新计算均值和标准差,反复几次。

2.2.2 IQR方法:更稳健的选择

IQR(四分位距)方法不依赖正态分布假设,对异常值更鲁棒。它的原理是:

  1. 计算数据的Q1(25%分位数)和Q3(75%分位数)
  2. IQR = Q3 - Q1
  3. 下界 = Q1 - 1.5 × IQR
  4. 上界 = Q3 + 1.5 × IQR

超出上下界的,就是异常值。这个1.5倍是经验值,我在项目中一般用1.5,但如果数据波动特别大,会放宽到3倍。

对比总结:

  • 数据接近正态分布 → 用3σ原则
  • 数据分布未知或有偏 → 用IQR方法
  • 数据量小(<30个点)→ 两种方法都不太可靠,建议人工检查

2.2.3 代码实现:两种异常检测方法

import numpy as np

def detect_outliers_3sigma(data):
    """基于3σ原则检测异常值"""
    mean = np.mean(data)
    std = np.std(data)
    upper = mean + 3 * std
    lower = mean - 3 * std
    outliers = (data < lower) | (data > upper)
    return outliers, lower, upper

def detect_outliers_iqr(data, multiplier=1.5):
    """基于IQR方法检测异常值"""
    q1 = np.percentile(data, 25)
    q3 = np.percentile(data, 75)
    iqr = q3 - q1
    lower = q1 - multiplier * iqr
    upper = q3 + multiplier * iqr
    outliers = (data < lower) | (data > upper)
    return outliers, lower, upper

# 示例数据
sensor_data = np.array([22.1, 22.3, 22.0, 22.2, 99.9, 22.1, 22.4])
outliers_3s, _, _ = detect_outliers_3sigma(sensor_data)
outliers_iqr, _, _ = detect_outliers_iqr(sensor_data)

print(f"3σ检测到的异常索引: {np.where(outliers_3s)[0]}")
print(f"IQR检测到的异常索引: {np.where(outliers_iqr)[0]}")

避坑指南:我曾经在一个项目中,直接用3σ原则检测风速数据,结果把正常的阵风(比如8m/s)也标记为异常了。为什么?因为那段时间平均风速只有2m/s,标准差很小,8m/s确实超过了3σ。但气象学上,阵风达到4倍平均风速是正常的。所以——异常值检测一定要结合领域知识,不能纯靠统计。

2.3 缺失值插补:把「断掉」的数据接起来

传感器偶尔会掉线,通信偶尔会丢包。数据出现缺失是常态,不是异常。处理缺失值,我常用的方法是线性插值前向填充

2.3.1 线性插值:适合缓慢变化的数据

线性插值假设两个已知点之间的变化是线性的。对于温度、气压这类变化缓慢的物理量,效果很好。

公式:y = y₁ + (y₂ - y₁) × (x - x₁) / (x₂ - x₁)

说白了,就是在两个点之间画一条直线,缺失点就在这条直线上。

2.3.2 前向填充:适合快速变化或离散数据

前向填充(Forward Fill)就是用上一个有效值填充缺失值。比如风向数据,变化很快,用线性插值反而会「制造」出实际不存在的风向。这时候用前向填充更合理。

我个人的经验法则:

  • 连续变化量(温度、气压、湿度)→ 线性插值
  • 离散量或突变量(风向、降雨、开关量)→ 前向填充
  • 缺失连续超过5个点 → 两种方法都不建议,需要标记为「数据不可用」

2.3.3 代码实现:两种插补方法

import pandas as pd
import numpy as np

# 构造带缺失值的数据
time = pd.date_range('2024-01-01 00:00:00', periods=10, freq='1S')
temp = [22.1, 22.3, np.nan, np.nan, 22.6, 22.8, np.nan, 23.0, 23.1, 23.2]
df = pd.DataFrame({'temp': temp}, index=time)

# 线性插值
df_linear = df.interpolate(method='linear')
print("线性插值结果:")
print(df_linear)

# 前向填充
df_ffill = df.ffill()
print("\n前向填充结果:")
print(df_ffill)

我的小技巧:在实际项目中,我不会只用一种方法。我会先看缺失值的分布——如果缺失是随机的、零散的,用线性插值;如果缺失是连续的(比如传感器掉线了5秒),我会先用前向填充,再用线性插值平滑一下。嗯,这算是我自己摸索出来的「组合拳」。

2.4 本章小结

数据预处理看起来琐碎,但它是整个数据融合流程的基石。我见过太多项目,算法模型调得再好,预处理没做好,结果一塌糊涂。

记住三个要点:

  1. 时间戳对齐:重采样+插值,让所有传感器「步调一致」
  2. 异常值检测:3σ原则适合正态分布,IQR方法更稳健,但都要结合领域知识
  3. 缺失值插补:线性插值适合连续量,前向填充适合离散量,别搞混了

下一章,我们会把这些预处理后的数据,真正送入融合算法。到时候你会发现——预处理做得越扎实,融合效果就越稳。这是我在无数个熬夜调试的夜晚,用血泪换来的经验。

公众号:蓝海资料掘金营,微信deep3321