新闻详情

新闻详情

首页 / 资讯中心 / 详情

DBSCAN聚类算法Matlab实现:从密度概念到调参避坑实战

发布时间:2026/10/1 16:00:43来源:尧图网络
DBSCAN聚类算法Matlab实现:从密度概念到调参避坑实战
简介这份代码包面向需要在MATLAB环境中开展无监督学习的数据分析与科研人员提供了DBSCAN基于密度的空间聚类算法的完整实现能够发现任意形状的聚类并对噪声不敏感适用于复杂结构、含噪较多的数据集处理。包内共3个文件均为MATLAB的m脚本涵盖DBSCAN核心算法、聚类结果可视化函数与测试运行脚本整体仅4KB轻量简洁便于直接调用与学习。已有2534人学习下载。通过阅读源码可理解ε邻域半径与MinPts最小邻域点数两个关键参数如何影响聚类效果掌握核心点、边界点与噪声点的判定逻辑以及从数据加载、参数设置到邻域搜索、聚类扩展、结果绘制的完整流程能帮助读者快速上手DBSCAN并迁移到自己的多维数据实验中。1. DBSCAN聚类算法matlab代码.zip 这包代码到底解决什么问题拿到“DBSCAN聚类算法matlab代码.zip”的人十有八九不是来学习的而是来救火的。你可能已经用 k-means 跑过一轮发现簇形状是任意的、K 值要靠猜、结果里全是本该被忽略的噪声点也可能手里是一批点云或用户特征希望聚出来的类别“自己冒出来”而不是先回答一堆前置问题。DBSCAN 的定位正好卡在这个需求上不需要预设类别数能识别任意形状簇还能把噪声点单独标出来。这份 Matlab 代码把这些能力落成可直接调用的.m函数和演示脚本省掉从零实现和纠结参数的时间。接下来的内容按拆包、跑通、调参、避坑到验证的顺序展开适合正在做点云、用户分群、社区服务需求分类分析等带噪声聚类任务的研究生和工程师。2. 动手前先弄清 DBSCAN 的三个密度概念从密度可达到第一个可直接运行的 dbscan.m2.1 核心点、边界点、噪声点为什么 DBSCAN 不用指定 K周志华《机器学习》里把 DBSCAN 的基础概念讲得很清楚给定邻域半径 eps 和密度阈值 MinPts一个样本的邻域内样本数不少于 MinPts 时它就是核心点核心点邻域内的其他点称为边界点既不是核心点也不在任何核心点邻域内的点是噪声点。三个概念之间靠“密度直达”“密度可达”“密度相连”串起来最终形成簇的定义。在 Matlab 里落地时这些概念直接对应一组数值运算eps 是一个距离阈值MinPts 是一个计数阈值。你在代码里做的无非是“找到每个点的邻域集合”然后“顺着核心点向外扩”。整个过程和 K 值无关和簇形状无关只和密度有关所以它才能处理 k-means 完全搞不定的半月形、环形、细长条数据。这也解释了为什么调参时总要把 eps 和 MinPts 放在一起看。它们不是两个独立旋钮而是一个“密度定义”的两个侧面eps 决定什么叫“邻域”MinPts 决定多少个点算“够密”。只看一个参数去调结果必然是飘的。2.2 从伪代码到可直接运行的 dbscan.m向量化实现理解概念之后真正的分水岭在于实现方式。网上能找到不少 DBSCAN 的 Matlab 代码但很多是用双重 for 循环写出来的教学版数据量到几千点就开始卡。Matlab 的强项是矩阵运算所以我在工程里更推荐用pdist2一次性算完距离矩阵再用矩阵索引完成核心点扩展。下面是我在项目中反复使用的一个最小实现逻辑和标准 DBSCAN 一致但省掉了低效的逐点距离计算function labels dbscan_core(X, eps, minPts) % DBSCAN 的 Matlab 向量化实现 % 输入X n×d 数值矩阵建议先做标准化 % eps 邻域半径和距离单位保持一致 % minPts 密度阈值含自身时实际要求 minPts-1 个邻居 % 输出labels n×1 向量0 未处理-1 噪声0 簇编号 n size(X, 1); labels zeros(n, 1); visited false(n, 1); clusterId 0; % 一次算好距离矩阵n 超过 1 万时改用分块方案见 4.3 节 D pdist2(X, X); for i 1:n if visited(i) continue; end visited(i) true; neighbors find(D(i, :) eps); if numel(neighbors) minPts labels(i) -1; % 暂时标记为噪声 else clusterId clusterId 1; labels(i) clusterId; idx 1; while idx numel(neighbors) j neighbors(idx); if ~visited(j) visited(j) true; jNeighbors find(D(j, :) eps); % 只有核心点才向邻居集合里追加点边界点只入队不扩散 if numel(jNeighbors) minPts neighbors union(neighbors, jNeighbors); end end % 之前被临时判为噪声的点如果落在当前簇的邻域内吸收进来 if labels(j) 0 || labels(j) -1 labels(j) clusterId; end idx idx 1; end end end end代码里有两个细节值得注意。第一pdist2(X, X)会把每个点和自己的距离也算进去也就是距离矩阵对角线上全是 0所以find(D(i,:)eps)天然包含自身。这意味着代码里写minPts5时实际要求是“包含自身共 5 个点”也可以理解为还差 4 个真实邻居。很多新手在这个细节上栽跟头后面 4.3 节会展开说。第二union(neighbors, jNeighbors)会自动排序并去重避免同一个点被反复处理。这个顺序会影响边界点的归属但对核心点的归属没有影响属于 DBSCAN 的固有特性。2.3 选型边界什么时候该用 DBSCAN什么时候不该硬用DBSCAN 不是银弹。选它之前先看清楚你的数据长什么样。判断维度适合 DBSCAN 的数据不适合 DBSCAN 的数据簇形状任意形状环形、细长、半月形数据本来就接近球形分布类别数完全未知不想预设 K业务上必须指定固定 K噪声采集数据里明显有离群点数据干净噪声很少维度23 维点云效果最好超过 20 维且未降维密度各簇密度差不多簇间密度差异极大一个 eps 管不过来我在实际项目里最常把 DBSCAN 用在点云和坐标类数据上比如激光雷达地面点分割、商户位置聚合、社区网格里的需求点位聚类。这类数据天然是二维或三维的噪声明确簇形状不规则DBSCAN 几乎是首选。反过来如果你手里是几百维的文本 TF-IDF 矩阵不要直接扔进 DBSCAN。高维空间里所有点之间的距离会迅速向一个均值集中k-距离曲线变得非常平eps 怎么选都像是撞运气。我一般会先用 PCA 或 UMAP 把维度降到 510 维再进来聚效果会稳定很多。另外要注意的是密度差异。如果数据里一个簇非常密、另一个簇非常稀DBSCAN 的全局 eps 很难同时满足两边。这种时候要么分块处理要么直接去看 OPTICS 或 HDBSCAN不要在一个 eps 上死磕。3. 打开 zip 后先别急着跑文件结构、数据预处理和第一个可视化的演示脚本3.1 zip 里的常见文件组织与依赖检查这类打包代码的文件组织方式通常有规律可循。我见过、也惯用的打包习惯是一个核心函数文件比如dbscan.m或dbscan_core.m一个距离计算辅助函数有的版本会把pdist2封装成cal_distance.m方便你换距离度量一个或多个演示脚本demo.m或demo_dbscan.m再配上几个.mat测试数据文件和一个 README 说明。拿到压缩包后的第一件事不是双击运行 demo而是做依赖检查。把解压后的文件夹添加到 Matlab 路径里然后在编辑器里打开demo脚本右键选择“分析当前文件”Matlab 会列出调用了哪些外部函数。如果里面出现pdist2、knnsearch、rangesearch这一类名称不用慌它们是 Matlab 内置函数分别对应不同版本实现里的距离计算方式。真正的风险在于压缩包里有两个同名文件或者某个辅助函数没被一起打进来。后者最典型症状是运行 demo 时报出“未定义函数或变量”这时候只能回到压缩包原始页面重新核对文件清单不要试图自己现写一个替代函数除非你完全清楚返回值的格式要求。还有一个容易被忽略的问题压缩包里的.m文件名如果和 Matlab 工具箱自带函数重名会出现路径遮蔽跑出莫名其妙的结果。比如有人把核心文件命名为dbscan.m但新版统计工具箱里也提供可选的 DBSCAN 支持函数同名冲突后你调用到的到底是哪一个取决于路径顺序。我的习惯是解压后先把核心文件重命名成dbscan_core.m这样不容易冲突的名字再去跑 demo。3.2 数据预处理从 CSV、Excel 到 DBSCAN 能直接吃的矩阵DBSCAN 的输入就是一个n×d的 double 矩阵所以数据处理环节的核心任务就是把这个矩阵准备好、洗干净。下面这段代码是我在读完 CSV 后的常用处理流程% 读取 CSV每行是一个样本 raw readmatrix(community_features.csv); % 删除含 NaN 的行这类样本参与距离计算会污染结果 raw(any(isnan(raw), 2), :) []; % 如果第一列是编号或 ID一定要去掉否则距离会被 ID 数值主导 raw raw(:, 2:end); % 对量纲差异明显的特征做 z-score 标准化 mu mean(raw); sigma std(raw); sigma(sigma 0) 1; % 常量列不参与聚类避免除零 X (raw - mu) ./ sigma;这个流程里有三个坑。第一ID 列必须删。如果你把 1、2、3……这样的编号留在矩阵里pdist2 计算时会把编号当成一个距离维度聚类结果直接变形。第二readmatrix只在新版本 Matlab 里可用老版本要改成xlsread或csvread这个兼容性问题在 3.1 节的依赖检查阶段就应该意识到。第三标准化要看业务场景。如果你手里是经纬度坐标不能直接 z-score因为经度纬度的参考系和单位是固定的标准化反而破坏了地理位置的真实几何关系。我处理点云坐标时的做法是先把经纬度投影成平面米制坐标比如在站点附近做局部切平面投影再按米为单位选 eps处理用户特征数据时则先 z-score再在标准化空间里选 eps。3.3 最小可运行示例一组带噪声的二维点云跑通并画出来演示脚本一般会自带测试数据但如果 zip 里的数据文件和你自己的数据格式不一致还是得回到“自己生成数据、自己跑通”这条路。下面是我用来验证代码完整性的最小示例脚本% demo_dbscan_simple.m % 构造左下密集簇 右上稀疏簇 全局噪声点 rng(7); n 300; X1 randn(n, 2) * 0.20 [0.4, 0.4]; % 左下簇密度较高 X2 randn(n, 2) * 0.25 [1.6, 1.0]; % 右上簇密度略低 X3 rand(50, 2) * 2.2 - 0.4; % 均匀噪声 X [X1; X2; X3]; % 初始参数eps 和 minPts 先用经验值后面用 4.1 节的 k-距离曲线校正 eps 0.30; minPts 6; labels dbscan_core(X, eps, minPts); % 画图gscatter 会自动给不同簇分配不同颜色 figure; gscatter(X(:, 1), X(:, 2), labels); title(sprintf(DBSCAN 聚类结果 eps%.2f minPts%d, eps, minPts)); xlabel(x); ylabel(y);这段脚本跑通后你会在图上看到两个明显的点簇噪声点被单独标成黑色或灰色看颜色就能读出聚类质量。这里两个簇都不是规则的圆形分布而是带噪声的随机形状正好击中 DBSCAN 相比 k-means 的核心优势不依赖球形假设。跑通之后建议顺手把图导出成论文可用的矢量格式。我的方法是print(gcf, -depsc, result.eps)得到的是矢量 EPS 文件。注意这里的 EPS 是文档格式和 DBSCAN 参数里的 eps 完全不是一回事只是名字撞车别搞混。4. eps 与 MinPts 调参避坑k-距离曲线、参数联动和 5 条血泪经验4.1 先用 k-距离曲线把 eps 定下来读图方法和代码eps 是整个 DBSCAN 里最玄学的参数但不该靠肉眼猜。最靠谱的起步方法是 k-距离曲线对每个点算出它到第 k 近邻的距离排序后画成曲线曲线出现明显拐点的位置就是 eps 的候选值。function choose_eps_by_kdist(X, k) % 画 k-距离排序曲线k 一般取 MinPts % 注意pdist2 矩阵包含自身第 k 列实际对应 k-1 个真实邻居 D pdist2(X, X); sortedD sort(D, 2); kdist sortedD(:, k); figure; plot(sort(kdist, descend), .-); grid on; xlabel(点编号按第 k 近邻距离降序); ylabel([第 num2str(k) 近邻距离]); end调用时把k设为你要用的minPts。读图的要点是找“肘部”曲线前段平缓代表密度高、邻近距离小后段快速上升甚至拖出长尾代表离群点或稀疏区。肘部所在的纵坐标就是 eps 的起点。把eps定在肘部右端而不是左端是大多数新手的习惯性错误——取小了全是噪声取大一小步就全连成一片。如果 k-距离曲线上出现两级甚至三级台阶说明数据里有明显不同密度的簇。这时候一个全局 eps 必然顾此失彼我一般直接放弃 DBSCAN 单参数版本转去用 OPTICS或者先把高密度点和低密度点拆成两批分别聚类不要让一个参数强行覆盖所有尺度。4.2 MinPts 怎么给维度法则与大噪声场景MinPts 的经验值和数据维度绑定得很紧。最经典的一句话规则是取2*d到3*dd 是特征维度。这句话在低维点云上一直好用原因是聚类结果的连通性对 MinPts 并不敏感只要 MinPts 在合理范围内改变它对簇边界的影响远小于改变 eps。所以我的顺序永远是先固定 MinPts再画 k-距离曲线定 eps不要一开始就让两个参数同时自由浮动。数据维度MinPts 起点说明246二维坐标点云、平面坐标369三维点云噪声大时取 8105101220标准化后的特征数据20先降维到 510高维下距离集中别硬套规则还有一个反直觉的趋势当数据量特别大、噪声又明显时MinPts 反而要调大一些。比如几十万个点的激光点云MinPts 取 5 会让任何一条窄带都成为连接桥聚类结果出现大量链式连通取 20 甚至 30能滤掉很多工程测量里的小误差点让簇结构稳定下来。MinPts 越大算法对局部小波动越不敏感代价是边界点变得更容易被划成噪声。4.3 5 条调参与使用中的踩坑记录下面这 5 条是我在多个项目里反复遇到、并且有明确解决路径的坑。按“现象 → 原因 → 解决”记录如下。坑 1所有点都被标成 -1聚类结果完全空白现象运行完 labels 全为 -1图上只有一种颜色。 原因eps 太小邻域里凑不够 minPts 个点或者数据里有量纲差异极大的特征列比如年龄和收入混在一起距离被大数值列主导小数值列上的密度信息全被淹没。 解决先做 z-score 标准化再用 4.1 节的 k-距离曲线确定 eps。如果标准化之后仍然全噪声检查是不是把 ID 列留在了矩阵里。坑 2聚类数量明显比预期少两个簇被一条窄桥硬连成一片现象业务上应该分开的两组点中间只隔了一条很细的点带DBSCAN 却把它们合并成一个簇。 原因这不是 Bug。DBSCAN 的合并依据是密度相连只要两个高密度簇之间存在一条密度可达的通路它就会认为它们属于同一个簇。 解决先调小 eps、调大 MinPts 试一遍如果还是连在一起说明数据本身是连续密度场不存在天然分界这时候应该换方法而不是强行调参。我常对这类数据改用 OPTICS看可达距离图来选择切割位置。坑 3一跑就是十几分钟甚至直接 Out of Memory现象数据量几万点运行后内存飙升代码卡在距离计算处。 原因pdist2(X, X)得到的是n×n矩阵n5 万时就是 20GB绝大多数机器撑不住。 解决对 n 上万的数据不要一次算完整距离矩阵分块计算邻域或者改用rangesearch(X, X, eps)它利用 KD 树只返回邻域内的点不产生完整矩阵。我在一次 5 万点激光点云分割里就是靠 rangesearch 把时间从半小时压到了几分钟。坑 4MinPts 取 3结果出现“糖葫芦串”式的大簇现象聚类结果里出现一个弯弯曲曲的大簇像糖葫芦一样把很多本不相干的点串在一起。 原因MinPts 太小核心点门槛过低一连串本应属于噪声的点都被拉入核心点集合形成链式传播。 解决把 MinPts 提到 2d3d 这个区间。二维数据就取 58三维取 69别贪小。先固定 MinPts 再调 eps顺序不要反。坑 5打开 zip 里的 .m 文件中文注释全是乱码现象代码能运行但注释和 README 里的中文变成一堆乱码看着像文件损坏。 原因打包者通常用 UTF-8 编码保存 .m 文件老版本 Windows 版 Matlab 默认按 GBK 打开编码不匹配。 解决用 Notepad 或 Matlab 编辑器把文件“另存为”并切换编码为 UTF-8如果还是不对就转存为 GB2312。这个坑只影响阅读不影响执行但建议先处理掉否则后续改参数时看错注释代价比想象中高。5. 验证聚类结果并往前再走一步轮廓系数、OPTICS 和自动化选参5.1 轮廓系数只对非噪声点算聚类做完之后第一件事是算一下轮廓系数但要注意把噪声点剔掉。DBSCAN 的输出里-1是噪声标签如果直接把整个 labels 丢给 Matlab 的silhouette函数它会认为-1也是一个真实类别结果完全失真。% 剔除噪声点后再算轮廓系数 valid labels 0; if any(valid) s silhouette(X(valid, :), labels(valid)); fprintf(平均轮廓系数 %.3f负值样本数 %d\n, mean(s), sum(s 0)); end轮廓系数对密度差异大的数据会偏低因为它是按距离均值定义的对密度本身不敏感。所以看到平均轮廓系数只有 0.3 时别急着否定结果先看负值样本主要落在哪里。如果负值集中在簇与簇的交界处这恰恰是 DBSCAN 里边界点的正常表现如果负值均匀散布在簇内部说明 eps 或 MinPts 里有一个明显不合理。我的验证习惯是轮廓系数作为初筛真正的拍板依据是把聚类结果画在散点图上和原始数据的空间分布对照着看一遍。5.2 密度差异大时往 OPTICS 方向走一步如果在 4.1 的 k-距离曲线上看到两个明显平台说明“一个全局 eps 包打天下”的假设不成立。此时不必再纠结 DBSCAN 的参数可以直接去看 OPTICS 的思路它把每个点的“核心距离”和“可达距离”算出来画一条可达距离图图的凹陷处就是自然簇边界不需要预设 eps。我在这类数据上的做法是先用 DBSCAN 快速摸清大概有几个密度层级再改用 OPTICS 做最终结果两个算法的代码结构高度相似往往只需要改核心扩展逻辑和输出格式。5.3 我习惯的调参收尾流程按这套流程收尾已经能搞定绝大多数实际数据数据先行标准化固定 MinPts 在 2d3d画 k-距离曲线定 eps 初值然后在这个初值附近做一次小范围扫描比如 eps 上下浮动 20%同时把 MinPts 取两个相邻值观察核心点归属是否稳定。边界点每次跑出来不一样是正常的核心点不应频繁变动只要核心点稳聚类结果就能用。我把这套流程在点云和用户特征两类数据上重复过很多遍每次都先把可视化图出了再谈算法优化现在已经是我的固定习惯。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Word中MathType公式编号错位的根源与修复 2026/10/1 16:38:27

