聚类分析(Cluster analysis)是一种数据归约技术,旨在揭露一个数据集中观测值的子集;关于“类”的概念是指由若干个观测值组成的群组。所谓聚类分析就是把大量的观测值归约为若干个类,群组内的观测值间的相似度比群间的观测相似度高。常用的聚类方法有层次聚类、划分聚类,下面一起来学习吧~
层次聚类 hierarchical agglomerative clustering
1、基本概念
- 特征: 起初定义每一个观测值属于一类;聚类就是每一次把两类聚成新的一类,直到所有的类聚成单个类为止。
- 适用范围:对于小样本来说很实用(150个观测以下)
- 聚类算法:常见的算法有 单联动(single)、全联动(complete)、平均联动(average)、质心(centroid),Ward法。常用的是平均联动
2、算法步骤
(1)定义每一个观测值为一类;
(2)计算每一类和其它各类的距离;
- 不同算法都需要首先计算被聚类的实体之间的距离;
- 每个观测值之间常用的距离量度是欧几里得距离(针对连续型变量),例见p345;
- 由于观测的变量性质不同,对距离的贡献可能差异很大,因此,需要对各变量做归一化处理(均值为0,标准差为1)
(3)把“距离”最短的两类合并成一类,这样类的个数就减少一个;
- 这里的“距离”应该不是指第二步直接的欧几里得距离,而是涉及的算法距离。
- 不同算法主要区别就是两类之间的距离定义不同。(详见p346)
(4)重复步骤1和步骤2,直到包含所有观测值的类合并成单个类为止。
3、实验示例
基于5种营养标准含量(变量)的27种食物(观测)进行层次聚类分析,探索不同食物的相同点与不同点,并分成有意义的类。此处层次聚类算法以平均联动(average)为例。
注意!在选择变量时,一定要慎重。要选择可能对识别和理解数据中不同观测值分组有重要影响的变量。~以免“烂泥扶不上墙”~
(1)数据预处理--归一化
par(ask=TRUE)
opar <- par(no.readonly=FALSE)
data(nutrient, package="flexclust") #加载数据包
head(nutrient, 10)
#可以看出energy这一列的数值差异很大,十分影响后几个变量的贡献,因此需要归一化。
nutrient.scaled <- scale(nutrient)
head(nutrient.scaled, 10)
#这样就舒服很多了
(2)计算欧几里得距离
d <- dist(nutrient.scaled)
#好像dist还是专门的一种储存格式
as.matrix(d)[1:4,1:4]
#局部查看一下
(3)平均联动层次聚类分析
fit.average <- hclust(d, method="average")
plot(fit.average, hang=-1, cex=.8, main="Average Linkage Clustering")
#绘树状图图,聚类可视化。
-
hang=-1
设定展示观测值标签的位置; - 树状图高度刻度代表了该高度(距离)类之间合并判定值--
如图注释:(1)首先是beef braised与smoked ham的最先合并(它们最矮--距离最近);
(2)然后是pork roast 和 pork simmered 的合并(重新计算距离后的最相近的点,下同);
(3)其次为chicken canned 和 tuna canned的合并;
(4)再然后是beef braised/smoked ham的类与pork roast/pork simmered的类的两小类的合并;
.............
最后是sardines canned 与一个大类的合并。
上图已经足够我们理解27种食物基于营养成分的相似性与差异性,也可进行深入的分析:将27种食物分为较少的、有意义的类。基于此,做如下分析步骤:
(1)确定聚类个数
NbClust包提供了众多的指数来确定在一个聚类分析里类的最佳数目。
library(NbClust)
nc <- NbClust(nutrient.scaled, distance="euclidean",
min.nc=2, max.nc=15, method="average")
#直接返回的结论推荐为2
- 提供的数据仍为归一化后的数据;
- distance="euclidean"交代距离量度为欧几里得方法;
- min.nc与max.nc设置可考虑的最小和最大聚类数;
- method交代层次聚类算法。
par(opar)
table(nc$Best.n[1,])
#分别各有四个评判准则赞同聚类个数为2、3、5、15
barplot(table(nc$Best.n[1,]),
xlab="Numer of Clusters", ylab="Number of Criteria",
main="Number of Clusters Chosen by 26 Criteria")
#将上述结果绘制成柱状图。
最终的决策聚类方案可以均试一试,选择一个最具有解释意义的聚类方案。下面以聚类数为5,演示一下。(后来自己做了一下聚类数为2的结果,可能理解了教材为什么不用2的原因了;就是树状图最左边的那个食物单分成了一类...)
(2)获取最终的聚类方案
- 利用cutree()函数把树状图分成5类(砍树.......)
clusters <- cutree(fit.average, k=5)
- 查看5种小类的观测数目
table(clusters)
- 观察5种类的变量平均水平,分别为原水平、归一化后的水平
aggregate(nutrient, by=list(cluster=clusters), median)
aggregate(as.data.frame(nutrient.scaled), by=list(cluster=clusters),
median)
- 可视化聚类方案
plot(fit.average, hang=-1, cex=.8,
main="Average Linkage Clustering\n5 Cluster Solution")
rect.hclust(fit.average, k=5)
由上图,尝试解释每类变量的含义:
- sardines canned 单独生成一类,因为钙的含量多;
- beef heart 单独生成一类,因为富含蛋白质和铁;
- clams类是低蛋白和高铁的;
- 另一较小类都是高能量和高脂肪的食物;
- 最大的一类均含有相对较低的铁。
划分聚类分析 partitioning clustering
1、基本概念
- 特征:首先指定类的个数K,然后观测值被随机分成K类,再重新形成聚合的类。
- 适用范围:大样本观测(成百上千)
- 算法:K均值、基于中心点的划分(PAM)
实验示例为178种意大利葡萄酒(观测)的13种化学成分含量数据(变量)。其实是3类葡萄酒,但是我们假装不知道,看能否分析出来,并且比较两种算法(K均值与PAM)的结果差异。
2、K均值聚类
K均值聚类为最常见的划分方法。
2.1、算法步骤
(1)选择K个中心点(随机选择K个观测),K数值就是我们预期的聚类数。
(2)把每个数据点分配给离它最近的中心点;第一次中心点是随机选择的,但也可以设置参数,选择最优的初始值。
(3)重新计算每类中的点到该类中心点距离的平均值;此时的中心点应该为每一类的均值中心点,对异常值敏感(之后都是如此)
(4)分配每个数据到它最近的中心点;
(5)重复步骤3、4,直到所有的观测值不在被分配或是达到最大的迭代次数(默认10次)
2.2、实验演算
(1)数据预处理:去除第一列干扰数据,并归一化数据。
data(wine, package="rattle")
head(wine)
df <- scale(wine[-1])
(2)确定待提取的聚类个数,同样可用NbClust包判断(顺序与层次聚类分析不同,如前所述,层次聚类分析在最后才确定聚类个数)
library(NbClust)
set.seed(1234)
nc <- NbClust(df, min.nc=2, max.nc=15, method="kmeans")
par(opar)
table(nc$Best.n[1,])
barplot(table(nc$Best.n[1,]),
xlab="Numer of Clusters", ylab="Number of Criteria",
main="Number of Clusters Chosen by 26 Criteria")
因为K值分析在开始时,要随机选择K个中心点,每次可能获得稍有差异的方案。使用
set.seed()
可以保证结果可复制。
此外,在K均值聚类中,类中总的平方值对聚类数量的曲线可能对确定聚类数量是有帮助的,详见p351
(3)K均值聚类分析
set.seed(1234)
fit.km <- kmeans(df, 3, nstart=25)
由于聚类方法对初始中心值的选择很敏感,
nstart=
参数允许尝试多种初始配置并输出最好的一个。比如上例,会生成25个初始配置。
fit.km$size
#三类所包含的样本数
fit.km$centers
#各类中心值(归一化后的数据)
aggregate(wine[-1], by=list(cluster=fit.km$cluster), mean)
#还原数据
(4)最后将聚类结果与原始数据标准结果(第一列数据)进行比对,看看分析质量如何。
- 通过flexclust包的兰德指数评价,变化范围从-1(完全不同意)到1(完全同意)
ct.km <- table(wine$Type, fit.km$cluster)
ct.km
library(flexclust)
randIndex(ct.km)
兰德指数接近0.9,看来K均值聚类算法还不错~
3、基于中心点的划分(PAM)
K均值法对均值异常敏感,相比来说,PAM为更稳健的方法。
3.1、算法步骤
(1)随机选择K个观测(每个都称为中心点);
(2)计算观测值到各个中心的距离;
(3)把每个观测值分配到最近的中心点;
(4)计算每个中心点到每个观测值的距离的总和(总成本);
(5)选择一个该类中不是中心的点,并和中心点互换;
(6)重新把每个点分配到距它最近的中心点;
(7)再次计算总成本;
(8)若新的总成本比步骤4计算的总成本少,就把新的点作为中心点;
(9)重复步骤5-8,直到中心点不变。
3.2、实验演算
- 基于上述2.2,判断聚类数为3后的基础上,进行分析
library(cluster)
set.seed(1234)
fit.pam <- pam(wine[-1], k=3, stand=TRUE)
# 进行PAM分析
fit.pam$medoids
# 查看各类中心值
fit.pam$id.med
# 查看各类观测数
clusplot(fit.pam, main="Bivariate Cluster Plot")
# 聚类可视化
- 最后计算下兰德指数,0.7左右,看来不比K均值法~
ct.pam <- table(wine$Type, fit.pam$clustering)
ct.pam
randIndex(ct.pam)
由于聚类分析有时也能分析出原本肯定不存在的类,因此需要验证(立方聚类规则,见p356)。此外,要想对自己的聚类结果更自信,可以尝试不同的聚类方法,若同一类持续复原,那么就增加了把握~~~
以上就是聚类分析的R语言学习,参考教材《R语言实战(第2版)》