3、数据预处理与清洗:缺失值处理、异常值检测、时间序列对齐、数据标准化

各位同学,欢迎来到数据预处理这一章。

说实话,在车载HMI这个领域,我见过太多团队花80%的时间调模型,结果效果差得离谱。为什么?因为数据没洗干净。你想想看,车载数据有多脏——传感器偶尔断连、GPS信号飘忽、用户操作日志里一堆乱码。我个人的习惯是,拿到数据的第一件事,不是急着分析,而是先「洗菜」。

这一章,我们就来聊聊怎么把这堆「脏数据」收拾利索。

3.1 缺失值处理:别让「空」坑了你

车载数据里,缺失值太常见了。比如用户开车进隧道,GPS信号丢了;或者某个传感器临时故障,几秒钟没上报数据。这些「空」如果不处理,模型会直接罢工。

常见的缺失值类型:

  • 完全随机缺失(MCAR):比如设备突然断电,数据完全丢失。这种情况很少见,但一旦发生,直接删除就行。
  • 随机缺失(MAR):比如用户进入隧道导致GPS丢失,但其他传感器还在工作。这种缺失跟其他变量有关,需要小心处理。
  • 非随机缺失(MNAR):比如用户故意关掉某个功能,导致数据不记录。这种最麻烦,我建议先跟产品经理聊聊,搞清楚用户行为背后的逻辑。

我常用的处理方法:

  1. 直接删除:如果缺失比例小于5%,而且数据量够大,直接删掉整行。简单粗暴,但有效。
  2. 均值/中位数填充:对于连续型数据,比如车速、转速,用中位数填充更稳健。我在项目中遇到过,用均值填充反而引入了偏差,因为车载数据经常有长尾分布。
  3. 前向/后向填充:时间序列数据里,用前一时刻的值填充后一时刻的缺失。比如GPS信号短暂丢失,用上一秒的位置补上,逻辑上说得通。
  4. 插值法:线性插值或样条插值,适合平滑变化的数据,比如加速度、方向盘转角。

重点提醒:千万别在缺失值处理上「一刀切」。我曾经有个项目,用均值填充了所有缺失的刹车踏板深度数据,结果模型预测的刹车意图完全不准。后来发现,用户急刹车时数据缺失率特别高,均值填充把那些极端情况全抹平了。

# 示例:用pandas处理缺失值
import pandas as pd
import numpy as np

# 读取车载传感器数据
df = pd.read_csv('vehicle_sensor_data.csv')

# 检查缺失值
print(df.isnull().sum())

# 方法1:删除缺失比例小于5%的列
df.dropna(thresh=len(df)*0.95, axis=1, inplace=True)

# 方法2:中位数填充车速数据
df['speed'].fillna(df['speed'].median(), inplace=True)

# 方法3:前向填充GPS数据
df['gps_lat'].fillna(method='ffill', inplace=True)
df['gps_lon'].fillna(method='ffill', inplace=True)

# 方法4:线性插值处理方向盘转角
df['steering_angle'].interpolate(method='linear', inplace=True)

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

车载数据里,异常值比缺失值更隐蔽。比如一辆车突然显示时速300公里,或者刹车踏板深度超过100%。这些数据要么是传感器故障,要么是记录错误。

我个人习惯用两种方法结合来检测异常值:

  • 3σ原则:假设数据服从正态分布,超过均值±3倍标准差的值视为异常。适合车速、转速这类相对稳定的数据。
  • 箱线图法(IQR):用四分位距(Q3 - Q1)来界定异常值。低于Q1-1.5*IQR或高于Q3+1.5*IQR的值,视为异常。这个方法对非正态分布的数据更友好。

我的小技巧:对于车载数据,我建议先做领域知识过滤。比如,一辆家用轿车的最高时速不会超过250公里,那超过这个值的直接标为异常。别完全依赖统计方法,否则会把真实的急加速场景误判为异常。

# 示例:异常值检测
from scipy import stats

# 3σ原则检测车速异常
z_scores = np.abs(stats.zscore(df['speed']))
df['speed_outlier_3sigma'] = z_scores > 3

# 箱线图法检测刹车深度异常
Q1 = df['brake_depth'].quantile(0.25)
Q3 = df['brake_depth'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['brake_outlier_iqr'] = (df['brake_depth'] < lower_bound) | (df['brake_depth'] > upper_bound)

# 领域知识过滤:刹车深度不可能超过100%
df['brake_outlier_domain'] = df['brake_depth'] > 100

注意:异常值不一定要删除。我曾经处理过一个案例,用户在山路频繁急刹车,数据里出现了大量「异常」的刹车深度值。这些其实是真实驾驶行为,不是传感器故障。所以,先搞清楚异常值产生的原因,再决定是删除、修正还是保留。

3.3 时间序列对齐:让所有数据「同步」

车载系统里,不同传感器的采样频率不一样。GPS可能每秒更新一次,方向盘转角每10毫秒记录一次,而发动机转速每100毫秒上报一次。如果不做时间对齐,你根本没法把不同数据关联起来分析。

对齐的两种思路:

  • 降采样:把所有数据统一到最低频率。比如GPS是1Hz,其他数据也降到1Hz。优点是简单,缺点是丢失了高频信息。
  • 升采样:把所有数据统一到最高频率。比如方向盘转角是100Hz,其他数据也插值到100Hz。优点是保留细节,缺点是计算量大。

我个人更推荐升采样,尤其是做驾驶行为分析时,高频数据往往藏着关键信息。比如用户快速打方向盘的动作,如果降采样到1Hz,根本看不出来。

# 示例:时间序列对齐
# 假设有两个DataFrame:gps_data(1Hz)和 steering_data(100Hz)

# 将时间列转为datetime类型
gps_data['timestamp'] = pd.to_datetime(gps_data['timestamp'])
steering_data['timestamp'] = pd.to_datetime(steering_data['timestamp'])

# 设置时间索引
gps_data.set_index('timestamp', inplace=True)
steering_data.set_index('timestamp', inplace=True)

# 升采样GPS数据到100Hz,使用前向填充
gps_resampled = gps_data.resample('10ms').ffill()

# 合并两个DataFrame
aligned_data = pd.merge_asof(steering_data, gps_resampled, 
                             left_index=True, right_index=True, 
                             direction='nearest')

避坑指南:我曾经在时间对齐上栽过跟头。当时用merge_asof做最近邻匹配,结果因为两个时间戳的精度不一致(一个精确到毫秒,一个精确到秒),匹配出来的数据全是错的。后来我统一把所有时间戳都转成纳秒级,问题才解决。记住,时间对齐前,先检查时间戳的精度和时区。

3.4 数据标准化:让不同量纲的数据「公平竞争」

车载数据里,量纲差异巨大。车速是0-200 km/h,方向盘转角是-540°到540°,而刹车深度是0-100%。如果不做标准化,模型会天然偏向数值大的特征。

两种主流方法:

方法 公式 适用场景 我的经验
Z-score标准化 (x - μ) / σ 数据近似正态分布,无严格上下界 适合车速、加速度这类数据
Min-Max归一化 (x - min) / (max - min) 数据有明确上下界,需要映射到[0,1] 适合刹车深度、油门开度这类百分比数据

你想想看,如果直接用原始数据训练模型,车速这个特征可能会主导整个模型,而方向盘转角的影响被完全淹没。标准化之后,所有特征都在同一个尺度上,模型才能公平地学习每个特征的重要性。

# 示例:数据标准化
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Z-score标准化
scaler_z = StandardScaler()
df[['speed', 'acceleration']] = scaler_z.fit_transform(df[['speed', 'acceleration']])

# Min-Max归一化
scaler_mm = MinMaxScaler()
df[['brake_depth', 'throttle']] = scaler_mm.fit_transform(df[['brake_depth', 'throttle']])

我的建议:标准化参数(均值和标准差,或最小值和最大值)一定要从训练集计算,然后应用到验证集和测试集。千万别在整个数据集上计算,否则会造成数据泄露。嗯,这个坑我踩过,后来被导师骂了一顿才记住。

小结

数据预处理与清洗,说白了就是给模型「喂好饭」。缺失值处理、异常值检测、时间序列对齐、数据标准化,这四步缺一不可。我做了这么多年车载HMI,最大的体会就是:数据质量决定了模型效果的上限,而模型算法只是逼近这个上限。

下一章,我们会聊特征工程,看看怎么从清洗好的数据里挖出更有价值的特征。到时候见。