DBSCAN全稱Density-Based Spatial Clustering of Applications with Noise,是一種密度聚類算法。
和Kmeans相比,不需要事先知道數據的類數。
以編程的角度來考慮,具體算法流程如下:
1.首先選擇一個待處理數據。
2.尋找和待處理數據距離在設置半徑內的數據。
3.將找到的半徑內的數據放到一個隊列中。
4.拿隊列頭數據作為當前待處理數據並不斷執行第2步。
5.直到遍歷完隊列中所有數據,將這些數據記為一類。
6.選擇沒有處理到的數據作為一個待處理數據執行第2步。
7.直到遍歷完所有數據,算法結束。
大概就是下圖所示的樣子:
我這里沒有單獨輸出離群點,不過稍微改進增加離群點個數判斷閾值應該就可以,比較容易修改。
代碼如下:
1 clear all; 2 close all; 3 clc; 4
5 theta=0:0.01:2*pi; 6 p1=[3*cos(theta) + rand(1,length(theta))/2;3*sin(theta)+ rand(1,length(theta))/2]; %生成測試數據 7 p2=[2*cos(theta) + rand(1,length(theta))/2;2*sin(theta)+ rand(1,length(theta))/2]; 8 p3=[cos(theta) + rand(1,length(theta))/2;sin(theta)+ rand(1,length(theta))/2]; 9 p=[p1 p2 p3]';
10
11 randIndex = randperm(length(p))'; %打亂數據順序
12 p=p(randIndex,:); 13 plot(p(:,1),p(:,2),'.') 14
15 flag = zeros(length(p),1); %聚類標記 16 clsnum = 0; %類的個數 17 disnear = 0.3; %聚類半徑 18
19 for i=1:length(p) 20 nxtp = p(i,:); %初始聚類半徑內的鄰域點隊列 21 if flag(i)==0
22 clsnum = clsnum+1; 23 pcstart = 1; %設置隊列起始指針 24 preflag = flag; %聚類標記更新 25 while pcstart<=length(nxtp) %判斷是否完成隊列遍歷 26 curp = nxtp(pcstart,:); %得到當前要處理的點 27 pcstart = pcstart+1; %隊列指針更新 28 diffp = p-curp; %這里直接和所有數據比較了,數據量大的時候可以考慮kdtree 29 dis = sqrt(diffp(:,1).*diffp(:,1)+diffp(:,2).*diffp(:,2)); %判斷當前點與所有點之間的距離 30
31 ind = dis<disnear; %得到距離小於閾值的索引 32 flag(ind) = clsnum; %設置當前聚類標記 33
34 diff_flag = preflag-flag; 35 diff_ind = (preflag-flag)<0; %判斷本次循環相比上次循環增加的點 36
37 tmp = zeros(length(p),1); 38 tmp(diff_ind) = clsnum; 39 flag = flag + tmp; %增加的點將其標記為一類 40 preflag = flag; %聚類標記更新 41 nxtp = [nxtp;p(diff_ind,:)]; %增加聚類半徑內的鄰域點隊列 42 end 43 end 44 end 45 %聚類可能不止三組,我偷懶不想判斷並plot了 46 figure; 47 plot(p(flag==1,1),p(flag==1,2),'r.') 48 hold on; 49 plot(p(flag==2,1),p(flag==2,2),'g.') 50 plot(p(flag==3,1),p(flag==3,2),'b.')