设为首页 收藏本站
查看: 2084|回复: 0

[经验分享] 基于密度的聚类算法

[复制链接]

尚未签到

发表于 2015-11-28 17:17:48 | 显示全部楼层 |阅读模式
普通的聚类算法只能发现一些特定形状的簇群,对于一些特殊形状簇群分类效果就很差。
而基于密度的分类就可以发现任意形状的簇。
先来一张效果图。
一个颜色表示一个簇群可以看出来算法发现很多不同形状的簇群。
以下就是算法的设计和实现。
算法聚类了各种不同的簇群,所以第一步是要实现一个,可以找出一个簇群的算法。算法的设计思想就是
找一个点,然后以这个为中心,给定距离为半径画圆,然后看这个圆中有多少个点,如果点的数量大于给定的阈值,那么这个点就是核心点,如果不是不是那么就是边界点或者噪声点。之后用这个方法去判断每一个刚刚圈中点的,看他是不是边界点,或者噪声点。
边界圈中的其他点就不需要再次判断,噪声点也是,但边界点会直接加入到导致他被圈中的核心点的簇群,而噪声点不会被加入到任何簇群中。
算法设计:
写一个函数,可以统计被圈中的数据的索引标签。getPointCircle.m
function re_labels = getPointCircle(data,center,radius)

%Author:Q
�te:2013/9/6
�scribe:得到以center为中心,radius为半径的data中的数据点

%参数说明:
%   输入参数:
%       data            输入数据
%       center          中心数据
%       radius          半径
%
%   输出参数:
%       re_labels       标签,圈中数据的标签

distance    = euclideanDistance(data,center);
index       = distance < radius;
re_labels   = index;

这个函数还使用到了另一个函数,这个函数用于求距离,求一组数据离中心点的欧拉距离(如果以后需要变动,直接变动这个恶函数即可,例如可以变为马氏距离)。euclideanDistance.m
function re_distance = euclideanDistance(data,center)

%Author:Q
&#65533;te:2013/9/1
&#65533;scribe:计算欧几里得距离的函数

%参数说明:
%   输入参数:
%       data            需要计算的数据,支持多个数据,行为数据,列为维度
%       center          计算离这个点的欧式距离,默认为原点
%   数据参数:
%       re_distance     计算所得的欧式距离,支持多个数据

[row column] = size(data);

if nargin == 1
    center = zeros(1,column);
end

tmpdis = data;

for i = 1:column
    tmpdis(:,i) = data(:,i) - center(i);
end

tmpdis      = tmpdis .* tmpdis;
re_distance = zeros(row,1);

for i = 1:row
    re_distance(i) = sqrt(sum(tmpdis(i,:)));
end

最后就是寻找簇群的函数。getNewCluster.mfunction re_lables = getNewCluster(data,center_lab,radius,MinPts)

%Author:Q
&#65533;te:2013/9/6
&#65533;scribe:找出中心为center范围为radius的簇群
%中心点有资格拉点,边界点没有资格

%参数说明:
%   输入参数:
%       data            待聚类数据,默认为没有进行任何处理的,单纯的n为数据
%       center_lab      中心点的索引,为什么写成这个样子?为什么不直接输入点?这样方便一点
%       radius          Eps领域的半径
%       MinPts          点密度,也就是点的数量
%
%   输出参数:
%       re_labels       分出的新簇群的索引号


[row column] = size(data);

labels = zeros(row,1);          %标记那些数据被标记过
stackmark = zeros(row,1) == 1;

checkpt = [];                   %保存所有要进行查找的点

checkpt = [checkpt ; data(center_lab,:)];
stackmark(center_lab) = true;
checkpos = 1;

judge = 0;

while judge == 0
    [tmprow tmpcolumn] = size(checkpt);
    if tmprow < checkpos
        judge = 1;
    else
        center_pt   = checkpt(checkpos,:);
        Eps_labels  = getPointCircle(data,center_pt,radius);
        
        if sum(Eps_labels) >= MinPts
                                            %这个是核心点,可以拉别的点进来
            mark_label              = cmpVector(data,center_pt);
            labels(mark_label)      = 1;         %核心点赋1,表示处理完毕
            Eps_labels(stackmark)   = false;
            Eps_labels(mark_label)  = false;
            checkpt                 = [checkpt ; data(Eps_labels,:)];
            stackmark(Eps_labels)   = true;
        else                                %这个是边界点,不能拉别的点进来
            mark_label = cmpVector(data,center_pt);
            labels(mark_label) = 1;
        end
        
        checkpos = checkpos + 1;
    end
