作者:张丹,前况客创始人兼CTO。
前言
聚类属于无监督学习中的一种方法,k-means作为数据挖掘的十大算法之一,是一种最广泛使用的聚类算法。我们使用聚类算法将数据集的点,分到特定的组中,同一组的数据点具有相似的特征,而不同类中的数据点特征差异很大。PAM是对k-means的一种改进算法,能降低异常值对于聚类效果的影响。
聚类可以帮助我们认识未知的数据,发现新的规律。
目录
- k-means实现
- PAM实现
- 可视化和段剖面图
1.k-means实现
k-means算法,是一种最广泛使用的聚类算法。k-means以k作为参数,把数据分为k个组,通过迭代计算过程,将各个分组内的所有数据样本的均值作为该类的中心点,使得组内数据具有较高的相似度,而组间的相似度最低。
k-means工作原理:
- 初始化数据,选择k个对象作为中心点。
- 遍历整个数据集,计算每个点与每个中心点的距离,将它分配给距离中心最近的组。
- 重新计算每个组的平均值,作为新的聚类中心。
- 上面2-3步,过程不断重复,直到函数收敛,不再新的分组情况出现。
k-means聚类,适用于连续型数据集。在计算数据样本之间的距离时,通常使用欧式距离作为相似性度量。k-means支持多种距离计算,还包括maximum, manhattan, pearson, correlation, spearman, kendall等。各种的距离算法的介绍,请参考文章R语言实现46种距离算法
1.1
kmeans()函数实现
在R语言中,我们可以直接调用系统中自带的kmeans()函数,就可以实现k-means的聚类。同时,有很多第三方算法包也提供了k-means的计算函数。当我们需要使用kmeans算法,可以使用第三方扩展的包,比如flexclust, amap等包。
本文的系统环境为:
- Win10 64bit
- R: 3.4.4 x86_64-w64-mingw32
接下来,让我们做一个k-means聚类的例子。首先,创建数据集:
1# 创建数据集 2> set.seed(0) 3> df 100,
使用stats::kmeans()函数,进行聚类。
1> cl 2); cl
对象属性解读:
- cluster,每个点的分组
- centers,聚类的中心点坐标
- totss,总平方和
- withinss,每个分组内的平方和
- tot.withinss,分组总和,sum(withinss)
- betweenss,组间的平方和,totss – tot.withinss
- size,每个组中的数据点数量
- iter,迭代次数。
- ifault,可能有问题的指标
1.2
kcca()函数实现
我们再使用flexclust::kcca()函数,进行聚类。
1# 安装flexclust包 2> # install.packages("flexclust") 3> library(flexclust) 4 5# 进行聚类 6> clk2);clk
我们比较2个不同包的k-means算法,所得到的分组数据都是一样的,中心点位置略有一点偏差。接下来,我们可以把聚类画图。
1> plot(df, col = cl$cluster, main="Kmeans Cluster")2> points(cl$centers, col = 1:3, pch = 10, cex = 4) # 画出kmeans()函数效果
从上图中看到k-means的总分2组,每个组的中心点分别用红色十字圆圈和黑色十字圆圈表示,为组内的所有数据样本的均值。再叠加上kcca()函数聚类后的中心点画图。
1> points(clk@centers, col = 3:4, pch = 10, cex = 4) # 画出kcca()函数效果
新的中心点,分别用别用绿色十字圆圈和蓝色十字圆圈表示。虽然我们使用了相同的算法,分组个数也相同,但中心点还有一些不同的。
这里其实就要对聚类的稳定性进行判断了,有可能是聚类迭代次数过少,就会出现不同的聚类结果,就需要增加迭代次数,达到每次计算结果是一致的。也有可能是因为不同的包,实现的代码有所区别导致的。
k-means算法,也有一些缺点就是对于孤立点是敏感的,会被一些极端值影响聚类的效果。一种改进的算法是PAM,用于解决这个问题。PAM不使用分组平均值作为计算的参照点,而是直接使用每个组内最中心的对象作为中心点。
2.PAM实现
PAM(Partitioning Around Medoids),又叫k-medoids,它可以将数据分组为k个组,k为数量是要事前定义的。PAM与k-means一样,找到距离中心点最小点组成同一类。PAM对噪声和异常值更具鲁棒性,该算法的目标是最小化对象与其最接近的所选对象的平均差异。PAM可以支持混合的数据类型,不仅限于连续变量。
PAM算法分为两个阶段:
- 第1阶段BUILD,为初始集合S选择k个对象的集合。
- 第2阶段SWAP,尝试用未选择的对象,交换选定的中心点,来提高聚类的质量。
PAM的工作原理:
- 初始化数据集,选择k个对象作为中心。
- 遍历数据点,把每个数据点关联到最近中心点m。
- 随机选择一个非中心对象,与中心对象交换,计算交换后的距离成本
- 如果总成本增加,则撤销交换的动作。
- 上面2-4步,过程不断重复,直到函数收敛,中心不再改变为止。
优点与缺点:
- 消除了k-means算法对于孤立点的敏感性。
- 比k-means的计算的复杂度要高。
- 与k-means一样,必须设置k的值。
- 对小的数据集非常有效,对大数据集效率不高。
在R语言中,我们可以通过cluster包来使用pam算法函数。cluster包的安装很简单,一条命令就安装完了。
1> install.packages("cluster")2> library(cluster)
pam()函数定义:
1pam(x, k, diss = inherits(x, "dist"), metric = "euclidean",2 medoids = NULL, stand = FALSE, cluster.only = FALSE,3 do.swap = TRUE,4 keep.diss = !diss && !cluster.only && n 100,
参数列表:
- x,数据框或矩阵,允许有空值(NA)
- k,设置分组数量
- diss,为TRUE时,x为距离矩阵;为FALSE时,x是变量矩阵。默认为FALSE
- metric,设置距离算法,默认为euclidean,距离矩阵忽略此项
- medoids,指定初始的中心,默认为不指定。
- stand,为TRUE时进行标准化,距离矩阵忽略此项。
- cluster.only,为TRUE时,仅计算聚类结果,默认为FALSE
- do.swap,是否进行中心点交换,默认为TRUE;对于超大的数据集,可以不进行交换。
- keep.diss,是否保存距离矩阵数据
- keep.data,是否保存原始数据
- pamonce,一种加速算法,接受值为TRUE,FALSE,0,1,2
- trace.lev,日志打印,默认为0,不打印
我们使用上面已创建好的数据集df,进行pam聚类,设置k=2。
1> kclus 2)
属性解读:
- medoids,中心点的数据值
- id.med,中心点的索引
- clustering,每个点的分组
- objective,目标函数的局部最小值
- isolation,孤立的聚类(用L或L*表示)
- clusinfo,每个组的基本信息
- silinfo,存储各观测所属的类、其邻居类以及轮宽(silhouette)值
- diss,不相似度
- call,执行函数和参数
- data,原始数据集
把聚类画图输出。
1# 画图2> plot(df, col = kclus$clustering, main="Kmedoids Cluster")3> points(kclus$medoids, col = 1:3, pch = 10, cex = 4)
图中,PAM聚类后分为2组,红色一组,黑色一组,用十字圆圈表示2个中心点,可以清晰地看到中心点就是数据点。
我们可以在开始计算时,设置聚类的中心点,为索引1,2坐标点,打印聚类的日志,查看计算过程。
1# 设置聚类的中心为1,2 2> kclus22,medoids=c(
通过日志查看,我们可以清楚地看到,2个中心的选择过程,分别用89替换1,距离成本减少93.214,用27替换2,距离成本减少1.1。
PAM作为k-means的一种改进算法,到底结果是否更合理,还要看最终哪种结果能够准确地表达业务的含义,被业务人员所认可,就需要不断地和业务人员来沟通。
3.可视化和段剖面图
我们实现了聚类计算后,通常需要把复杂的数据逻辑,用简单的语言和图形来解释给业务人员,聚类的可视化就很重要的。如果数据量不太大,参与聚类的指标维度不太多的时候,我们可以用2维散点图,把指标两两画出来。
我们对iris数据集,进行k-means聚类分成3组,画出聚类后的2维散点图结果。
1> res 2> pairs(iris, col = res$cluster + 1)
每2个维度就会生成一张图, 我们可以全面直观的看到聚类的效果。
高级画图工具,使用GGally包中的ggpairs()函数。
1> library(GGally)2> ggpairs(iris,columns = 1:5,mapping=aes(colour=as.character(res$cluster)))
图更漂亮了而且包含更多的信息,除了2维散点图,还包括了相关性检查,分布图,分箱图,频率图等。用这样的可视化效果图与业务人员沟通,一定会非常愉快的。
但是如果数据维度,不止3个而是30个,数据量也不是几百个点,而是几百万个点,再用2维散点图画出来就会很难看了,而且也表达不清,还会失去重点,计算的复杂度也是非常的高。
当数据量和数据维度多起来,我们就需要用段剖面图来做展示了,放弃个体特征,反应的群体特征和规律。
使用flexclust包中的barchart()函数,画出段剖面图,我们还是用iris数据集进行举例。
1> library(flexclust) 2> clk2 -5], k=
如上图所示,每一区块是一个类别,每行是不同的指标。红点表示均值,柱状是这个类别每个指标的情况,透明色表示不重要指标。
查看段剖面图,可以清楚的看到,每个分组中特征是非常明显的。
- Cluster1中,有39个数据点占26%,Sepal.Width指标在均值附近,其他指标都大于均值。
- Cluster2中,有61个数据点占41%,Sepal.Width指标略小于均值,其他指标在均值附近。
- Cluster3中,有50个数据点占33%,Sepal.Width略大于均值,其他指标都小于均值。
从段剖面图,我们可以一眼就能直观地发现数据聚类后的每个分组的总体特征,而不是每个分组中数据的个体特征,对于数据的解读是非常有帮助的。
对于段剖面图,原来我并不知道是什么效果。在和业务人员沟通中,发现他们使用SAS软件做出了很漂亮的段剖面图,而且他们都能理解,后来我发现R语言也有这个工具函数,图确实能极大地帮助进行数据解读,所以写了这篇文章记录一下。
总
结
本文介绍了k-means的聚类计算方法和具体的使用方法,也是对最近做了一个聚类模型的总结。作为数据分析师,我们不仅自己能发现数据的规律,还要让业务人员看明白你的思路,看懂数据的价值,这也是算法本身的价值。