保姆级教程:用NumPy和Pandas搞定METR-LA交通数据集预处理(附完整代码)
2026/6/3 14:41:02 网站建设 项目流程

从零掌握METR-LA交通数据预处理:NumPy与Pandas实战指南

当第一次打开METR-LA交通数据集的.h5文件时,许多开发者都会陷入困惑——原始数据是二维的N×T矩阵,但模型需要的是N×T×12的三维结构。这种维度转换不仅是简单的格式调整,更关系到模型能否有效捕捉交通流的时间依赖性。本文将用工程化的视角,带你完整实现从原始数据到模型可用格式的蜕变过程。

1. 理解METR-LA数据集的核心结构

METR-LA数据集记录了洛杉矶高速公路207个传感器在2012年3月至6月间的交通速度数据,采样间隔5分钟。原始数据存储为34272个时间点×207个传感器的二维表格,这种"平面"结构无法直接用于时空预测模型。

关键认知突破点

  • 原始数据中的每列代表一个传感器,每行是一个时间点的全网络快照
  • 时间序列预测需要滑动窗口提取连续时间片段
  • 最终需要构造(样本数, 时间步长, 传感器数, 特征维度)的四维张量
import pandas as pd df = pd.read_hdf('metr-la.h5') print(f"数据集形状:{df.shape}") # 输出 (34272, 207)

2. 滑动窗口机制深度解析

时空预测模型的典型范式是用过去12个时间步预测未来12个时间步。这需要精心设计偏移量系统:

import numpy as np x_offsets = np.arange(-11, 1, 1) # [-11, -10,..., 0] y_offsets = np.arange(1, 13, 1) # [1, 2,..., 12]

窗口滑动原理

  1. 每个样本由连续24个时间点组成
  2. 前12个时间点(x)作为输入特征
  3. 后12个时间点(y)作为预测目标
  4. 窗口以步长1滑动,生成数万个训练样本

注意:要预留足够的边缘数据,避免越界访问。对于12步预测,首尾各需要保留11个时间点不被采样。

3. 时间特征工程实战

原始数据仅含交通速度,但加入时间特征能显著提升模型性能。我们将时间标准化为一天中的相对位置:

# 将时间戳转换为当天的相对位置(0-1范围) time_ind = (df.index.values - df.index.values.astype("datetime64[D]")) / np.timedelta64(1, "D") # 扩展为三维结构(时间步, 传感器, 特征) time_in_day = np.tile(time_ind, [1, num_nodes, 1]).transpose((2, 1, 0))

特征合并技巧

  1. 使用np.expand_dims为速度数据增加特征维度
  2. 通过np.concatenate合并速度和时序特征
  3. 最终数据形状变为(34272, 207, 2)
data = np.expand_dims(df.values, axis=-1) # (34272,207,1) data = np.concatenate([data, time_in_day], axis=-1) # (34272,207,2)

4. 样本生成与数据集拆分

核心采样算法需要处理三个关键问题:

  1. 避免访问越界的时间点
  2. 高效生成数万个样本对
  3. 保持时空数据的连续性
min_t = abs(min(x_offsets)) # 11 max_t = abs(len(data) - max(y_offsets)) # 34260 samples = [] for t in range(min_t, max_t): x_t = data[t + x_offsets] # (12,207,2) y_t = data[t + y_offsets] # (12,207,2) samples.append((x_t, y_t)) x, y = np.stack(samples, axis=0) # (34249,12,207,2)

数据集拆分最佳实践

  • 训练集:70%(约23974个样本)
  • 验证集:10%(约3425个样本)
  • 测试集:20%(约6850个样本)

使用np.savez_compressed保存数据集,既节省空间又保持数据完整:

np.savez_compressed( "train.npz", x=x_train, y=y_train, x_offsets=x_offsets, y_offsets=y_offsets )

5. 工程化改进与性能优化

原始实现存在三个可以优化的关键点:

  1. 内存优化
    • 使用生成器替代列表存储样本
    • 分块处理超大数据集
def sample_generator(data, x_offsets, y_offsets, batch_size=1000): for t in range(min_t, max_t, batch_size): batch_x = data[t + x_offsets] batch_y = data[t + y_offsets] yield batch_x, batch_y
  1. 并行处理

    • 使用multiprocessing加速样本生成
    • 对每个传感器独立处理
  2. 数据验证

    • 检查NaN值并合理填充
    • 验证时间连续性
# 检查时间连续性 time_diff = np.diff(df.index.values.astype('int64')) assert np.all(time_diff == 5*60*1e9) # 5分钟间隔

6. 完整代码架构设计

对于工业级应用,建议采用面向对象的设计模式:

class METRLAPreprocessor: def __init__(self, h5_path): self.df = pd.read_hdf(h5_path) self.num_nodes = self.df.shape[1] def add_time_features(self): # 实现时间特征添加逻辑 pass def generate_samples(self, x_steps=12, y_steps=12): # 实现样本生成逻辑 pass def split_and_save(self, output_dir): # 实现数据集拆分与保存 pass

这种封装方式提供了更好的:

  • 可维护性:各功能模块解耦
  • 可扩展性:方便添加新特征
  • 可复现性:参数集中管理

7. 常见陷阱与解决方案

陷阱1:内存爆炸

  • 现象:处理大数据集时内存耗尽
  • 解决方案:使用dask替代pandas,或分块处理

陷阱2:时间错位

  • 现象:预测结果出现相位偏移
  • 检查:验证x_offsets和y_offsets的对应关系

陷阱3:数据泄漏

  • 现象:测试集信息污染训练集
  • 防护:确保样本窗口不跨越数据集边界
# 防泄漏检查示例 assert set(test_dates) & set(train_dates) == set()

在实际项目中,我们还需要考虑:

  • 节假日特殊处理
  • 传感器故障数据的修复
  • 不同时间粒度的融合

处理交通数据就像组装精密钟表——每个齿轮(数据维度)都必须完美咬合。当第一次看到模型能够准确预测未来一小时的交通状况时,你会理解这些预处理步骤的价值。记住,好的数据准备不是机械的格式转换,而是对数据时空特性的深刻理解与重塑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询