end

re_lables = labels == 1;

最后一个测试代码查看函数是否正确工作。
clear all
close all
clc

datanum = 80;
data = abs(rand(datanum,2))*100;

%function re_lables = getNewCluster(data,center_lab,radius,MinPts)

center_lab = zeros(datanum,1);

center_lab(25) = 1;

labels = center_lab == 1;

re_labels = getNewCluster(data,labels,10,2);

tmpdata = data(re_labels,:);

figure
plot(data(:,1),data(:,2),'*b');
hold on
plot(tmpdata(:,1),tmpdata(:,2),'*','Color',[0.5 0.5 0.5]);

测试结果为:
可见函数分类正确找出了同密度的簇群,而且形状不规则。之后就只需要重复调用该函数对没有分类的数据分类,就可以得到最后的结果
函数代码:dbScan.m
function [re_labels num] = dbScan(data,MinPts,Eps)

%Author:Q
&#65533;te:2013/9/6
&#65533;scribe:密度聚类算法

%参数说明:
%   输入参数:
%       data        待聚类数据
%       MinPts      领域的中心点
%       Eps         邻域半径
%
%   输出参数:
%       re_labels   发分类之后的标签
%       num         有多少个簇群

mark = 1;               %分类标记

[row column] = size(data);

labels = zeros(row,1);          %数据的标签

index_deal = labels == 0;       %未分类数据的索引号

for i = 1:row
    if (labels(i) == 0)         %表示数据未被分类
        center_lab = cmpVector(data,data(i,:));
        labels_ind = getNewCluster(data,center_lab,Eps,MinPts);
        
        
        if sum(labels_ind) == 1
            labels(labels_ind) = -1;            %噪声点为-1
            index_deal(labels_ind) = false;
        else                                    %一个普通的簇群点
            labels(labels_ind) = mark;
            mark = mark + 1;
            index_deal(labels_ind) = false;
        end
    end
end

re_labels = labels;
num = mark;

这个函数使用了另一个cmpVector因为整个过程都是同一份数据,所有对数据的操作都是索引号。所以用到一个cmpVector去寻找数据的索引cmpVector.m
function re_labels = cmpVector(data,vector)

%Author:Q
&#65533;te:2013/9/6
&#65533;scribe:找出Data中行为vector的矩阵的索引
%目的是为了解决不用直接data == vector这个问题,要找出符合条件的索引

%参数说明:
%   输入参数:
%       data        待比较的矩阵
%       vector      待比较的向量
%
%   输出参数:
%       re_labels   返回找出之后的索引

[row column] = size(data);

labels = [];

for i = 1:row
    tmplabe = 0;
   
    if sum(data(i,:) == vector) ~= 0
        tmplabe = 1;
    end
   
    labels = [labels ; tmplabe];
end

re_labels = labels == 1;


最后来一个测试demo

clear all
close all
clc

datanum = 200;
data = abs(rand(datanum,2))*100;

[re_labels labels_num]= dbScan(data,2,8)


figure
index = re_labels == -1;
plot(data(index,1),data(index,2),'.k');

for i = 1:labels_num
    index = re_labels == i;
    hold on
    plot(data(index,1),data(index,2),'*','Color',[abs(rand(1,1)) abs(rand(1,1)) abs(rand(1,1))]);
end

运行查看效果
分类效果还是很不错的。刚刚设置的是只要圈中两个点就可以算为核心点改变参数,设置为3个点为核心点(目地是测试函数是否正常工作)。
可以发现两个点的簇群消失,这些点全部被标记为噪声点。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-144453-1-1.html 上篇帖子: 用java端,通过log4j 把日志写入scribe 日志系统 下篇帖子: CentOS5.5下scribe写入数据到HDFS配置方法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表