Word中MathType公式编号错位的根源与修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
游戏逆向分析系统方法论:从目标建模到反调试对抗的完整路径 2026/10/1 16:38:20

游戏逆向分析系统方法论:从目标建模到反调试对抗的完整路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
打家劫舍动态规划详解:从状态定义到滚动数组优化 2026/10/1 16:38:06

打家劫舍动态规划详解:从状态定义到滚动数组优化

最近刷 LeetCode Hot100 刷到第 68 题,正好是 198. 打家劫舍。这题在动态规划里算是最经典的“入门题中的入门题”,但真正能一次写对的人并不多。我见过不少面试者上来就写递归,写一半卡壳;也有人用贪心思路,反例一跑就…

阅读更多 →
DSP国产替代全解析:C2000生态壁垒与F28335迁移实战 2026/10/1 16:37:59

DSP国产替代全解析:C2000生态壁垒与F28335迁移实战

过去这轮芯片缺货里,最难受的不只是ST的客户,TI C2000系列的用户其实更憋屈。TMS320F28335这颗服役十几年的老将,至今仍是电机控制、数字电源、车载OBC项目里的常青树,结果交期一拖,很多人被迫第一次认真研究DSP国产替…

阅读更多 →
HAProxy超时配置与负载均衡算法实战:从线上故障到最佳实践 2026/10/1 16:37:52

HAProxy超时配置与负载均衡算法实战:从线上故障到最佳实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
I2C总线死锁实战:从模式状态机、时钟延展与九脉冲恢复全解析 2026/10/1 16:37:52

I2C总线死锁实战:从模式状态机、时钟延展与九脉冲恢复全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