
简介PDF文档《第20期_交通路网的建模、评价及预测-报告版》面向交通工程、城市规划及相关数据科学领域的学习者与从业者聚焦利用复杂网络理论对城市路网进行建模、效率评价与状态预测并延伸到SCATS信号控制场景及路网数据修复方法适合作为课程报告、项目预研或技术综述的参考。资源为单个PDF文件大小4.76MB内容结构完整包含背景与意义、路网建模、路网评价、路网预测、路网数据修复五大板块可系统了解节点出/入度、平均路径长度、聚类系数等核心指标以及基于SCATS的周末/工作日路网状态对比、Top 5关键节点识别等具体案例和基于时间序列、机器学习的状态预测思路。目前已有146人学习适合需要快速建立城市交通路网分析框架、撰写相关报告或开展入门研究的读者。1. 从SCATS到复杂网络交通路网为什么要先建模再评价做交通数据的人应该都遇到过这个局面SCATS信号系统里线圈、信号配时、子区划分的数据全都有看起来什么都能做可真要回答哪个路口在拖累整个片区时却拿不出一张能算的图。这份报告给我最大的启发是它把路网先抽象成 URN(N, E, ES) 这样的有向图再在图上做瓶颈识别和状态预测所有讨论才有公共底座。对做智慧城市、交通规划或数据分析的人这套方法的价值在于不依赖特定厂商的数据格式只要有交叉口、路段和方向信息就能复现。接下来按建模、评价、预测、数据修复的顺序拆开讲每步都给出可以照着改的脚本和参数口径。2. 城市路网有向图建模URN、边属性与SCATS子区映射2.1 节点、边与属性集合把路网变成图论能处理的结构交通路网建模最常见做法是以交叉口为节点 N路段为边 EES 存放边的属性比如道路长度、车道数、限速、方向限制等。这个抽象看起来简单落地时有两个容易出错的地方。第一个是方向。现实中两条单向道并排如果建模成无向边就会丢失禁左、禁右、单行线这些约束。报告里 URN 明确用了有向图边集合 E 里的每条边带方向转向上需要允许掉头或左右转时得额外维护一个转向边或转向罚函数集合而不是简单在邻接矩阵里填 1。第二个是层级。路网有快速路、主干道、次干道、支路之分直接混在一个图里平均路径长度会被大量短途支路拉低。工程上常见做法是分成两层底层是完整路网用于局部路径计算上层是干道路网用于宏观评价。这样第 3 章要算的聚类系数和全局效率才不会被细枝末节干扰。2.1.1 边属性里该放什么ES 不是一个死结构建议至少包含length路段长度用于计算行程时间和平均路径长度lanes车道数用于容量估计speed_limit限速用于自由流时间估计area_type建成区/郊区用于速度折减scats_subareaSCATS 子区编号用于与信号控制联动其中 scats_subarea 这个属性在报告里比较关键它把信号控制逻辑单元和地理路网打通了后面第 4 章的预测特征要依赖它。2.2 用Python快速构建URN并计算基础特征我一般用 NetworkX 的 DiGraph 来承载 URN读取一个 CSV 边表就能建图。边表里每条记录是一段有向路段字段包括起点节点、终点节点、长度、车道数和方向。import networkx as nx import pandas as pd edges pd.read_csv(urn_edges.csv) G nx.DiGraph() for _, row in edges.iterrows(): G.add_edge( row[from_node], row[to_node], lengthrow[length_m], lanesrow[lanes], speed_limitrow[speed_kmh], subarearow[scats_subarea], ) print(节点数:, G.number_of_nodes()) print(有向边数:, G.number_of_edges()) print(弱连通分量数:, nx.number_weakly_connected_components(G))代码逻辑分三段先建空有向图再逐行把边表和属性写入最后输出三个基础统计量。弱连通分量数尤其要留意如果大于 1说明路网被人工切断了后面算平均路径长度会得出错误结论。参数方面length_m 和 speed_limit 用来算路段自由流行程时间lanes 在容量约束分析里会用到subarea 在第 4 章做区域级预测时按它聚合。2.3 把SCATS子区映射到图结构SCATS 的子区是信号控制层面的逻辑单元一个子区通常包含多个交叉口且子区边界并不总是和行政区边界重合。常见做法不是新建一张子区表而是把 subarea 编号作为属性写进边这样既保留地理拓扑又能按 subarea 做聚合计算。这里有一个容易踩的坑SCATS 子区在信号周期切换时可能重新划分同一路段在不同时段的子区编号会变。如果你要按子区统计流量就得在边属性里再存一个生效时间段或者单独维护一个时段子区映射表而不是把 subarea 当成固定属性。报告里 SCATS 下的路网建模之所以能支撑评价和预测正是因为建模阶段就把这个动态属性纳入了数据结构。3. 路网评价指标落地出入度、平均路径长度与聚类系数的计算口径3.1 XU等人在2020年那套指标到底在算什么报告引用了 XU Dong-wei 等人在 IEEE Intelligent Transportation Systems Magazine 2020 年发表的方法核心是把路网评价拆成节点和网络两个层次。节点层看的是出度和入度。某个交叉口的出度表示从它直接出发的路段数量入度表示直接汇入它的路段数量。报告里的符号 A(VNi) 和 B(VNi)本质上就是对全图所有节点的出度序列和入度序列做聚合。高出入度节点通常是区域性枢纽但未必是拥堵点真正值得关注的是出入度差值大的节点比如入度远大于出度大概率是汇入瓶颈信号配时需要考虑延长绿灯或增加蓄车空间。网络层看的是平均路径长度 L 和聚类系数 C。平均路径长度反映全网通达效率聚类系数反映相邻节点之间互相连通的紧密程度聚类系数高意味着局部回路多、绕行选择多抗拥堵能力更强。这两个指标一个管全局、一个管局部组合起来能判断瓶颈是单点问题还是结构性问题。3.1.1 全局效率的出入方向报告里还有两个式子涉及出方向效率和入方向效率。简单理解出方向效率评估的是从任意节点出发向外扩散信息或车流的能力入方向效率评估的是向任意节点汇入的能力。不对称路网里这两者差距明显比如放射性路网出方向效率高入方向效率可能被市中心节点拖累。计算时不要只算全图平均效率要把出、入两个方向分开算否则会掩盖这种不对称性。3.2 在NetworkX里计算这批指标import networkx as nx # 承接上一节的 G # 节点度 in_degrees dict(G.in_degree()) out_degrees dict(G.out_degree()) # 有向图平均路径长度需要先取最大弱连通分量否则会因断点报错 giant max(nx.weakly_connected_components(G), keylen) Gg G.subgraph(giant).copy() avg_path_length nx.average_shortest_path_length(Gg, weightlength) clustering nx.average_clustering(Gg) # 全局效率有向图没有内置函数按定义计算 def global_efficiency(graph, weightlength): n graph.number_of_nodes() if n 2: return 0.0 lengths dict(nx.all_pairs_dijkstra_path_length(graph, weightweight)) total 0.0 count 0 for u in lengths: for v, d in lengths[u].items(): if u ! v and d ! 0: total 1.0 / d count 1 return total / (n * (n - 1))这段先取最大弱连通分量原因是有向图里两个节点如果不在同一个弱连通分量最短路径长度为无穷大任何平均计算都会失真。average_shortest_path_length 的 weight 参数用 length 属性这样得到的是按实际路段长度加权的路径而不是路段数量。聚类系数在 NetworkX 里有向图版本和无向图版本行为不同有向图会把方向纳入计算官方文档对此有明确说明建议跑之前先确认自己要不要方向信息。自定义的 global_efficiency 函数里边长用 Dijkstra 计算倒数求和再归一化时间复杂度和空间占用都要心里有数几百个节点的路网没问题上万个节点时建议用采样估计。3.3 指标参数表与瓶颈识别边界指标对应报告代号计算方式典型用途节点出度A(VNi)G.out_degree(node)识别交通发生源判断流出压力节点入度B(VNi)G.in_degree(node)识别汇入瓶颈配合信号配时平均路径长度Lnx.average_shortest_path_length全网通达效率越低越好聚类系数Cnx.average_clustering局部绕行能力越高越稳健全局效率出C(DVNet_G)按出方向边集计算评估从源点扩散的效能全局效率入)D(DVNet_G)按入方向边集计算评估向终点汇集的效能报告中把周末和工作日分开对比并列出 Top 5 in-degree 节点这是评价部分最实用的操作方式不只看某一天的绝对值而是看工作日与周末的排名变化。如果某个节点在工作日入度排名很高、周末掉出前列说明它主要承担通勤压力改善措施盯早晚高峰即可如果周末排名依然高则是常态性片区枢纽需要考虑路网结构层面的分流。边界情况也要说明平均路径长度对断开的网络极其敏感只要有一条关键路段断裂最大弱连通分量缩小指标值可能大幅跳变所以修复数据、保证网络连通是评价计算的前置条件。4. SCATS路网状态预测与数据修复时间序列插补和异常检测4.1 SCATS下的预测流程从信号周期到流量特征SCATS 能直接提供的原始数据包括信号周期长度、相位绿灯时间、线圈检测的流量和占有率。预测任务通常不是直接预测速度而是预测下一时段的饱和度或排队长度因为信号控制直接干预的就是这两个量。推荐的建模流程分四步。第一步是数据对齐把 SCATS 的历史记录按 15 分钟或 5 分钟粒度对齐到路网边属性上每一条边对应一个时间序列。第二步是数据修复由于线圈故障、通信中断导致的缺失值必须处理否则特征工程会把这些洞带进模型。第三步是特征构造除了当前时刻流量和占有率还要加入滞后 1 个周期、前一日同时刻、周末标记、SCATS 子区聚合流量这些变量。第四步才是选模型训练和验证。4.1.1 特征构造的几个实用参数滞后阶数信号周期一般在 60 到 180 秒之间15 分钟粒度下取 1 到 4 阶滞后比较稳妥日周期性加入前一天同时段流量能捕捉通勤规律周模式周一早高峰与周二早高峰特征不同用 one-hot 编码星期几子区联动相邻子区同时段流量也是有用特征SCATS 子区之间信号协调本身就有联动关系4.2 缺失流量插补与异常检测的工程实现路网数据修复报告里提了两类手段插补缺失值、检测异常值。常见做法是先用时间序列插补把洞填上再做异常检测顺序不能反否则异常检测模型会被缺失值干扰。import numpy as np import pandas as pd from sklearn.experimental import enable_iterative_imputer from sklearn.impute import IterativeImputer from sklearn.ensemble import IsolationForest # df: 每一列是一条路段的流量时间序列, 缺失值记为 NaN imputer IterativeImputer(max_iter20, random_state42) df_filled pd.DataFrame(imputer.fit_transform(df), columnsdf.columns) # 对修复后的序列做异常检测 iso IsolationForest(contamination0.05, random_state42) anomaly_labels iso.fit_predict(df_filled)IterativeImputer 是多重插补的一种工程化实现它对每一列缺失值用其他列建立回归模型来预测填充迭代多轮逼近稳定值。max_iter 控制迭代轮数一般 10 到 20 轮足够太多只会徒增计算时间。contamination0.05 表示假设 5% 的数据点是异常值这个比例要根据实际路况调如果某条路段正在施工流量长期偏低正常状态本身就会被标成异常此时要把施工时段单独标记出来不参与异常检测。异常点处理有个细节检测出来之后不要直接删掉或改成均值。SCATS 数据里流量异常往往对应真实事件比如事故、临时管制这类数据对预测模型来说是宝贵的样本。建议单独存一列 anomaly_flag让模型自己学习异常时段与正常时段的流量关系比硬性清洗效果更好。4.3 短时流量预测梯度提升与LSTM的取舍报告里提到 SVM 和神经网络都是候选方法。从工程实践看如果数据量在几万到几十万条这个量级梯度提升树比神经网络更稳原因有三个一是对表格型特征天然友好SCATS 特征大多是数值型和类别型混合二是对时间序列的缺失值有一定鲁棒性树模型不会因为个别异常值剧烈波动三是调参路径成熟LightGBM 的早停机制能省下不少验证时间。import lightgbm as lgb feature_cols [flow_lag1, flow_lag2, occupancy, prev_day_same_time, subarea_flow, hour, is_weekend] X df_filled[feature_cols] y df_filled[flow_next] model lgb.LGBMRegressor( n_estimators800, learning_rate0.05, num_leaves31, subsample0.8, colsample_bytree0.8, random_state42, ) model.fit(X, y, eval_set[(X_val, y_val)], callbacks[lgb.early_stopping(50)])特征列里有几项值得解释。flow_lag1 和 flow_lag2 是前两个时间步的流量用来捕捉短时惯性prev_day_same_time 是昨日同时段流量用来锚定通勤节奏subarea_flow 是 SCATS 子区聚合流量相当于把信号控制范围内的协同信息加了进来。early_stopping 的 50 表示验证集损失连续 50 轮不下降就停止训练防止过拟合。LSTM 更适合长周期依赖强的场景比如预测未来 1 到 2 小时的路况但在 15 分钟尺度上梯度提升的性价比更高。5. 验证技巧用弱连通分量和流量守恒核对建模结果5.1 用弱连通分量定位建模错误图中出现多个弱连通分量通常意味着数据有问题。最常见的两个原因一是转向数据不完整左转、掉头允许关系没有补充进边集合导致交叉口之间存在逻辑断点二是坐标系转换时把部分节点坐标偏移了不再是同一个路网。检查方法很简单把每个分量的节点数打印出来如果出现节点数只有 1 的孤立分量逆查对应路段数据。wcc list(nx.weakly_connected_components(G)) isolated [comp for comp in wcc if len(comp) 1] print(弱连通分量数量:, len(wcc)) print(孤立节点数量:, len(isolated))孤立节点多了以后评价指标计算毫无意义。处理时不要直接删节点回到源头检查边表里是否有指向该节点的路段因为转向限制被过滤掉了。如果有补充转向边即可如果确实是断头路才删除。这个技巧在报告第 5 章数据修复之前就应该执行它决定后续修复和预测的数据基础是否成立。5.2 流量守恒校验修复结果数据修复效果验证多数人只看缺失率是否下降这不够。我建议用流量守恒做二次校验一个交叉口的流入总量应等于流出总量与内部停车、转弯损耗之和如果修复后的数据在某个节点严重不平衡说明插补序列之间互相矛盾。具体做法是分时段汇总每个节点所有入边的流量和出边流量计算偏差率。node_flow_diff {} for node in G.nodes(): in_flow sum(df_filled.loc[df_filled[to_node] node, flow].sum()) out_flow sum(df_filled.loc[df_filled[from_node] node, flow].sum()) node_flow_diff[node] abs(in_flow - out_flow) / max(in_flow, out_flow)偏差率超过 0.15 的节点优先检查优先怀疑插补模型在该节点附近没有学对上下游关系。此时回看 IterativeImputer 的迭代轮数是否过少或者该节点所在子区的异常点是否被误填充。把守恒校验和弱连通分量检查合在一起就是一套低成本的路网数据体检流程不用等到预测效果差再回头排查数据。本文还有配套的精品资源点击获取