发表在 Science 上的一种新聚类算法

    今年 6 月份,Alex Rodriguez 和 Alessandro Laio 在 Science 上发表了一篇名为《Clustering by fast search and find of density peaks》的文章,为聚类算法的设计提供了一种新的思路。虽然文章出来后遭到了众多读者的质疑,但整体而言,新聚类算法的基本思想很新颖,且简单明快,值得学习。这个新聚类算法的核心思想在于对聚类中心的刻画上,本文将对该算法的原理进行详细介绍,并对其中的若干细节展开讨论。

最后,附上作者在补充材料里提供的 Matlab 示例程序 (加了适当的代码注释)。

clear all
close all
disp(‘The only input needed is a distance matrix file‘)
disp(‘The format of this file should be: ‘)
disp(‘Column 1: id of element i‘)
disp(‘Column 2: id of element j‘)
disp(‘Column 3: dist(i,j)‘)

%% 从文件中读取数据
mdist=input(‘name of the distance matrix file (with single quotes)?\n‘);
disp(‘Reading input distance matrix‘)
xx=load(mdist);
ND=max(xx(:,2));
NL=max(xx(:,1));
if (NL>ND)
  ND=NL;  %% 确保 DN 取为第一二列最大值中的较大者,并将其作为数据点总数
end

N=size(xx,1); %% xx 第一个维度的长度,相当于文件的行数(即距离的总个数)

%% 初始化为零
for i=1:ND
  for j=1:ND
    dist(i,j)=0;
  end
end

%% 利用 xx 为 dist 数组赋值,注意输入只存了 0.5*DN(DN-1) 个值,这里将其补成了满矩阵
%% 这里不考虑对角线元素
for i=1:N
  ii=xx(i,1);
  jj=xx(i,2);
  dist(ii,jj)=xx(i,3);
  dist(jj,ii)=xx(i,3);
end

%% 确定 dc

percent=2.0;
fprintf(‘average percentage of neighbours (hard coded): %5.6f\n‘, percent);

position=round(N*percent/100); %% round 是一个四舍五入函数
sda=sort(xx(:,3)); %% 对所有距离值作升序排列
dc=sda(position);

%% 计算局部密度 rho (利用 Gaussian 核)

fprintf(‘Computing Rho with gaussian kernel of radius: %12.6f\n‘, dc);

%% 将每个数据点的 rho 值初始化为零
for i=1:ND
  rho(i)=0.;
end

% Gaussian kernel
for i=1:ND-1
  for j=i+1:ND
     rho(i)=rho(i)+exp(-(dist(i,j)/dc)*(dist(i,j)/dc));
     rho(j)=rho(j)+exp(-(dist(i,j)/dc)*(dist(i,j)/dc));
  end
end

% "Cut off" kernel
%for i=1:ND-1
%  for j=i+1:ND
%    if (dist(i,j)<dc)
%       rho(i)=rho(i)+1.;
%       rho(j)=rho(j)+1.;
%    end
%  end
%end

%% 先求矩阵列最大值,再求最大值,最后得到所有距离值中的最大值
maxd=max(max(dist)); 

%% 将 rho 按降序排列,ordrho 保持序
[rho_sorted,ordrho]=sort(rho,‘descend‘);

%% 处理 rho 值最大的数据点
delta(ordrho(1))=-1.;
nneigh(ordrho(1))=0;

%% 生成 delta 和 nneigh 数组
for ii=2:ND
   delta(ordrho(ii))=maxd;
   for jj=1:ii-1
     if(dist(ordrho(ii),ordrho(jj))<delta(ordrho(ii)))
        delta(ordrho(ii))=dist(ordrho(ii),ordrho(jj));
        nneigh(ordrho(ii))=ordrho(jj);
        %% 记录 rho 值更大的数据点中与 ordrho(ii) 距离最近的点的编号 ordrho(jj)
     end
   end
end

%% 生成 rho 值最大数据点的 delta 值
delta(ordrho(1))=max(delta(:));

%% 决策图

disp(‘Generated file:DECISION GRAPH‘)
disp(‘column 1:Density‘)
disp(‘column 2:Delta‘)

fid = fopen(‘DECISION_GRAPH‘, ‘w‘);
for i=1:ND
   fprintf(fid, ‘%6.2f %6.2f\n‘, rho(i),delta(i));
