Processing math: 100%

K-means 是最常见的一种聚类分析算法。直觉上来讲,它的步骤非常简单:

  1. 随机选择K个聚类中心
  2. 将每个数据点分类至离其最近的聚类中心
  3. 根据第2步的分类,重新计算每一个分类的中心
  4. 用第3步中的分类中心重新为所有数据点分类
  5. 重复3,4两步直至收敛

R中,K-means聚类算法的函数原型为kmeans(x, centers)(第5行),其中参数x的类型是matrixdata frame,代表一个数值型的数据集(如数据集不是数值型,则应选择一种合适的编码方法);而参数centers则是无符号整型,是K-means中的K值,也即聚类的个数。

一般来说,我们可以通过比较聚类内部聚类之间的相似程度,来作为衡量聚类效果的方法,从统计的角度来说也叫做方差分析。下面的wssplot函数比较了不同K值下的组内方差,这样我们就用画图的办法来确定最优的K值。

In [1]:
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 Group Variance")
}

因为K-means算法对聚类中心的初始值有一定敏感度,通过set.seed()确定随机数种子(这里设为1234),我们可以得到一致的聚类结果。

下面我们通过一个例子来学习如何在R中进行K-means聚类分析。

我们这里使用的意大利红酒数据集可以在UCI 机器学习数据库下载。在R里也可以通过rattle包来直接调取数据集。

In [2]:
data(wine, package="rattle")
In [3]:
head(wine)
Out[3]:
TypeAlcoholMalicAshAlcalinityMagnesiumPhenolsFlavanoidsNonflavanoidsProanthocyaninsColorHueDilutionProline
1114.231.712.4315.61272.83.060.282.295.641.043.921065
2113.21.782.1411.21002.652.760.261.284.381.053.41050
3113.162.362.6718.61012.83.240.32.815.681.033.171185
4114.371.952.516.81133.853.490.242.187.80.863.451480
5113.242.592.87211182.82.690.391.824.321.042.93735
6114.21.762.4515.21123.273.390.341.976.751.052.851450

这个数据集里包含了对每个红酒样本13种不同化学成分的测量结果,共计有178个样本。

为了保证可靠的方差分析,我们先用scale函数对样本特征的取值范围进行归一化。

In [4]:
df <- scale(wine[-1])

下面的图表现了不同K值下组内方差的结果。x轴代表聚类个数,y轴代表平均组内方差。我们可以看到,当聚类数目越大的时候,每组的组内方差就越小。

In [5]:
wssplot(df)

但显然K值并不是越大越好。K值越大,聚类结果的信息量越低。在这个数据集中,我们从上图可以看到,大概在K=3之前,组内方差下降比较明显,K=3之后组内方差下降放缓。看起来K=3是一个比较好的选择。

我们将K=3时的聚类结果保存在fit.km变量中。

In [6]:
fit.km <- kmeans(df, 3, nstart=25)

每一类的样本个数:

In [7]:
fit.km$size
Out[7]:
  1. 51
  2. 65
  3. 62

每一类的聚类中心:

In [8]:
round(fit.km$centers, 2)
Out[8]:
AlcoholMalicAshAlcalinityMagnesiumPhenolsFlavanoidsNonflavanoidsProanthocyaninsColorHueDilutionProline
10.160.870.190.52-0.08-0.98-1.210.72-0.780.94-1.16-1.29-0.41
2-0.92-0.39-0.490.17-0.49-0.080.02-0.030.06-0.90.460.27-0.75
30.83-0.30.36-0.610.580.880.98-0.560.580.170.470.781.12

注意我们在聚类之前,对特征进行了归一化,所以上表中是归一化之后的特征取值。下表则是在原范围内的取值:

In [9]:
round(aggregate(wine[-1], by=list(cluster=fit.km$cluster), mean), 2)
Out[9]:
clusterAlcoholMalicAshAlcalinityMagnesiumPhenolsFlavanoidsNonflavanoidsProanthocyaninsColorHueDilutionProline
1113.133.312.4221.2498.671.680.820.451.157.230.691.7619.06
2212.251.92.2320.0692.742.252.050.361.622.971.062.8510.17
3313.6822.4717.46107.972.8530.291.925.451.073.161100.23

聚类分析属于机器学习中的无监督学习,所以一般来说较难评估其效果。幸运的是,在原数据集中附带了样本的分类信息,也正好是3类,我们可以用混淆矩阵来评估聚类结果:

In [10]:
table(wine$Type, fit.km$cluster)
Out[10]:
   
     1  2  3
  1  0  0 59
  2  3 65  3
  3 48  0  0

可以看到聚类结果较好,分别只有3个第2类的样本被错误归类到了第1类和第3类之中。