资讯详情

Whitemud Drive高速数据集实战:轨迹清洗、特征工程与时序验证指南

📅 2026/10/21 2:41:47 | 华诺云谱 👁 阅读
Whitemud Drive高速数据集实战:轨迹清洗、特征工程与时序验证指南
简介加拿大Whitemud Drive高速公路数据集是阿尔伯塔省埃德蒙顿市一条被用作试验道路的市内高速公路的实测数据全程28公里限速80公里/小时主干道与闸道均部署地感线圈可同时观测车流量、车速与车辆密度是交通流预测、出行时间预测及可变限速VSL控制研究的关键实证数据。打包为rar压缩包约138MB包含748个文件以692个xls原始检测数据为主辅以51个jpg速度等高线图、少量xlsx汇总表、txt说明、m脚本及png图片便于快速理解数据结构并开展可视化分析。目前已有2492人学习下载尤其适合智能交通方向的研究生、工程师用于短期出行需求预测、瓶颈路段检测、天气对可变限速策略影响分析以及高速公路通行能力评估。利用该数据集可直接开展交通流模型验证、出行时间预测建模量化评估可变限速控制对缓解拥堵、降低事故率的作用为相关科研与工程实践提供有力支撑。1. 加拿大Whitemud Drive高速公路数据集一份被低估的交通流样本第一次打开加拿大Whitemud Drive高速公路数据集时我原以为拿到的是干净整齐的表格结果却是几十万行带着坐标噪声、时间戳错位和车辆ID断裂的原始轨迹记录。这份数据集本质上是某条城市快速路在一段连续时间内的车辆移动轨迹或断面流量记录常被用于交通流预测、跟车行为建模、轨迹平滑与地图匹配等任务。它和常见的仿真交通数据最大的区别是真实、琐碎、有大量测量噪声而这恰恰是它最有价值的地方。适合的人群很明确——做智能交通算法落地、时序预测或者轨迹挖掘的工程师和研究者。新手可以拿它练数据清洗基本功熟手可以用它验证模型在真实噪声下的鲁棒性。它不是什么标准答案而是一块需要自己动手打磨的原材料。2. 从压缩包到结构化数据Whitemud Drive 文件解析与格式确认拿到.rar压缩包后很多人第一件事就是解压然后直接写代码读数据结果被字段名、分隔符和编码问题折腾半天。我的习惯是先做一次“文件体检”把数据集的物理形态摸清楚再决定用哪套解析方案。这一步做扎实了后面所有清洗和建模工作才会顺。2.1 解压先别急着跑代码文件类型识别与快速预览无论你在哪台机器上工作解压只是第一步。真正要关心的是解压后目录里到底装了什么——是单个大 CSV还是分了多个子目录的原始采集片段。常见做法是先用系统命令做最基本的探查不要急着打开 Python。# 解压到独立目录避免污染工作区 mkdir -p whitemud_raw unzip -o whitemud_dataset.rar -d whitemud_raw # 或者用 unrar 处理 .rar 格式 unrar x whitemud_dataset.rar whitemud_raw/ # 看目录结构判断是单文件还是多文件分片 find whitemud_raw -type f | head -20 # 对疑似数据文件做类型识别 file whitemud_raw/*.csv whitemud_raw/*.txt这段命令的核心价值是让你在写任何解析代码之前先确认三个关键信息文件数量、文件格式、字符编码。unrar x和unzip -o的区别在于前者保留目录结构后者把文件解到指定目录如果你在 Windows 上工作可以用 WinRAR 或 7-Zip 的图形界面效果一样。file命令会告诉你文件到底是纯文本 CSV、制表符分隔的 TSV还是某种二进制格式——很多交通采集设备会输出带 BOM 头的 UTF-8 文件肉眼看不出来但file能显示出来。拿到文件清单后我一般会再抽几行数据看一眼:# 看第一行确认是表头还是直接是数据 head -5 whitemud_raw/trajectories.csv # 统计行数和列数建立初步规模认知 wc -l whitemud_raw/trajectories.csv这一步看起来基础但能避免一个非常常见的翻车场景直接按表头名取列结果发现这个文件根本没有表头字段顺序全靠猜。如果head显示第一行是时间戳和数字说明没有表头你就得在解析时手动指定列名。2.2 字段级拆解时间戳、位置坐标与车辆ID的对应关系这类高速公路轨迹数据集的字段设计大同小异核心信息通常是车辆ID、时间戳、位置坐标经纬度或平面坐标、瞬时速度、车道编号和目标车距。Whitemud Drive 这类城市快速路数据集里因为采集设备可能是路侧单元或车载 GPS字段的命名和单位会存在差异最常见的问题是坐标到底走的是 WGS84 经纬度还是某种投影坐标。我一般会在解析脚本里加一个“schema 探针”把每一列的类型、缺失比例和样例值打出来而不是凭感觉写死。import pandas as pd # 先只读前 5000 行做快速探查避免一次性载入大文件卡死 df_sample pd.read_csv( whitemud_raw/trajectories.csv, nrows5000, sep, ) print(df_sample.dtypes) print(df_sample.head(3)) print(缺失比例) print(df_sample.isnull().mean().round(4))这段代码有三个关键设计第一nrows5000限定了读取行数几百 MB 的文件也能秒开先看清楚结构再决定要不要全量加载。第二dtypes能直接暴露字段类型问题比如时间戳被读成了object而不是datetime64说明原始格式不是标准 ISO 字符串。第三缺失比例是第一个需要记录的数据质量指标如果某列缺失达到 30%下游做特征工程时就得考虑是插值还是丢弃。拿到 schema 之后重点确认三件事。时间戳字段要统一成 UTC 还是本地时间坐标字段是经纬度还是 UTM 投影坐标车辆 ID 是唯一标识还是存在复用。其中车辆 ID 复用是这类数据集的普遍坑点——某个 ID 可能代表一段连续轨迹但也可能在同一时间片内出现两次说明采集端做了轨迹拼接或重识别。我会用分组统计的方式快速验证# 同一辆车是否出现在多个不连续时间段 grp df_sample.groupby(vehicle_id)[timestamp] stats pd.DataFrame({ count: grp.count(), time_span: grp.max() - grp.min(), }) print(stats.describe())逻辑上如果某辆车的time_span超过一个合理的行程时长比如 2 小时或者count出现极端峰值基本可以确定这个车辆 ID 不是一次完整的跟车记录而是被多个传感器拼接出来的。这个判断直接影响后续轨迹切片方式千万别把两段不同时间、不同位置的轨迹当成一辆车的连续行为来建模。3. 把原始轨迹变成可训练样本清洗、切片与特征工程结构摸清之后下一步就是让数据变得“可用”。原始轨迹数据直接喂给模型基本不可能收敛因为里面充满了传感器噪声、时间戳抖动和无效定位。这个阶段的工作决定了模型效果的天花板投入产出比远高于调参。3.1 轨迹清洗四板斧去漂移、补缺失、对齐时间、过滤静置我处理轨迹数据有一个固定流程先去坐标漂移再补时间缺口然后统一时间基准最后把原地不动的车辆过滤掉。四步做完数据质量会有一个质的提升。漂移是 GPS 轨迹最常见的噪声表现是车辆位置在极短时间内跳出去几百米又跳回来。判断标准可以用“连续两点间的位移速度是否超过物理极限”比如一辆车在 1 秒内位移了 300 米那瞬时速度就是 300 m/s这在城市快速路上是不可能的。import numpy as np import pandas as pd def haversine_distance(lat1, lon1, lat2, lon2): 计算两个经纬度点的球面距离米 R 6371000.0 phi1, phi2 np.radians(lat1), np.radians(lat2) dphi np.radians(lat2 - lat1) dlambda np.radians(lon2 - lon1) a np.sin(dphi/2)**2 np.cos(phi1)*np.cos(phi2)*np.sin(dlambda/2)**2 return 2 * R * np.arcsin(np.sqrt(a)) df[prev_lat] df.groupby(vehicle_id)[lat].shift(1) df[prev_lon] df.groupby(vehicle_id)[lon].shift(1) df[dist] haversine_distance(df[lat], df[lon], df[prev_lat], df[prev_lon]) df[dt] df.groupby(vehicle_id)[timestamp].diff().dt.total_seconds() # 瞬时速度超过 60 m/s 视为漂移点约 216 km/h超过快速路限速 df[speed_estimate] df[dist] / df[dt].replace(0, np.nan) drift_mask df[speed_estimate] 60.0 print(f漂移点数量: {drift_mask.sum()})这里的关键参数是速度阈值 60 m/s你可以根据道路实际限速调整。Whitemud Drive 这类城市快速路限速一般在 80-100 km/h取 60 m/s 已经是相当宽松的上限能保住正常超速车辆同时把绝大多数定位跳变过滤掉。groupby(vehicle_id).shift(1)保证了计算相邻点距离时不会跨车辆计算避免把前一辆车的终点和后一辆车的起点连到一起。时间对齐解决的是另一个问题不同采集设备的时间戳精度可能不一致有的精确到秒有的精确到毫秒。如果模型输入需要固定时间间隔就得做重采样。# 把每辆车的轨迹按 1 秒间隔重采样使用线性插值填补缺失 def resample_trajectory(group): group group.set_index(timestamp).sort_index() new_index pd.date_range(group.index.min(), group.index.max(), freq1S) return group.reindex(new_index).interpolate(methodlinear) df_aligned df.groupby(vehicle_id, group_keysFalse).apply(resample_trajectory)重采样的代价是会产生一部分插值点插值点的可信度低于原始观测点。我一般在重采样后保留一列is_interpolated标记方便下游做训练时筛选或加权。如果原始数据采样频率本身是 10Hz就没必要强行重采样到 1Hz保持原始频率反而更好。最后是静置过滤。城市快速路虽然通畅但某些时段的拥堵可能让车辆在原地停留。静止点本身对速度预测模型是有效训练样本但对轨迹平滑和跟车模型来说长时间静置会让数据分布偏斜。我会把连续静止超过 60 秒的片段单独切掉。3.2 用滑动窗口切出模型输入序列构造与目标字段设计清洗完的数据还是表格形态需要把它切成“序列样本”才能喂给时序模型。这里最常用的做法是固定窗口滑动切片——用过去 N 秒的位置和速度预测未来 M 秒的速度或位置。窗口长度和预测步长是模型效果最敏感的两个超参数。def create_sequence_samples(df, window10, horizon5, step2): 从轨迹表构造 (输入序列, 预测目标) 样本对 - window: 输入序列长度单位秒 - horizon: 预测未来多少秒 - step: 滑动步长控制样本重叠度 samples, targets [], [] for veh_id, group in df.groupby(vehicle_id): group group.sort_values(timestamp) if len(group) window horizon: continue values group[[speed, acceleration, lane]].values for start in range(0, len(group) - window - horizon 1, step): x values[start:start window] y group[speed].values[start window:start window horizon] samples.append(x) targets.append(y.mean()) return np.array(samples), np.array(targets)三个参数需要重点说明。window10表示用过去 10 个观测点做输入在 1Hz 重采样下就是 10 秒的历史信息horizon5表示预测未来 5 秒的平均速度step2控制相邻样本的时间重叠度step 越小样本越多但相关性越高训练时需要用 shuffle 打散。一个血泪经验是如果 step 设成了 1相邻样本几乎完全相同模型会严重过拟合验证集上表现很好但换到新一天的数据上直接崩掉。目标字段的设计要和任务对齐。如果目标是预测平均速度用y.mean()取未来 5 秒的均值是合理的如果目标是预测轨迹坐标就得把未来的经纬度序列作为目标那y就应该是二维数组而不是标量。这个选择必须在建模前敲定因为不同目标定义的模型能力边界完全不同。3.3 落盘为 parquet按时间段分片避免全量读内存清洗和切片完成之后数据量通常会膨胀不少尤其是做了重采样和滑动窗口之后。几十万行原始数据可能变成几百万个样本全部塞进 CSV 会让后续每次训练都卡在 I/O 上。我习惯在这一步把中间结果转成列式存储格式按天或按小时分片训练时按需加载。import pyarrow.parquet as pq import pyarrow as pa # 按日期列分片每个分片包含一个完整时间段的数据 df[date] df[timestamp].dt.date for date, group in df.groupby(date): table pa.Table.from_pandas(group) pq.write_table(table, fwhitemud_processed/{date}.parquet) print(f已写入 {date}: {len(group)} 行)这里按日期分片的逻辑是交通流数据有极强的昼夜周期特性按天切分能方便你做训练集/验证集的时间隔离避免数据穿越。如果用随机切分某天的早高峰数据可能同时出现在训练集和验证集里模型评估结果会虚高。parquet 的另一个优势是压缩率高同样的数据比 CSV 通常小 60%-70%而且读取时可以只选择需要的列内存压力小很多。4. Whitemud Drive 数据集实战中的 5 个坑现象、原因与解决这是整个流程里最值得写下来的部分。真实数据集的坑往往不在模型复杂度上而在数据本身的隐蔽问题。以下 5 个坑我基本每个都踩过写出现象、原因和解决方式帮你绕开。4.1 坐标跳变一辆车 1 秒内位移了 300 米现象清洗前直接可视化轨迹发现某些车辆的路径上出现脱离道路的“飞点”位置直接跳到几公里外的街区下一秒又回到道路上。原因这是 GPS 定位漂移或地图匹配失败在城市场景中尤其常见高楼反射和多路径效应会严重干扰定位。解决按速度阈值过滤计算相邻点距离和间隔时间瞬时速度超过 60 m/s 的点直接剔除然后用前后有效点做插值修复。阈值设置要留足余量——高速环境下车辆也可能短时间达到 130 km/h 约 36 m/s所以 60 m/s 能保留真实高速行驶记录只去掉明显不可能的点。4.2 时间戳格式混乱一会是本地时间一会是 UTC现象把数据按时间排序后发现同一辆车的轨迹时间出现“倒流”或者相邻时间段之间的间隔忽大忽小。原因采集设备在数据传输过程中一部分设备用了本地时区一部分用了 UTC写入数据集时没有统一转换。还有可能是跨了夏令时切换节点。解决统一转成 UTC 时间戳再做排序和重采样。具体做法是先识别字段里的时区偏移量再用tz_localize和tz_convert统一到 UTC宁可全部转成一个基准也不要保留混合时区。处理完后做一次时间单调性校验确保每个vehicle_id内部的时间戳严格递增。4.3 车辆 ID 复用同一个 ID 出现在两小时后完全不同的位置现象某辆车的轨迹时间跨度长达 3 小时行驶里程超过 200 公里但该快速路全长通常不到几十公里数据存在明显的不合理。原因采集系统对不同传感器段的车辆做了重识别一旦车辆在某个断面被跟丢又在新断面重新出现系统可能沿用旧 ID导致同一 ID 下拼接了两段不同行程。解决按时间间隔切分轨迹。检查相邻点的时间差如果超过一个阈值我一般用 120 秒认为车辆轨迹断开把同一 ID 切成两条独立轨迹。切分后分别生成新的轨迹 ID避免把不连续的时间段当成一个序列建模。4.4 特征泄漏样本构造时不小心把未来信息放进输入现象模型在验证集上指标极好但部署到新数据上预测效果大幅下降速度预测偏差超过 30%。原因滑动窗口切片时用了包含未来点的行做当前样本的输入特征最常见的是在特征工程阶段计算了“整段轨迹的平均速度”作为特征但这个统计量是在样本构造后算的包含了未来信息。解决严格按时间顺序构造样本所有特征只允许使用当前窗口之前的数据。一个实用的自检方法是把样本的输入特征矩阵打乱后重新训练如果效果几乎不变说明特征本身就带着泄漏如果效果大幅下降才说明时间依赖是真的有效。4.5 数据不平衡白天黑夜的样本量差距悬殊现象数据集里白天样本占 80% 以上夜晚样本稀疏模型在夜间场景下的预测误差明显偏高。原因城市快速路的车流量本身就有强时段性夜晚车流少是客观规律采集到的轨迹数量自然少。解决不要盲目对少数类做重采样而是先按小时统计样本分布再决定是否按时间段分层采样。我的做法是训练时按小时分层采样保证每个小时的样本量被压到相近水平或者在评估指标上分开看白天和夜间的表现接受夜间数据不足的现实不做过度增强。5. 用按天留出法验证模型从随机切分翻车到时间感知评估最后一个章节想分享一个具体技巧交通流数据的模型验证必须用时间隔离的方式而不是随机切分。这是我最惨痛的一次翻车经历——当时做速度预测按随机 8:2 切分训练集和验证集验证集误差 4%感觉很完美。结果模型被放到新一周的数据上测试误差直接飙到 21%。原因很简单高速公路数据是强时间序列随机切分会让同一辆车的轨迹片段同时出现在训练集和验证集里模型实际上记住了车辆个体的行为模式而不是学到了通用的交通流规律。要验证真实泛化能力必须在时间维度上做隔离。做法是按天留出比如把前 6 天做训练第 7 天做验证。如果数据跨度更长可以用滑动的时序交叉验证每次换不同的天做验证集。days sorted(df[date].unique()) train_days days[:-1] # 前 N-1 天 test_day days[-1] # 最后一天做验证 train_df df[df[date].isin(train_days)] test_df df[df[date] test_day] # 训练集和验证集之间没有任何时间重叠 print(f训练集天数: {len(train_days)}, 验证集: {test_day})这个验证方式的附加好处是能观察模型在不同日期类型上的表现差距。比如验证集落在周一早高峰的预测误差明显高于周二周三说明模型还没有学好周周期特征。我会再用一个分组指标来定位问题是否出在特定时段test_df[hour] test_df[timestamp].dt.hour hourly_error test_df.groupby(hour).apply( lambda g: np.abs(g[pred] - g[true]).mean() ) print(hourly_error)看到误差高峰出现在清晨 6-9 点时我通常选择在训练数据里加一周同小时的历史统计特征而不是盲目把模型加宽加深。这种做法在 Whitemud Drive 这类真实交通数据集上非常管用——它逼你从“模型结构”的执念里走出来正视数据分布本身的时间结构。现在做数据项目我再也不会用随机切分来评估模型了。时间感知的评估方式虽然让验证集上的数字没那么好看但它骗不了人模型上线后的表现心里有底得多。这个习惯是从一次次的翻车里换来的希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