end

%% 选择一个围住类中心的矩形
disp(‘Select a rectangle enclosing cluster centers‘)

%% 每台计算机,句柄的根对象只有一个,就是屏幕,它的句柄总是 0
%% >> scrsz = get(0,‘ScreenSize‘)
%% scrsz =
%%            1           1        1280         800
%% 1280 和 800 就是你设置的计算机的分辨率,scrsz(4) 就是 800,scrsz(3) 就是 1280
scrsz = get(0,‘ScreenSize‘);

%% 人为指定一个位置,感觉就没有那么 auto 了 :-)
figure(‘Position‘,[6 72 scrsz(3)/4. scrsz(4)/1.3]);

%% ind 和 gamma 在后面并没有用到
for i=1:ND
  ind(i)=i;
  gamma(i)=rho(i)*delta(i);
end

%% 利用 rho 和 delta 画出一个所谓的“决策图”

subplot(2,1,1)
tt=plot(rho(:),delta(:),‘o‘,‘MarkerSize‘,5,‘MarkerFaceColor‘,‘k‘,‘MarkerEdgeColor‘,‘k‘);
title (‘Decision Graph‘,‘FontSize‘,15.0)
xlabel (‘\rho‘)
ylabel (‘\delta‘)

subplot(2,1,1)
rect = getrect(1);
%% getrect 从图中用鼠标截取一个矩形区域, rect 中存放的是
%% 矩形左下角的坐标 (x,y) 以及所截矩形的宽度和高度
rhomin=rect(1);
deltamin=rect(2); %% 作者承认这是个 error,已由 4 改为 2 了!

%% 初始化 cluster 个数
NCLUST=0;

%% cl 为归属标志数组,cl(i)=j 表示第 i 号数据点归属于第 j 个 cluster
%% 先统一将 cl 初始化为 -1
for i=1:ND
  cl(i)=-1;
end

%% 在矩形区域内统计数据点(即聚类中心)的个数
for i=1:ND
  if ( (rho(i)>rhomin) && (delta(i)>deltamin))
     NCLUST=NCLUST+1;
     cl(i)=NCLUST; %% 第 i 号数据点属于第 NCLUST 个 cluster
     icl(NCLUST)=i;%% 逆映射,第 NCLUST 个 cluster 的中心为第 i 号数据点
  end
end

fprintf(‘NUMBER OF CLUSTERS: %i \n‘, NCLUST);

disp(‘Performing assignation‘)

%% 将其他数据点归类 (assignation)
for i=1:ND
  if (cl(ordrho(i))==-1)
    cl(ordrho(i))=cl(nneigh(ordrho(i)));
  end
end
%% 由于是按照 rho 值从大到小的顺序遍历,循环结束后, cl 应该都变成正的值了. 

%% 处理光晕点,halo这段代码应该移到 if (NCLUST>1) 内去比较好吧
for i=1:ND
  halo(i)=cl(i);
end

if (NCLUST>1)

  % 初始化数组 bord_rho 为 0,每个 cluster 定义一个 bord_rho 值
  for i=1:NCLUST
    bord_rho(i)=0.;
  end

  % 获取每一个 cluster 中平均密度的一个界 bord_rho
  for i=1:ND-1
    for j=i+1:ND
      %% 距离足够小但不属于同一个 cluster 的 i 和 j
      if ((cl(i)~=cl(j))&& (dist(i,j)<=dc))
        rho_aver=(rho(i)+rho(j))/2.; %% 取 i,j 两点的平均局部密度
        if (rho_aver>bord_rho(cl(i)))
          bord_rho(cl(i))=rho_aver;
        end
        if (rho_aver>bord_rho(cl(j)))
          bord_rho(cl(j))=rho_aver;
        end
      end
    end
  end

  %% halo 值为 0 表示为 outlier
  for i=1:ND
    if (rho(i)<bord_rho(cl(i)))
      halo(i)=0;
    end
  end

end

%% 逐一处理每个 cluster
for i=1:NCLUST
  nc=0; %% 用于累计当前 cluster 中数据点的个数
  nh=0; %% 用于累计当前 cluster 中核心数据点的个数
  for j=1:ND
    if (cl(j)==i)
      nc=nc+1;
    end
    if (halo(j)==i)
      nh=nh+1;
    end
  end

  fprintf(‘CLUSTER: %i CENTER: %i ELEMENTS: %i CORE: %i HALO: %i \n‘, i,icl(i),nc,nh,nc-nh);

end

cmap=colormap;
for i=1:NCLUST
   ic=int8((i*64.)/(NCLUST*1.));
   subplot(2,1,1)
   hold on
   plot(rho(icl(i)),delta(icl(i)),‘o‘,‘MarkerSize‘,8,‘MarkerFaceColor‘,cmap(ic,:),‘MarkerEdgeColor‘,cmap(ic,:));
end
subplot(2,1,2)
disp(‘Performing 2D nonclassical multidimensional scaling‘)
Y1 = mdscale(dist, 2, ‘criterion‘,‘metricstress‘);
plot(Y1(:,1),Y1(:,2),‘o‘,‘MarkerSize‘,2,‘MarkerFaceColor‘,‘k‘,‘MarkerEdgeColor‘,‘k‘);
title (‘2D Nonclassical multidimensional scaling‘,‘FontSize‘,15.0)
xlabel (‘X‘)
ylabel (‘Y‘)
for i=1:ND
 A(i,1)=0.;
 A(i,2)=0.;
end
for i=1:NCLUST
  nn=0;
  ic=int8((i*64.)/(NCLUST*1.));
  for j=1:ND
    if (halo(j)==i)
      nn=nn+1;
      A(nn,1)=Y1(j,1);
      A(nn,2)=Y1(j,2);
    end
  end
  hold on
  plot(A(1:nn,1),A(1:nn,2),‘o‘,‘MarkerSize‘,2,‘MarkerFaceColor‘,cmap(ic,:),‘MarkerEdgeColor‘,cmap(ic,:));
end

%for i=1:ND
%   if (halo(i)>0)
%      ic=int8((halo(i)*64.)/(NCLUST*1.));
%      hold on
%      plot(Y1(i,1),Y1(i,2),‘o‘,‘MarkerSize‘,2,‘MarkerFaceColor‘,cmap(ic,:),‘MarkerEdgeColor‘,cmap(ic,:));
%   end
%end
faa = fopen(‘CLUSTER_ASSIGNATION‘, ‘w‘);
disp(‘Generated file:CLUSTER_ASSIGNATION‘)
disp(‘column 1:element id‘)
disp(‘column 2:cluster assignation without halo control‘)
disp(‘column 3:cluster assignation with halo control‘)
for i=1:ND
   fprintf(faa, ‘%i %i %i\n‘,i,cl(i),halo(i));
end

本系列聚类算法的其他链接:

第一章  引言

第二章  预备知识

第三章  直接聚类法

第四章  K-means

第五章  DBSCAN

第六章  OPTICS

第七章 聚类分析的效果评测

第八章 数据尺度化问题

作者: peghoty

出处: http://blog.csdn.net/itplus/article/details/38926837

欢迎转载/分享, 但请务必声明文章出处.

时间: 2024-11-15 02:30:58

发表在 Science 上的一种新聚类算法的相关文章

一种新型聚类算法(Clustering by fast search and find of density peaksd)

最近在学习论文的时候发现了在science上发表的关于新型的基于密度的聚类算法 Kmean算法有很多不足的地方,比如k值的确定,初始结点选择,而且还不能检测费球面类别的数据分布,对于第二个问题,提出了Kmean++,而其他不足还没有解决,dbscan虽然可以对任意形状分布的进行聚类,但是必须指定一个密度阈值,从而去除低于此密度阈值的噪音点,这篇文章解决了这些不足. 本文提出的聚类算法的核心思想在于,对聚类中心的刻画上,而且认为聚类中心同时具有以下两种特点: 本身的密度大,即它被密度均不超过它的邻

博客上的几种新职业介绍

看了很多博客,发现了一些共同点.我觉得可以把这些博主分类一下,形成几种新的职业. 我不是在嘲讽谁,真的. 超文本抄书匠 Hypertext Book Copier 工作描述 手打大师,大段抄录实体书.教材.资料内容,并发表在博客里,作为教程系列,或者记录系列,列出目录超链接,坚称"为学习留下记录"云云.与此同时,可恶的鼠标总是会自动在发表之前勾选发布至首页,大师本人也很无奈. 入职条件 有一块优质的键盘,打字速度快.一套纸质资料,素材必不可少,否则无法开展工作. 要点 在与大家分享的时

Science上发表的超赞聚类算法(转)

作者(Alex Rodriguez, Alessandro Laio)提出了一种很简洁优美的聚类算法, 可以识别各种形状的类簇, 并且其超参数很容易确定. 算法思想 该算法的假设是类簇的中心由一些局部密度比较低的点围绕, 并且这些点距离其他有高局部密度的点的距离都比较大. 首先定义两个值: 局部密度以及到高局部密度点的距离: 其中 是一个截断距离, 是一个超参数.?所以相当于距离点i的距离小于的点的个数. 由于该算法只对的相对值敏感, 所以对dc的选择比较鲁棒, 一种推荐做法是选择使得平均每个点

Zstandard:一种新的无损压缩算法

Zstandard(缩写为Zstd)是一种新的无损压缩算法,旨在提供快速压缩,并实现高压缩比.它既不像LZMA和ZPAQ那样追求尽可能高的压缩比,也不像LZ4那样追求极致的压缩速度. 下面是一组基准测试数据: 压缩算法名称 压缩比 压缩速度(MB/s) 解压速度(MB/s) zlib 1.2.8 -6 3.099 18 275 Zstd 2.872 201 498 zlib 1.2.8 -1 2.73 58 250 LZ4 HC r127 2.72 26 1720 QuickLZ 1.5.1b6

二十八、带给我们一种新的编码思路——EFW框架CS系统开发中的MVC模式探讨

回<[开源]EFW框架系列文章索引>        EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://pan.baidu.com/s/1eQCc69G       前言:记得最初写出Winform版MVC的代码是在公司的一个产品中,产品有几个界面功能比较多,一个界面窗体的代码尽然有1万多行代码,让我们在维护这几个界面的时候非常的痛苦,你可能想可以把这个大的界面拆分成几个小的界面在集成在一起不就好了,但实际上这样

6:知识的领域和结构,一种新的奇思妙想的组合结合

1.三个案例 案例1:幼儿的语言学习 可见现象: 1.听音学语言,只有声音--结果:学不会 2.听看学语言,声音和画面--结果:很容易学会 通过不断的倾听声音和观察的画面形成关联 案例2:PPT训练营 可见现象: 操作技巧很熟练,但是成品整体水平很差劲. 做好PPT的3个领域知识: 1.PPT操作技巧: 插入元素.母版.快捷键.动画 2.足够的审美知识: 空间结构.颜色搭配.元素设计 3.足够的逻辑能力 结构大纲.先后顺序.语言提炼 案例3:记忆数字 一样长度的数字,有些容易记忆有些却很难 20

如何在CentOS/RHEL上安装或升级新的内核版本

Linux内核版本 Linux内核版本分为主线.稳定和长期版本.(所有内核版本都可以在内核开发官方网站https://www.kernel.org上获取)目前我们看到最新的稳定内核版本为4.10.12. 主线版本代表整个Linux内核的一个树干,新的主线版本每2-3个月发布一次,所有的新功能及特性都将会包含主线版本中.稳定内核则是在主线版本中,被认为是"稳定的"得出.稳定内核的任何错误修复都将从主线树中返回,也就是主线内核出现的任何错误(包括之前的任何旧版本内核的错误和BUG)在得到修

5、三种新方法判断是否包含字符串/repeat()方法

传统上,JavaScript只有indexOf方法,来判断一个字符串是否包含在另一个字符串中.ES6又提供了三种新的方法. includes();返回布尔值,表示是否找到了参数字符串: startsWith();返回布尔值,表示参数字符串是否出现在在源字符串的开头: endWith();返回布尔值,表示参数字符串是否出现在源字符串的结尾. var str = "Hello world!"; str.startsWith("Hello") // true str.en

将dll文件注入到其他进程中的一种新方法

http://www.45it.com/windowszh/201212/33946.htm http://www.hx95.cn/Article/OS/201212/65095.html 我们知道将动态连接库注入到其他进程中有很多种方法.最常见的方法是使用钩子函数(Hook),但是这种方法主要有两个缺点:第一如果某个进程没有加载User32.dll,那么Hook DLL将永远也不会被加载.第二Hook DLL加载的时机问题,只有在进程发出User32调用的时候, Hook DLL才有可能被加载