brief

聚类分析是一种数据归约技术,旨在揭漏一个数据集中观测值的子类。子类内部之间相似度最高,子类之间差异性最大。至于这个相似度是一个个性化的定义了,所以有很多聚类方法。
最常用的聚类方法包括层次聚类和划分聚类。

  • 层次聚类,每一个观测自成一个类,然后这些类两两合并,直到所有的类都被合并为止。计算相似度的方法有单联动,全联动,平均联动,质心和ward法。
  • 划分聚类,首先指定子类个数K,然后观测被随机分配成K类,再重新聚合成为类。计算相似度的方法包括K-means和围绕中心点的划分PAM

每个聚类方法都有优缺点,甚至聚类的时候出现不存在的类,这些方法最好是对照使用。

基本步骤

有效的聚类分析是一个多步骤的过程,每一步的决策都会影响下一步的分析。所以多探索多尝试最重要。
基本步骤:

  • 选择合适的变量。这些变量对数据的理解和识别区分应该起主要作用,否则再高级的聚类方法也无法弥补聚类变量选不好的问题。
  • 缩放数据。如果数据的波动范围很大,那么该变量对结果的影响也是最大的(这不就是合适的变量嘛)。但用这样的原始数据并不可取,最好用标准化的数据代替。
    下面是三种缩放方法:
df1 <- apply(mydata, 2, function(x){(x-mean(x))/sd(x)})
df2 <- apply(mydata, 2, function(x){x/max(x)})
df3 <- apply(mydata, 2, function(x){(x ̢ mean(x))/mad(x)})
  • 寻找异常点。很多聚类方法对异常值很敏感,他能扭曲我们的聚类方案。可以通过outliers包中的函数检测离群点,mvoutlier包中有识别多元变量的离群点函数。
  • 计算距离。dist()计算距离和选择相应的算法
  • 选择聚类算法。小样本(<150个观测)或者嵌套聚类可以选择层次聚类。划分聚类可以处理大样本但是需要先确定子类个数K。
  • 尝试其他的聚类算法或者改变距离计算方法
  • 确定类的数目。可以使用NbCluster包中的NbCluster()函数,该函数提供了30个不同的指标帮助你进行选择。
  • 提取子类
  • 结果可视化
  • 解读子类。子类内部有何相似性?子类之间有何差异性?
  • 结果验证。有没有可能是数据集或者聚类方法导致了这种结果,事实上这种划分是没有意义的?

其中距离的计算部分:

分层聚类算法_层次聚类


其他计算距离的算法详见帮助文档

?dist

层次聚类

分层聚类算法_分层聚类算法_02


分层聚类算法_r语言_03

分层聚类算法_层次聚类_04

# install.packages("flexclust")

data(nutrient, package="flexclust")
row.names(nutrient) <- tolower(row.names(nutrient))
head(nutrient)
# 缩放数据
nutrient.scaled <- scale(nutrient,center = T,scale = T)
head(nutrient.scaled)
# 计算欧几里得距离
d <- dist(nutrient.scaled)
head(d)
typeof(d)
str(d)
# 将距离数据传入层次聚类函数
# 距离数据
fit.average <- hclust(d, method="average")
plot(fit.average, hang=-1, cex=.8, main="Average Linkage Clustering")

分层聚类算法_层次聚类_05


分层聚类算法_子类_06

  • 上述的层次聚类树可视化了聚类的过程,观测之间的相似性和异质性也可以清楚的看到。接下来我们还可以进行子类的确定,让观测分配到合适的类群。最后进行子类提取
library(NbClust)

# 需要输入需要聚类的数据框,距离计算公式,聚类方法
nc <- NbClust(nutrient.scaled, distance="euclidean",
              min.nc=2, max.nc=15, method="average")
table(nc$Best.n[1,]) # 看看多少个指标支持对应的子类个数

分层聚类算法_聚类_07


有1个指标支持1个子类

有4个指标支持2个子类

有4个指标支持3个子类

有4个指标支持5个子类

Nbcluster推荐聚成2类

# 那就把K设置成 2或者3或者5试试看
clusters <- cutree(fit.average, k=2)  # cutree将层次聚类结果划分为5个部分
# 第一个子类包括了26个观测,第二个子类包括了一个观测
table(clusters)
# 利用聚合函数看看原来的数据框
aggregate(nutrient, by=list(cluster=clusters), median) # 主要是calcium的含量区别

# 可视化看看
plot(fit.average, hang=-1, cex=.8,
     main="Average Linkage Clustering\n2 Cluster Solution")
rect.hclust(fit.average, k=2)

分层聚类算法_子类_08

划分聚类

kmeans

brief

分层聚类算法_层次聚类_09


分层聚类算法_聚类_10

分层聚类算法_r语言_11


分层聚类算法_子类_12

实例

  • 第一步:确定子类个数K值
library(NbClust)

data(wine, package="rattle")
head(wine)
df <- scale(wine[-1])

set.seed(1234)
nc <- NbClust(df, min.nc=2, max.nc=15, method="kmeans")
table(nc$Best.n[1,]) # 3个子类最好
# 或者使用碎石图的方法找拐点,如下代可以实现
wssplot <- function(data, nc=15, seed=1234){
	wss <- (nrow(data)-1)*sum(apply(data,2,var))
	for (i in 2:nc){
		set.seed(seed)
		wss[i] <- sum(kmeans(data, centers=i)$withinss)}
		plot(1:nc, wss, type="b", xlab="Number of Clusters",
		ylab="Within groups sum of squares")
}
wssplot(df)
  • 第二步:聚类
set.seed(1234)
fit.km <- kmeans(df, 3, nstart=25)

str(fit.km)

分层聚类算法_子类_13


size : 每个子类包含的观测数目

iter : 迭代次数

withinss : 子类内部 距离之和

centers: 子类的中心点,都少个变量就会有对应的多少个数值,组成了一个高维的点。可能不好理解,看看看下面的代码。

fit.km$centers
aggregate(wine[-1], by=list(cluster=fit.km$cluster), mean)

分层聚类算法_子类_14


第一个子类的中心点是

分层聚类算法_层次聚类_15

  • 结果怎么样?
    wine$ type是真实的分类,fit.km$ cluster是kmeans的聚类
    可以看到大约6个观测被错误的分配了,三个观测属于第二个子类,却被分到了第一个子类,还有三个观测属于第二个子类,却被分到了第三个子类。

PAM

brief

分层聚类算法_层次聚类_16


分层聚类算法_r语言_17

实例

library(cluster)
set.seed(1234)
fit.pam <- pam(wine[-1], k=3, stand=TRUE)
fit.pam$medoids

clusplot(fit.pam, main="Bivariate Cluster Plot")

分层聚类算法_r语言_18

  • 结果怎么样
table(wine$Type,fit.pam$clustering)

分层聚类算法_子类_19


在这个数据集上PAM表现不如Kmeans。所以数据集和算法相互影响的,每种聚类方法都有优缺点,多对照使用。