DBSCAN全稱Density-Based Spatial Clustering of Applications with Noise,是一種密度聚類算法。算法
和Kmeans相比,不須要事先知道數據的類數。編程
以編程的角度來考慮,具體算法流程以下:測試
1.首先選擇一個待處理數據。spa
2.尋找和待處理數據距離在設置半徑內的數據。指針
3.將找到的半徑內的數據放到一個隊列中。code
4.拿隊列頭數據做爲當前待處理數據並不斷執行第2步。blog
5.直到遍歷完隊列中全部數據,將這些數據記爲一類。索引
6.選擇沒有處理到的數據做爲一個待處理數據執行第2步。隊列
7.直到遍歷完全部數據,算法結束。it
大概就是下圖所示的樣子:
我這裏沒有單獨輸出離羣點,不過稍微改進增長離羣點個數判斷閾值應該就能夠,比較容易修改。
代碼以下:
clear all; close all; clc; theta=0:0.01:2*pi; p1=[3*cos(theta) + rand(1,length(theta))/2;3*sin(theta)+ rand(1,length(theta))/2]; %生成測試數據 p2=[2*cos(theta) + rand(1,length(theta))/2;2*sin(theta)+ rand(1,length(theta))/2]; p3=[cos(theta) + rand(1,length(theta))/2;sin(theta)+ rand(1,length(theta))/2]; p=[p1 p2 p3]'; randIndex = randperm(length(p))'; %打亂數據順序 p=p(randIndex,:); plot(p(:,1),p(:,2),'.') flag = zeros(length(p),1); %聚類標記 clsnum = 0; %類的個數 disnear = 0.3; %聚類半徑 for i=1:length(p) nxtp = p(i,:); %初始聚類半徑內的鄰域點隊列 if flag(i)==0 clsnum = clsnum+1; pcstart = 1; %設置隊列起始指針 preflag = flag; %聚類標記更新 while pcstart<=length(nxtp) %判斷是否完成隊列遍歷 curp = nxtp(pcstart,:); %獲得當前要處理的點 pcstart = pcstart+1; %隊列指針更新 diffp = p-curp; %這裏直接和全部數據比較了,數據量大的時候能夠考慮kdtree dis = sqrt(diffp(:,1).*diffp(:,1)+diffp(:,2).*diffp(:,2)); %判斷當前點與全部點之間的距離 ind = dis<disnear; %獲得距離小於閾值的索引 flag(ind) = clsnum; %設置當前聚類標記 diff_flag = preflag-flag; diff_ind = (preflag-flag)<0; %判斷本次循環相比上次循環增長的點 tmp = zeros(length(p),1); tmp(diff_ind) = clsnum; flag = flag + tmp; %增長的點將其標記爲一類 preflag = flag; %聚類標記更新 nxtp = [nxtp;p(diff_ind,:)]; %增長聚類半徑內的鄰域點隊列 end end end
%聚類可能不止三組,我偷懶不想判斷並plot了 figure; plot(p(flag==1,1),p(flag==1,2),'r.') hold on; plot(p(flag==2,1),p(flag==2,2),'g.') plot(p(flag==3,1),p(flag==3,2),'b.')
結果以下:
原始數據:
聚類結果: