聚类算法之k-means算法
一、k-means算法介绍
k-means最早是由James MacQueen在1967年提出的,这一观点能够追溯到1957年Hugo Steinhaus所提出的想法。1957年,斯图亚特最先提出这一标准算法,当初是作为一门应用于脉码调制的技术,直到1982年,这一算法才在贝尔实验室被正式提出。1975年和1979年,Hartigan和Wong分别提出了一个更高效的版本号。k-means算法针对聚类所得簇划分求得最小平方误差,其采用了贪心策略,通过迭代优化来近似求解。
算法描写叙述如下:
1 输入:簇的数目以及数据集D。
2 输出:k个簇的集合。
3 方法:从D中随意选择k个对象作为初始簇中心,遍历所有数据,依据簇中对象的均值,将每一个对象指派到最相似的簇,并更新簇均值,即计算每一个簇中对象的均值以及计算准则函数,直至准则函数不再发生变化。
二、k-means算法的性能分析
1 优点
(1)k-means算法是解决聚类问题的一种经典算法,算法简单、高速。
(2)对处理大数据集,该算法是相对可伸缩的并且高效率的。由于它的复杂度大约是O(nkt),当中n是全部对象的数目,k是簇的数目,t是迭代的次数。通常k<<n。这个算法常常以局部最优结束。
(3)k-means算法尝试找出使平方误差函数值最小的k个划分。当簇是密集的、球状或团状的,而簇与簇之间差别明显时,它的聚类效果非常好。
2 缺点
(1)k-means算法仅仅有在簇的平均值被定义的情况下才使用,不适用于某些应用,如涉及有分类属性的数据不适用。
(2)要求用户必须事先给出要生成的簇的数目k。
(3)对初始值敏感。对于不同的初始值。可能会导致不同的聚类结果。
(4)不适合于发现非凸面形状的簇,或者大小区别非常大的簇。
(5)对于"噪声"和孤立点数据敏感,少量的该类数据可以对平均值产生极大影响。
三、k-means算法的改进
对K-means算法提出的一些改进如下:
1 数据预处理。首先对样本数据进行正规化处理,这样就能防止某些大值属性的数据左右样本间的距离。给定一组含有n个数据的数据集,每个数据含有m个属性。分别计算每个属性的均值、标准差对每条数据进行标准化。
2 初始聚类中心选择。初始聚类中心的选择对最后的聚类效果有非常大的影响,原K-means算法是随机选取k个数据作为聚类中心,而聚类的结果要是同类间尽可能相似,不同类间尽可能相异,所以初始聚类中心的选取要尽可能做到这一点。采用基于距离和的孤立点定义来进行孤立点的预先筛选,并利用两两数据之间的最大距离在剩余数据集合中寻找初始聚类中心。但对于实际数据,孤立点个数往往不可预知。在选择初始聚类中心时,先将孤立点纳入统计范围。在样本中计算对象两两之间的距离,选出距离最大的两个点作为两个不同类的聚类中心,接着从其余的样本对象中找出已经选出来的全部聚类中心的距离和最大的点为还有一个聚类中心,直到选出k个聚类中心。这样做就减少了样本输入顺序对初始聚类中心选择的影响。
3 迭代过程中聚类种子的选择。聚类中心选好以后,就要进行不断的迭代计算,K-means算法是将聚类均值点(类中全部数据的几何中心点)作为新的聚类种子进行新一轮的聚类计算,新的聚类种子可能偏离真正的数据密集区,从而导致偏差,特别是在有孤立点存在的情况下,有非常大的局限性。在选择初始中心点时,由于将孤立点计算在内,所以在迭代过程中要避免孤立点的影响。这里依据聚类种子的计算时,采用簇中那些与第k-1轮聚类种子相似度较大的数据,计算他们的均值点作为第k轮聚类的种子,相当于将孤立点排除在外。孤立点不參与聚类中心的计算。这样聚类中心就不会由于孤立点的原因而明显偏离数据集中的地方。在计算聚类中心的时候,要运用一定的算法将孤立点排除在计算均值点那些数据之外,这里主要采用类中与聚类种子相似度大于某一阈值的数据组成每一个类的一个子集,计算子集中的均值点作为下一轮聚类的聚类种子。为了能让很多其它的数据參与到聚类中心的计算中去,阈值范围要包括大多数的数据。在第k-1轮聚类获得的类,计算该类中全部数据与该类聚类中心的平均距离S,选择类中与聚类种子相似度大于2S的数据组成每一个类的一个子集,不管是否有明显的孤立点存在,以此子集的均值点作为第k轮聚类的聚类种子,两倍的平均距离都能包括大多数的数据。
四、实例分析
运用软件自带的数据集“USArrests”为例,进行K-means算法的实践。对数据进行标准化之后,确定最佳聚类数目。设置随机数种子为123,保证实验的可重复进行。
图1 美国50个州聚类数目碎石图
由图1可知:聚为四类最合适,当然这个没有绝对的,从指标上看,选择坡度变化不明显的点最为最佳聚类数目。由此就得到了聚四类结果如图2:
图2 美国50个州的聚四类图
#R软件代码
library(factoextra)
library(broom)
data("USArrests")
df <- scale(USArrests)
head(df, n = 5)
fviz_nbclust(df, FUNcluster =kmeans,method = "wss")
geom_vline(xintercept = 4, linetype = 2)
set.seed(123)
km_result <- kmeans(df, 4, nstart = 24)
print(km_result)
dd <- cbind(USArrests, cluster = km_result$cluster)
head(dd)
table(dd$cluster)
fviz_cluster(km_result, data = df,
palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
ellipse.type = "euclid",
star.plot = TRUE,
repel = TRUE,
ggtheme = theme_minimal()