资讯动态

改进聚类算法在道路事故多发路段鉴别中的应用

发布时间:2026/9/19 17:24:20 来源:尧图企业网站定制
简介这是一篇发表于《武汉理工大学学报》的学术论文面向交通安全管理与智能交通研究者、研究生和道路工程师针对事故多发路段鉴别中阈值选择难的问题提出改进DBSCAN聚类算法。算法结合累计频率曲线法自适应选取最小密度点既保留累计频率法计算简便的优势又避免固定路段划分导致的事故密集区被分割或漏检能识别任意长度、更集中的事故多发段并用安徽省某高速公路实际数据验证。资料包仅含1个PDF文件大小277KB包含引言、算法原理、关键参数改进、实例验证等完整内容便于直接参考学习。已有141人学习下载适合想掌握聚类算法在交通安全中应用的读者可借此理解邻域半径与MinPts的取值影响、桩号数据一维聚类流程及工程实现。1. 从“事故黑点”到“多发路段”聚类算法为什么能换一种算法去鉴别道路交通事故多发路段的鉴别本质上是把一个连续的道路网络离散化成若干个“单元”再在这些单元里找出事故风险显著偏高的那部分。传统的做法比如事故数法、事故率法、当量事故数法逻辑都很直白划定固定长度的路段统计事故次数或伤亡人数再和某个阈值或者区域平均值比较。但这种固定分段的思路有一个天然缺陷——事故分布并不按等间距的路段边界走一个 2 公里的长下坡加弯道组合可能事故集中在其中 300 米内用 1 公里等长路段一划信号就被稀释了。改进聚类算法解决的是这个“边界错位”问题。聚类不预设路段长度它让数据自己在空间上聚合事故密集的区域自然形成簇簇的边界就是你要找的多发路段边界。谢练这篇研究之所以值得关注核心不在“用了聚类”而在“改了聚类”——把不适合道路线状数据分布的经典算法改造成能处理地理坐标、能感知道路拓扑约束、能输出稳定可迁移路段结果的形态。本文就顺着这个思路讲清楚经典聚类为什么不够用、改进改在哪个环节、参数怎么落到实际道路数据上、以及聚类结果如何对接后续的鉴别判定标准。我假定读者已经掌握 KMeans 和 DBSCAN 的基本原理下面的内容不再从“聚类是什么”讲起而是直接聚焦到“道路事故数据聚类”这一特定场景里真正会卡住人的细节。2. 经典 KMeans 与 DBSCAN 在事故路段鉴别里的三条硬伤2.1 欧氏距离假设经纬度不是平面坐标KMeans 和 DBSCAN 默认使用欧氏距离计算样本间相似度。但道路事故数据采集到的是经纬度坐标经纬度本身是球面坐标直接用 (lon1 - lon2)² (lat1 - lat2)² 开根号在中高纬度地区会产生明显的距离误差。我曾在北纬 40° 左右的城市做过测试两起事故实际相距 1 公里直接用经纬度欧氏距离计算误差累计后聚类边界会偏移大约 100~150 米。这个量级在路段鉴别里足以把两个相邻但风险特征不同的弯道合并成同一个簇。改进的做法有两种常见路线。第一种是把经纬度投影到平面坐标系比如 UTM 分区投影或高斯-克吕格投影再做标准聚类第二种是直接替换距离度量函数在聚类迭代过程中调用 Haversine 公式计算球面距离。前者的缺点是投影转换会引入变形尤其跨投影带时误差会突变后者的缺点是每次迭代都要做三角函数运算数据量上到 10 万条事故记录时计算开销会明显上升。2.1.1 用 Haversine 距离替换欧氏距离的代价import numpy as np from math import radians, cos, sin, asin, sqrt def haversine(lon1, lat1, lon2, lat2): 计算两个经纬度点之间的球面距离单位米 R 6371000 # 地球平均半径单位米 lon1, lat1, lon2, lat2 map(radians, [lon1, lat1, lon2, lat2]) dlon lon2 - lon1 dlat lat2 - lat1 a sin(dlat/2)**2 cos(lat1) * cos(lat2) * sin(dlon/2)**2 c 2 * asin(sqrt(a)) return R * c这段代码把两起事故的经纬度转成弧度后按球面三角公式算出实际距离。注意R 6371000用的是平均半径如果项目对精度有更高要求可以换成 WGS84 椭球下的不同半径值但通常事故鉴别的精度到米级足够平均半径不会带来实际影响。替换距离函数之后KMeans 的质心更新逻辑会出问题。KMeans 在每次迭代中用簇内样本的均值作为新质心但在球面坐标下直接对经纬度取平均值得到的质心并不在球面最短路径的中心上。解决方法是把经纬度转成三维直角坐标在笛卡尔空间里算平均向量再归一化投影回经纬度。这一步不处理的话聚类中心会偏移。2.2 预先指定簇数 K事故分布没有先验知识KMeans 需要预先指定 K 值但一条几十公里的国道上到底有几个事故多发段事故数据本身不会告诉你。常见的做法是用手肘法或轮廓系数辅助判断但这两者在道路事故数据上的表现都不稳定——事故数据往往伴随大量零值路段和少量极端高值路段数据分布严重偏斜轮廓系数的判别曲线会变得平缓难以找到明显的“肘部”。另一个更实际的问题是KMeans 假设每个簇是凸形的、大小相近但道路事故密度的分布根本不是这样。一条穿过城区的国道可能 5 公里范围内有 3 个密集点团每个点团的半径不到 200 米而郊区的密集点团可能拉长到 1.5 公里。KMeans 强行把数据分成 K 个球状簇遇到狭长分布的密集路段时会把一个连续的事故密集带拦腰截断或者把两个本应独立的路段并进同一个簇。2.2.1 手肘法在事故数据上的局限from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import numpy as np # coords 为预处理后的平面坐标数组形状 (n_samples, 2) # 这里假设已经完成了投影转换 inertia_scores [] silhouette_scores [] for k in range(2, 12): km KMeans(n_clustersk, random_state42, n_init10) labels km.fit_predict(coords) inertia_scores.append(km.inertia_) silhouette_scores.append(silhouette_score(coords, labels)) # 打印每个 K 的轮廓系数观察变化趋势 for k, s in enumerate(silhouette_scores, start2): print(fK{k}, silhouette{s:.4f})这段代码跑完后你大概率会看到轮廓系数在 K5 之后仍然缓慢上升没有一个明确的峰值。这不是代码写错了而是事故数据本身的簇结构不够清晰。常规的数据集轮廓系数会在最佳 K 值处出现明显尖峰但事故数据中大量低密度区域的存在会让轮廓系数曲线趋于平缓。所以在实践里KMeans 用于事故路段鉴别通常只作为粗糙的预分段工具用较大的 K 值把数据切成小块再对每个块做进一步的密度分析。直接拿手肘法选出的 K 去跑最终结果很容易得到一组看着合理、换条路就完全失效的路段边界。2.3 DBSCAN 的全局 Eps城市与郊区密度无法统一DBSCAN 不需要预先指定簇数但需要设定邻域半径 Eps 和最小样本数 MinPts。在道路事故场景里这个参数比 K 值更难定。一条道路的不同段落交通流量、路况复杂度、周边土地利用性质差异极大事故密度天然有数量级差别。用全局统一的 Eps在城市密集段会把多个独立事故点团连成一个大连片在郊区稀疏段又会把真实的多发路段拆成碎片。具体来说我曾处理过一条省道的数据城区段 2 公里内有 40 起事故郊区段 10 公里内只有 30 起。设 Eps300 米、MinPts5 时城区段所有事故都被归入同一个簇但这个簇横跨了两个信号交叉口和一条桥梁内部其实有三个独立风险点而郊区段则只识别出 2 个簇丢失了若干长度在 100 米左右的小规模事故集中点。这暴露了 DBSCAN 全局参数的一个核心假设整个数据集有相近的密度。事故数据几乎不可能满足这个假设。改进聚类算法在这里的切入点就是“局部自适应 Eps”——让密度高的区域用较小的邻域半径密度低的区域自动放宽。后面我会讲一种基于 K 近邻距离排序来自适应确定每个点 Eps 的方法那是谢练这类研究里比较常见的改进路线也是实操上性价比最高的方案。3. 改进聚类算法与道路多发路段鉴别技术的实战路线3.1 改进思路总览KMeans 密度峰 自适应 DBSCAN把经典聚类算法应用在道路事故数据上的常见改进路线是把 KMeans 和 DBSCAN 的优势做组合。KMeans 擅长快速划分数据全集复杂度低能对整个道路网的事故点做全局粗分DBSCAN 擅长发现任意形状的密集簇而且能自动把离群点标为噪声不被孤立的偶发事故牵着走。改进思路概括成三步第一步用 KMeans 对事故点做粗聚类K 值取大一些比如总事故数的平方根目的是把整个研究区域切开避免后续密度聚类在多密度区域互相干扰。第二步对 KMeans 得到的每个子簇计算内部事故点之间的平均距离和密度分布推导出该子簇的局部 Eps 和 MinPts。第三步在每个子簇内部重新运行 DBSCAN把事故点聚成最终簇再沿道路线形把簇投影成路段实体。这个组合的优势在于KMeans 的全局划分由自适应子簇密度估计接管DBSCAN 的局部参数由 KMeans 子簇提供自适应性。两者互补了对方最明显的短板。3.2 版本依赖与完整实验环境用 Python 操作数据、跑聚类、画地图我建议直接使用以下依赖。这不是教程里必须的最新版本但我实际跑下来稳定、API 熟悉能避免很多不必要的踩坑。pip install numpy1.24.3 pandas2.0.3 scikit-learn1.3.2 pip install scipy1.10.1 matplotlib3.7.2说明scikit-learn1.3.2 的KMeans默认参数n_init行为有变化如果你下载的是更新版本如 1.4 以上跑 KMeans 时要显式指定n_init10否则会有收敛警告。此外数据清洗用到的缺失值处理、重复坐标删除建议结合pandas在聚类之前完成因为 DBSCAN 对这种异常值很敏感。3.3 完整代码从事故 CSV 到改进聚类的全过程下面这份代码可以直接跑通一个最小实验。我特意把步骤拆得比论文里更细一些方便你对照路试数据一条条核。import pandas as pd import numpy as np from sklearn.cluster import KMeans, DBSCAN from sklearn.preprocessing import StandardScaler from scipy.spatial.distance import pdist, squareform import math # ---------- 1. 读入事故数据 ---------- # 文件中至少需要包含经纬度两列 df pd.read_csv(accidents.csv) print(f原始事故记录: {len(df)} 条) # 删除经纬度缺失的记录避免 DBSCAN 因 NaN 崩溃 df df.dropna(subset[longitude, latitude]) # 删除完全重复的坐标同一点多起事故的保留一条但可在后续加权重 df df.drop_duplicates(subset[longitude, latitude]) print(f清洗后事故点: {len(df)} 条) # ---------- 2. 经纬度转平面坐标 ---------- # 这里用简单的 UTM 投影使用 pyproj 更精确此处演示用等距横轴墨卡托近似 lon_center df[longitude].mean() lat_center df[latitude].mean() df[x] (df[longitude] - lon_center) * 111320 * math.cos(math.radians(lat_center)) df[y] (df[latitude] - lat_center) * 110540 # 标准化消除两个轴上的单位差异本质上 X/Y 已是米标准化主要是为了聚类时数值稳定 coords df[[x, y]].values # ---------- 3. 第一层KMeans 粗划分 ---------- # K 取 sqrt(N) 附近N 为事故点数 K int(np.sqrt(len(coords))) km KMeans(n_clustersK, random_state42, n_init10) km_labels km.fit_predict(coords) df[km_cluster] km_labels # ---------- 4. 第二层每个子簇内自适应 DBSCAN ---------- final_labels np.zeros(len(coords), dtypeint) cluster_offset 1 for cid in np.unique(km_labels): # 取出该粗簇内的所有点 mask df[km_cluster].values cid sub_coords coords[mask] # 如果该簇内点数太少直接视为噪声 if len(sub_coords) 5: final_labels[mask] -1 continue # 计算簇内两两距离用于估算局部 Eps dist_mat squareform(pdist(sub_coords, metriceuclidean)) # 取每个点到其第 4 近邻的距离MinPts 5 时对应 k4 k_nearest 4 sorted_dist np.sort(dist_mat, axis1) k_dist sorted_dist[:, k_nearest] # 用平均 k 距离作为候选 Eps eps_local np.percentile(k_dist, 85) # 运行 DBSCAN注意到目前为止 MinPts 与近邻数保持一致 sub_db DBSCAN(epseps_local, min_samples5).fit(sub_coords) sub_labels sub_db.labels_ # 分配全局标签保证每个簇的 ID 唯一 for sub_label in np.unique(sub_labels): if sub_label -1: final_labels[mask] -1 else: final_labels[mask (np.arange(len(coords)) np.where(mask)[0][np.where(sub_labels sub_label)][:, None]).any(0)] cluster_offset cluster_offset 1 df[final_cluster] final_labels # 统计每个簇的事故数量 cluster_counts df[df[final_cluster] ! -1].groupby(final_cluster).size() print(聚类结果统计:) print(cluster_counts.sort_values(ascendingFalse))每段代码的逻辑说明如下坐标转换部分是整个流程里最容易出错的地方。我用了近似投影公式x为经度差乘以 111320 再乘以纬度的余弦y为纬度差乘以 110540。这个近似在东西跨度小于 10 公里的项目里误差小到可以忽略但如果你的研究区域横跨几十公里建议直接用pyproj做标准 UTM 投影否则聚类边界会在东西方向有系统偏移。KMeans 粗划分的 K 值取sqrt(N)是一个通用经验值。事故点有 500 个时 K 约 22效果是把每条路切成小段每段内的事故点数在 10~30 个左右。这个量级非常适合做后续的局部密度估计。DBSCAN 自适应部分的核心在np.percentile(k_dist, 85)这行。我计算每个点到第 4 近邻的距离然后取这些距离的 85 分位数作为局部 Eps。百分之 85 这个值是我在几条省道数据上调参得到的一个起点实际使用时需要根据簇内点的稀疏程度调整。如果聚类结果把同一条路段的点拆成太多碎片就调高到 90如果不同风险点被合并在一起就调低到 75。这里有一个常见的陷阱直接在 KMeans 划分的子簇上独立跑 DBSCAN会把落在子簇边界两侧的本应属于同一个事故群的点强行分开。解决办法是在边界处做一定重叠——KMeans 粗划分时可以选择让每个点同时归属最近的 2 个簇中心增加重叠区域的候选集合但这样代码复杂度会上升。对于初步鉴别研究直接硬划分也能接受后续做路段投影时可以把相邻簇合并判断。3.4 用聚类结果鉴别事故多发路段从簇到路段的三个规则聚类完成后得到的是一组事故点集合但“事故多发路段”是道路上的线段实体不是点集合。把簇转换成路段的常见做法是这样第一个规则是沿道路投影。把每个簇内事故点的经纬度投影到最近的道路中心线上取投影点的最小和最大里程桩号作为路段起终点。这个操作在 ArcGIS 里叫“近邻分析”在 PostGIS 里可以用ST_ClosestPoint完成。第二个规则是密度阈值判定。簇内单位里程的事故数要大于某个阈值才记为多发路段。比如某省标准是“每公里年均事故数大于 10 起”那就用簇内事故数除以簇沿道路的长度公里再除以统计年数和阈值比较。第三个规则是相邻簇合并。如果两个簇沿道路方向间隔小于 50 米且中间没有桥梁、互通等明显结构分界通常应该合并成一条路段否则会给后续治理方案设计带来不必要的碎片化。3.4.1 用 PostGIS 把簇投影到路网上-- 假设事故点已按聚类结果标记 cluster_id -- 道路表 roads 有几何列 geom 和路由字段 route_id WITH cluster_points AS ( SELECT cluster_id, ST_Collect(geom) AS pts FROM accidents WHERE cluster_id IS NOT NULL GROUP BY cluster_id ) SELECT cp.cluster_id, ST_ClosestPoint( (SELECT ST_Collect(geom) FROM roads), cp.pts ) AS proj_point FROM cluster_points cp;这段 SQL 把每个簇的事故点几何对象聚合起来然后取路网上离这个聚合点集最近的点。实际项目中更精确的做法是对道路进行线性参考把事故点映射到路线的里程值上再计算路段长度但这里给出的方法已经能把聚类结果快速初步转成路网位置方便后续可视化。3.5 参数对照改进 KMeans 密度峰参数与标准 DBSCAN 参数对比下面的参数表是我在多个道路事故数据集上调试得到的一组常用范围给没有头绪的读者一个起步参考。表中数值并非绝对标准不同数据尺度下需要按比例调整。参数标准 KMeans标准 DBSCAN改进KMeans 自适应 DBSCAN簇数/邻域手动指定 K通常 5~10全局 Eps包含全图KMeans K 取 sqrt(N)各子簇独立 EpsMinPts不适用全局设定通常 5~10固定 5与近邻数一致Eps 取值不适用人工反复试错np.percentile(k_dist, 75~90)处理多密度差强制等大小簇差全局单一密度好各子簇密度独立估计计算复杂度O(N·K·iter)O(N²)O(N·K·iter ΣN_i²)离群点处理强制入簇标记为噪声子簇边界噪声保留内部噪声标记表格里最后一行值得展开改进方案中KMeans 会把每个点都强制划入某个粗簇包括真正的孤立事故点但这些点在子簇内的 DBSCAN 阶段会被标记为噪声标签-1所以在最终结果里它们依然不会参与事故多发路段的判定不会拉偏簇的边界。这比单独使用 KMeans 更接近真实风险分布。4. 改进聚类算法在道路多发路段鉴别中的正确使用与质量验证4.1 算法选型边界改进聚类不是唯一答案如果你手里的道路数据本身带有明确的线性参考系统每个事故点都已经映射到里程桩号上那么用一维聚类或滑动窗口法处理可能更直接。改进聚类算法的优势场景是事故坐标是离散经纬度、没有现成的桩号映射关系、道路线形数据质量一般或缺失。这种情况下聚类能利用事故点在空间上的自然聚集跳过了路网匹配这一步直接得到风险区域。但要注意聚类算法对事故记录的完整性非常敏感。如果事故数据漏报率超过 30%聚类结果的形状会有明显偏移——密集区域的簇会变得更零碎边界会漂移。所以在数据质量评估阶段我会先用简单统计看事故数据的年度分布和路段覆盖度如果发现明显漏报建议先做数据补齐不要急着进聚类环节。4.2 聚类质量验证轮廓系数之外的三个关键指标聚类完成后不能只看图画得好不好看要有定量指标验证。第一个指标是“簇内道路连续比例”。理想的多发路段簇应该沿道路方向连续延伸不应该在中间断开。我计算每个簇中最远两起事故沿道路的路径距离和簇内所有事故两两间的道路距离平均值比较。如果路径距离是直线距离的 1.5 倍以上说明簇跨越了较大的弯道或绕行结构这个簇的边界需要重新检查。第二个指标是“噪声占比”。噪声点占全体事故点的比例在 10%~25% 之间是比较合理的区间。噪声占比过高说明 Eps 设小了或 MinPts 设大了噪声占比过低说明聚类把孤立事故也拉进了路段多发路段边界会虚胖。第三个指标是“路段区分度”。对聚类得到的所有路段计算路段内事故密度与全域平均事故密度的比值。如果多数路段这个比值在 1~2 之间说明聚类结果和直接按路段长度平均没什么差别聚类没有真正识别出多发路段的特征。好的聚类结果应该让这个比值拉开差距至少要有三分之一的路段比值超过 3。# 计算每个簇的事故密度起/公里并与全域密度比较 df[density_ratio] 0.0 global_points_per_km len(coords) / 100 # 假设道路总长约 100 公里 for cid in cluster_counts.index: # 这里简化处理簇内点的包络长度用经纬度直接计算 sub df[df[final_cluster] cid] lon_span (sub[longitude].max() - sub[longitude].min()) * 111320 * math.cos(math.radians(lat_center)) lat_span (sub[latitude].max() - sub[latitude].min()) * 110540 # 取两个方向的较长值作为路段长度的近似 approx_length_km max(lon_span, lat_span) / 1000 density len(sub) / max(approx_length_km, 0.1) df.loc[df[final_cluster] cid, density_ratio] density / global_points_per_km high_risk df[df[density_ratio] 3] print(f密度比为全域 3 倍以上的事故点占比: {len(high_risk) / len(df[df[final_cluster] ! -1]):.1%})注意这段代码为了演示做了简化用经纬度跨度近似路段长度实际项目中建议用路网匹配后的真实里程。占比如果在 20%~40% 之间说明聚类结果的分辨力是合格的。4.3 可视化路网上叠加聚类簇与事故散点import matplotlib.pyplot as plt from matplotlib.lines import Line2D fig, ax plt.subplots(figsize(12, 8)) # 假设 roads 是道路线段的坐标列表这里用简单线段替代 for seg in road_segments: # road_segments: List[List[Tuple[float, float]]] xs [p[0] for p in seg] ys [p[1] for p in seg] ax.plot(xs, ys, colorgray, linewidth2, zorder1) # 绘制事故点按最终聚类簇上色噪声点为黑色 colors plt.cm.tab20(np.linspace(0, 1, cluster_offset)) for cid in cluster_counts.index: sub df[df[final_cluster] cid] ax.scatter(sub[x], sub[y], colorcolors[cid % len(colors)], s25, zorder2) noise df[df[final_cluster] -1] ax.scatter(noise[x], noise[y], colorblack, s10, alpha0.5, zorder2) # 标注路段中心 for cid in cluster_counts.index: sub df[df[final_cluster] cid] cx, cy sub[x].mean(), sub[y].mean() ax.text(cx, cy, fR{cid}, fontsize9, fontweightbold) ax.set_aspect(equal) ax.set_xlabel(东西方向位移米) ax.set_ylabel(南北方向位移米) ax.legend(handles[ Line2D([0], [0], markero, colorw, markerfacecolorgray, label道路), Line2D([0], [0], markero, colorw, markerfacecolorblack, label噪声事故点) ], locupper right) plt.show()画出图后重点检查两类现象一是相邻簇之间是否距离过近二是有没有簇跨越了道路的分岔口。事故多发路段鉴别最终要落到治理建议如果簇把两条不同道路的交叉口包含进来建议拆开重新聚类。4.4 纵向稳定性验证与横向对比聚类结果的路段边界不能只在一年的数据上成立。我通常会做一次纵向验证用连续三年的数据分别跑同类改进聚类然后对比三年路段边界的变化。如果某条路段在三年里都出现且位置偏移不超过 50 米这个路段是稳定的高风险路段可以作为治理优先对象如果某条路段只在个别年份出现要把该年的特殊因素如大型施工、极端天气纳入分析。横向对比方面我会把聚类得到的多发路段与现有事故黑点台账通常每年由交警或公路部门根据事故统计认定做叠加分析。聚类结果覆盖了台账中 80% 以上的黑点并且额外发现了 2~3 处台账没有覆盖的小型密集点团说明聚类算法确实找出了人工统计容易遗漏的对象。这里的百分比要求不是固定标准但低于 70% 就要回头检查聚类参数和数据质量。纵向验证的具体操作不复杂分别导入三个年份的事故 CSV重复前面的清洗和聚类流程然后算路段中心点之间的距离匹配关系。建议写一个循环把三年的结果输出成 GeoJSON用 QGIS 或 Kepler.gl 叠加查看。5. 实际项目中的参数经验与排错技巧事故多发路段鉴别落地5.1 MinPts 和 Eps 的联动调整技巧很多人在自定义改进聚类时MinPts 看到别人设 5 就跟着设 5完全没意识到 MinPts 对 Eps 选择的影响。我这里给一个联动调整的通用规则先把 MinPts 固定为 5计算 k 距离第 4 近邻距离的 85 分位数作为初始 Eps聚类后如果路段数量过多、碎片化严重把百分位调到 90 或 92如果路段数量过少、边界过大把百分位下调到 75。注意每次调百分位后要重新检查路段的“簇内道路连续比例”这个指标能直接反映边界是否合理。另一个技巧是用 k 距离曲线的拐点代替固定百分位。画出所有点的 k 距离升序排序曲线曲线从平缓转为陡峭的拐点对应合适的 Eps。这个方法比直接设百分位更直观而且在数据量大的时候能帮助你理解整个数据集的密度分布形态。实际项目中直接取拐点对应的 k 距离值通常能在第一轮就得到较合理的聚类结果。5.2 跑聚类时最常见的三个报错与解决方法第一个报错是ValueError: eps cannot be negative。这通常是因为子簇内事故点全部重合导致所有距离为零k 距离也为零进而计算出负的 Eps。解决方法是把完全重合的坐标做去重处理或者在距离矩阵上加一个极小值1e-6。第二个报错是RuntimeWarning: Mean of empty slice。出现这种情况是因为 KMeans 划分的某个子簇在后续操作中被过滤掉导致数组索引悬空。我用过一个简单的防御性写法在每个子簇迭代前先判断len(sub_coords) MinPts满足就直接标记为噪声并 continue避免后面的矩阵运算对空数组操作。第三个更隐蔽内存溢出。DBSCAN 用pdist计算两两距离数据量到 5 万条时距离矩阵的大小是 5 万乘以 5 万约 200 亿个浮点数内存直接爆炸。遇到大数据量不要用squareform(pdist(...))改用sklearn.metrics.pairwise_distances_chunked分块计算或者直接使用scikit-learn中 DBSCAN 自带的algorithmball_tree后者可以用树结构避免全量距离矩阵。在 scikit-learn 的 DBSCAN 实现里algorithmkd_tree或ball_tree并不需要你显式调用距离矩阵直接用归一化后的坐标矩阵即可它能内部完成近邻搜索# 大数据量下的 DBSCAN 调用方式避免显式距离矩阵 from sklearn.cluster import DBSCAN import numpy as np # 先用 StandardScaler 统一尺度这里仅作演示如果你已经用米制坐标也可以不缩放 coords_scaled (coords - coords.mean(axis0)) / coords.std(axis0) db DBSCAN(eps0.3, min_samples5, algorithmball_tree, metriceuclidean).fit(coords_scaled)metriceuclidean配合algorithmball_tree时内部使用欧氏距离的近邻搜索不需要计算完整距离矩阵内存表现好得多。但要注意如果数据点是经纬度且需要球面距离ball_tree无法直接使用haversine这时应当预先投影成平面坐标再聚类。5.3 从路段鉴别结果到治理决策一份可直接上会的输出模板论文层面把路段鉴别做完只是第一步工程落地时你需要把聚类结果整理成决策者可用的报告。我通常以表格形式输出一份“事故多发路段清单”字段包括路段编号、起终点桩号、道路名称、事故起数、平均事故密度、风险等级、建议治理方向。风险等级按密度比和事故严重程度综合分三级一级对应事故密度为全域平均 5 倍以上且包含死亡事故的路段二级对应 3~5 倍或涉及重伤事故三级对应其余超过阈值但严重程度较低的路段。治理方向的建议要简洁明确例如“建议增设限速标志和减速震荡标线”“渠化交叉口左转专用道”“弯道外侧增设护栏和反光诱导标”避免写大而全的策略每个路段一条核心措施。这份表可以直接用来申请治理经费、调整巡查频率、优化警力部署。理论上事故多发路段鉴别只有落到这个层面聚类改进才有实际价值。5.4 可复现的评估清单一篇研究论文审稿人想看到的验证逻辑如果你把这个标题写成论文投稿审稿人关心的通常不是算法有多新颖而是你怎么证明改进聚类比传统方法更准。实践里我给审稿人看的验证清单是聚类结果与事故黑点台账的叠加一致率定性三类算法KMeans、DBSCAN、改进算法在同一数据集上的噪声占比、簇数稳定性对比定量三年数据纵向匹配度稳定性与传统固定分段法得到的事故率排名对比边界合理性治理建议的可解释性——按聚类结果做出治理措施后次年事故率的变化因果验证这条路走到最后改进聚类不只是发了篇论文而是能让道路管理者看到一个从数据到决策的闭环。鉴别出的路段落实了治理措施后事故率真的在降这才是“改进”两个字最硬核的含义。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价