上回说到数量生态学笔记||层次聚类,在聚类算法中层次聚类是比较长常用的。它的基本思想是:通过某种相似性测度计算节点之间的相似性,并按相似度由高到低排序,逐步重新连接个节点。在另一类聚类中并不需要分层次,仅对一组对象简单分组,尽量使组内对象之间比组间对象之间的相似度更高。
本小节介绍两种非层次聚类算法,k-均值划分(k-means);围绕中心点划分(PAM)。此处需要注意的是,不同纲量的变量在进行非层次聚类之前应该进行标准化,因为方差的纲量等于变量纲量的平方,如果不标准化,方差无意义。
k-均值划分
k-均值划分使用数据局部结构构建聚类簇:通过确认数据高密度区构建分类组。K-means算法是硬聚类算法,是典型的基于原型的目标函数聚类方法的代表,它是数据点到原型的某种距离作为优化的目标函数,利用函数求极值的方法得到迭代运算的调整规则。K-means算法以欧式距离作为相似度测度,它是求对应某一初始聚类中心向量V最优分类,使得评价指标J最小。算法采用误差平方和准则函数作为聚类准则函数。
算法过程:
- 选定 K 个中心 μk的初值。这个过程通常是针对具体的问题有一些启发式的选取方法,或者大多数情况下采用随机选取的办法。因为k-means并不能保证全局最优,而是否能收敛到全局最优解其实和初值的选取有很大的关系,所以有时候我们会多次选取初值跑 k-means,并取其中最好的一次结果。
-
将每个数据点归类到离它最近的那个中心点所代表的 cluster 中。
用公式
计算出每个 cluster 的新的中心点。
- 重复第二步,一直到迭代了最大的步数或者前后的 J 的值相差小于一个阈值为止。
[k-means百科]https://baike.baidu.com/item/K-means/4934806)
深入理解K-Means聚类算法
k-均值划分是一种线性方法,因此不适用于含有很多零值的原始数据。遇到这样的数据,我们可以先对其进行预转化。为了与之前聚类分析的对象保持一致,可以使用之前已经范数标准化后的物种数据作为案例。
首先,载入数据:
# 加载所需的程序包
library(ade4)
library(vegan) # 应该先加载ade4后加载vegan以避免冲突
library(gclus)
library(cluster)
library(RColorBrewer)
library(labdsv)
library(mvpart)
library(MVPARTwrap) # MVPARTwrap这个程序包必须从本地zip文件安装
# 导入CSV格式的数据
rm(list = ls())
setwd("D:\\Users\\Administrator\\Desktop\\RStudio\\数量生态学\\DATA")
spe <- read.csv("DoubsSpe.csv", row.names=1)
env <- read.csv("DoubsEnv.csv", row.names=1)
spa <- read.csv("DoubsSpa.csv", row.names=1)
# 删除无物种数据的样方8
spe <- spe[-8,]
env <- env[-8,]
spa <- spa[-8,]
# 物种多度数据:先计算样方之间的弦距离矩阵,然后进行单连接聚合聚类
# **************************************************************
spe.norm <- decostand(spe, "normalize")
spe.ch <- vegdist(spe.norm, "euc")
spe.ch.single <- hclust(spe.ch, method="single")
为了与之前的Ward聚类结果比较,设定k=4组进行k-均值划分,并将其结果与ward结果进行比较。
# 预转化后物种数据k-均值划分
# ****************************
spe.kmeans <- kmeans(spe.norm, centers=4, nstart=100)
#注意:即使给定的nstart相同,每次运行上述命令,所产生的结果也不一定
#完全相同,因为每次运算设定的初始结构是随机的。
# 比较当前分4组的分类结果与之前Ward聚类的结果。
table(spe.kmeans$cluster, spebc.ward.g)
spebc.ward.g
1 2 3 4
1 11 1 0 0
2 0 0 0 3
3 0 6 0 0
4 0 0 8 0
#这两个聚类结果是否非常相似?哪个(或哪些)对象有差别?
spe.kmeans$cluster
1 2 3 4 5 6 7 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
1 1 1 1 3 1 1 3 1 1 1 1 1 1 3 3 3 3 4 4 4 2 2 2 4 4 4 4 4
spebc.ward.g
1 2 3 4 5 6 7 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
1 1 1 1 2 1 1 2 1 1 1 1 1 2 2 2 2 2 3 3 3 4 4 4 3 3 3 3 3
#这两个聚类结果是否非常相似?哪个(或哪些)对象有差别?
对于那些无法从原始数据转化后进行欧式距离计算的距离指数(如bray-curtis),必须先将原始的距离矩阵通过主坐标分析(PCoA)获得一个n行的数据表格,然后在进行k-均值划分。
# k-均值划分,2组到10组
# ************************
spe.KM.cascade <- cascadeKM(spe.norm, inf.gr=2, sup.gr=10, iter=100,
criterion="ssi")
plot(spe.KM.cascade, sortg=TRUE,border="white")
#该图显示每个对象在每种分类组数下的归属(图上每行代表一种组数)。图
#内的表格有不同的颜色,每行两种颜色,代表分两组k=2,三种颜色代表k=3,#依此类推。右图代表不同k值条件下的中止标准的统计量。此系列中,到底
#多少组数是最佳方案?如果倾向于较大的组数,哪个是最佳方案呢?
函数casecadeKM()也提供数据结果。其中result给出每种组数k条件下的tess统计量和准则的值,其中partition包含每个组类所含对象的信息。
summary(spe.KM.cascade)
spe.KM.cascade$results
#这里,最小SSE值用于确定在给定组数k下的最佳方案,而calinski和ssi
#指标用于确定最佳k值,两个指标解决不同的问题。
spe.KM.cascade$partition
#记住不同的组数k={2,3,…,10},每次都是独立运行。因此图4.15从下到上
#结构互相独立,与层次聚类树的嵌套结构不同。
# 按照k-均值划分结果重新排列样方
spe[order(spe.kmeans$cluster),]
# 使用函数vegemite()重新排列样方-物种矩阵
ord.KM <- vegemite(spe, spe.kmeans$cluster)
spe[ord.KM$sites, ord.KM$species]
围绕中心点划分(PAM)
围绕中心点划分是从所有的数据观测点寻找k个代表性的对象或形心点,这些代表性的对象应该反映数据的主体结构。k-均值是最小二乘法,pam不是,pam的输入数据可以是原始数据也可以是相异矩阵,并且允许通过轮廓宽度确定最佳分类数。
下面我们利用轮廓宽度图比较k-均值法和pam法的分组结果。
# 形心点划分(PAM)
# 基于弦距离矩阵进行计算
# ***********************
# 聚类簇数量的选择
asw <- numeric(nrow(spe))
# 循环计算2至28组分类数下平均轮廓宽度
# asw对象名称取自"average silhouette width"
for (k in 2:(nrow(spe)-1))
asw[k] <- pam(spe.ch, k, diss=TRUE)$silinfo$avg.width
k.best <- which.max(asw)
cat("", "Silhouette-optimal number of clusters k =", k.best, "\n",
"with an average silhouette width of", max(asw), "\n")
plot(1:nrow(spe), asw, type="h", main="Choice of the number of clusters",
xlab="k (number of groups)", ylab="Average silhouette width")
axis(1, k.best, paste("optimum",k.best,sep="\n"), col="red", font=2,
col.axis="red")
points(k.best, max(asw), pch=16, col="red", cex=1.5)
#当k=2时,PAM具有最佳的方案(asw=0.3841),这并不是我们期望的结果。
#如果选择常用的4组分法,从轮廓宽度角度分析结果并不好(asw=0.2736)。
#尽管如此,我们还是需要分析PAM分4组的情况。
# PAM分4组情况
# PAM分4组情况
spe.ch.pam <- pam(spe.ch, k=4, diss=TRUE)
summary(spe.ch.pam)
spe.ch.pam.g <- spe.ch.pam$clustering
spe.ch.pam$silinfo$widths
# 将当前的分类结果与之前Ward聚类和k-均值划分进行比较
table(spe.ch.pam.g, spebc.ward.g)
table(spe.ch.pam.g, spe.kmeans$cluster)
#PAM结果与Ward聚类和k-均值划分结果显著不同
# k=4组下k-均值法和PAM法轮廓宽带图
par(mfrow=c(1,2))
plot(silhouette(spe.kmeans$cluster,spe.ch), main="轮廓宽度图:k-均值法",
cex.names=0.8, col= spe.kmeans$cluster+1,border="white")
plot(silhouette(spe.ch.pam), main="轮廓宽度图:PAM", cex.names=0.8,
col=spe.ch.pam$silinfo$widths+1,border="white")
#基于此图,可以分辨PAM和k-均值法哪个有更好的平均轮廓值。请将当前
#结果与Ward聚类比较轮廓宽带图。
上述例子表明,即便是目标相同且属于同一大类的聚类方法也可能产生完全不同的结果。我们应该根据哪种方法能够提供更多有用的信息或者能够更好地被环境变量解释来选择合适的聚类方法。
# 绘制4组k-均值样方分组结果沿Doubs河的分布地图
# ***********************************************
plot(spa, asp=1, type="n", main="Four k-means groups",
xlab="x coordinate (km)", ylab="y coordinate (km)")
lines(spa, col="light blue")
text(50, 10, "Upstream", cex=1.2, col="red")
text(25, 115, "Downstream", cex=1.2, col="red")
grKM <- spe.kmeans$cluster
k <- length(levels(factor(grKM)))
for (i in 1:k) {
points(spa[grKM==i,1], spa[grKM==i,2], pch=i+20, cex=3, col=i+1, bg=i+1)
}
text(spa, row.names(spa), cex=0.8, col="white", font=2)
legend("bottomright", paste("Group",1:k), pch=(1:k)+20, col=2:(k+1),
pt.bg=2:(k+1), pt.cex=2, bty="n")