K-means聚类算法及改进策略

原创
2017/02/19 14:31
阅读数 2.3K

 

1.问题描述

    与监督学习“对于输入数据X能预测到变量Y”不同,在无监督学习中,目标变量事先并不知道,我们的任务是“能从数据X中能发现什么”,将有相同特征属性的数据划为一类。本实验的目的是使用聚类的方法,根据不同品种的红酒化学成分分析数据,确定哪些品种之间的某个特征属性是类似的,并将类似的归到一类。

1.1数据集描述

    本实验采用的是UCI的Wine数据集,虽然Wine数据集本身只适用于分类算法,但是由于分类和聚类的目的是相同的,所以只要在取数据集时,不取目标变量即可,即不取目标变量的Wine数据集可以用于聚类算法。

Wine数据集的数据是在意大利同一地区种植,但来自三个不同品种衍生出的葡萄酒的化学分析的结果。该分析确定在每个品种的葡萄酒中发现13成分的数量,即对应13个特征属性。数据集的简要信息可参见表1:

数据集特点:  

多元

实例数:

178

领域:

物理

属性特点:

实数

属性数:

13

捐赠日期

1991-07-01

相关的任务:

分类

缺失值?

没有

网络点击次数:

607541

表1  Wine数据集的简要介绍

从表中可以发现:该数据集有13个属性,且属性是连续的float型数据,原始没有缺失。

1.2数据预处理

    由于wine数据集本身是适用于分类算法的,不能直接用于聚类算法,所以需要对原始数据进行预处理。以其中一条数据为例:

1,14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065

第一列元素代表所属的衍生品种,即目标变量,剩下的属性分别代表:乙醇,苹果酸,灰分,灰Alcalinity,镁,总酚,黄酮,Nonflavanoid酚,Proanthocyanins,颜色强度,色调,稀释葡萄酒OD280 / OD315,脯氨酸。

每行为一组数据,即一个样本分析结果,因此读取数据的时候可以按行读取。

因为本实验采用的是聚类算法,所以不需要用到目标变量,故当读取数据集中的数据时,应该自动过滤掉第一列元素。

因为数据集的属性之间是以逗号分隔的且是float数据类型,所以每行属性通过逗号分割,可以读取到一个list中,为了数据格式的统一,需要将每个数据应用于float函数。

最后为了方便处理,将数据集文件读取到一个矩阵中。

数据预处理流程图如图1:

图1 数据预处理流程

2.算法描述

2.1为什么选择该算法

   要想明白为什么选择该算法,必须先理解聚类和分类的区别。分类就是按照某种标准给对象贴标签,再根据标签来区分归类;聚类是指事先没有“标签”而通过某种成团分析找出事物之间存在聚集性原因的过程,对最终聚集的簇再给出“簇识别”。而现实中,很多数据是没有“标签”的,所以要想分类,就需要使用聚类算法。

    聚类算法的实现形式有很多种,其中最简单的就是k-means聚类算法,通过构建k-means算法并观察其实际效果,分析讨论k-means算法的一些缺陷,然后为了提高聚类性能对聚类算法的效果进行量化度量,接着试图对k-means算法进行后处理以提高聚类性能,提出了二分k-means聚类算法。

2.2 k-means聚类算法

    k-means聚类算法是发现给定数据集的k个簇的算法。簇的个数k是用户给定的,每个簇通过其质心,即簇中所有点的中心来描述。

    k-means算法的工作流程是这样的:首先根据给定的数据集范围随机确定k个初始点作为质心,然后将数据集的每个样本分配到一个簇中,最后更新每个簇的质心为改成所有点的平均值,迭代上述过程直到所有簇稳定为止。

    k-means聚类算法的伪代码表示如下:

       创建k个点作为起始质心

       当任意一个点的簇分配结果发生改变时

           对数据集中的每个样本点

              对每个质心

                  计算质心与样本点之间的距离

              将样本点分配到距其最近的簇

           对每一个簇,计算簇中所有点的均值并将均值作为质心

2.3 二分k-means聚类算法

    k-means算法结束后得到了k个簇质心,被分到某个簇的样本点到簇质心肯定还会存在一定的误差(欧几里得距离),那么我们就可以采用一定的后处理来减小这个距离以提高聚类的性能。首先我们采用SSE(误差平方和)来具体量化聚类效果,SSE越小,表示样本点距离簇质心越近,聚类效果越好。降低SSE的办法有两种:一是增加簇的个数,这种方法肯定可以降低SSE,但是增加簇的个数违背了聚类的目的,我们要做的是保持簇数目不变的情况下提高簇的质量,二是将最大的SSE簇划分成两簇,同时为保持簇数目不变需要合并最近的两个簇或合并SSE增幅最小的两个簇。

    为了克服k-means聚类算法容易陷入局部最小值的问题和提高聚类的性能,提出了二分k-means聚类算法。该算法基本思想是首先将所有的样本点划分到一个簇,然后将该簇一分为二,之后选择其中一个簇继续进行划分,直到得到用户指定的簇数目为止。选择哪个簇进行划分取决于对其划分是否可以最大程度的降低SSE。该算法是基于k-means聚类算法的,因为在尝试将某个簇一分为二的时候采用的策略是k-means算法。

    二分k-means聚类算法的伪代码表示如下:

       将所有点看成一个簇

       当簇数目小于k时

           对于每一个簇

              计算总误差

              在给定的簇上面尝试进行k-means聚类算法,此时k = 2

              计算将该簇一分为二之后的总误差

           选择误差最小的那个簇进行最终划分操作

    二分k-means聚类算法的流程图如图2:

图2 二分k-means聚类算法

3.实验方案

3.1实验方案

本实验涉及到两个算法,k-means算法和改进的二分k-means算法,实验的方案设计是基于wine数据集的。实验整体思路如下:首先讨论聚类算法k-means的优劣性,然后为了提高聚类性能,提出了对k-means算法的后处理策略并设计程序证明,最后给出改进了的二分k-means聚类算法。

(1)设计并实验k-means算法,讨论该算法的优劣性和适用性。

(2)后处理策略:增加簇数目。为了证明不同簇数目会对结果产生影响和寻找合适的簇数目k,依次将簇数目k从2增加到13,对于每个k,分别迭代100次,取SSE的平均值,然后对比实验结果,讨论选择不同的簇数目对聚类结果的影响,最后折中选取比较合适的簇数目k。最后用散点图展示相同算法不同簇数目k对应的SSE。

(3)后处理策略:将SSE值最大的簇进行划分,提出了二分k-menas算法。比较两个聚类算法的性能,对比k-means聚类算法和二分k-means聚类算法孰优孰劣。同样将两个算法应用同一组数据,对于不同的簇数目分别迭代100次取SSE的平均值,并将结果在散点图中展示不同算法相同簇数目下的SSE,另外选择其中两个属性,直接对比两个算法产生的聚类效果。

(4)对于二分k-means算法在尝试簇划分过程中可能出现划分后某簇的样本数为0的问题,给出不同的处理策略。一是试图防止产生簇中包含样本数为0,如果某簇包含的样本个数小于2个,则在尝试簇划分时,跳过该簇。二是在尝试簇划分后对产生样本个数为0的簇进行后处理,有两种处理办法:丢弃样本个数为0 的簇和重做簇划分。本实验采用防止和丢弃的方法,对比并分析两次实验的结果。

3.2实验结果

       (1)取k = 5,将k-means算法迭代1000次的10次耗时结果如下表2:

1

614.762274175s

2

642.86932431s

3

631.952348123s

4

632.261385422s

5

619.53197542s

6

639.52424699s

7

628.098381691s

8

622.378562432s

9

630.446311029s

10

625.142309641s

表2  k-means算法耗时

从结果来开,k-means算法的耗时比较大。

对于k-means算法,通过简单分析即可明白,选择不同的距离计算方法和调整簇质心的策略都会对结果产生影响。因为此时选择计算距离的方法是欧氏距离,且簇质心取的是簇内所有样本的均值,所以优点就是容易实现。因为每个样本点都要对每个簇检查一下距离且所有样本点遍历一遍才调整一次簇质心,所以在大规模的数据集上收敛较慢,比较耗时,又因为选择的是欧式距离,所以容易收敛到局部最小值。故k-means算法适用于数值型的小规模数据集。

(2)k-means算法不同簇数目k对应的SSE,得到的实验结果如图3:

图3 k-means算法不同簇数目k对应的SSE

从实验结果分析可知:随着k的增加,SSE整体上逐渐减小的。k = 3时突然增加是因为簇质心是随机的,具有不确定性,最终导致结果陷入局部最小值。由实验可知:k过大,易陷入局部最小值,k过小,不能完全提取特征。对于本实验,较合适的簇数目可选择为8。

(3)对比k-means聚类算法和二分k-means聚类算法的优劣性,对于不同的簇数目k从2依次增加到13,分别迭代100次取SSE的平均值,得到的实验数据如图4,对应的散点图如图5,上方为k-means算法画出的,下方为二分k-means算法得到的,选取wine数据集的第1、13个特征属性展示应用k-means算法和二分k-means算法后产生的聚类效果如图6、7所示:

图4 不同算法不同簇对应的SSE和运行时间

图5 不同算法不同簇对应的SSE散点图

图6 k-means算法聚类效果

图7 二分k-means算法聚类效果

从上述实验结果可知:k-means算法的聚类性能不如二分k-means算法,但是改进的二分k-means算法明显更耗时,收敛速度较k-means算法慢,因为尝试簇划分的过程其实就是一次调用k-menas的过程,所以二分k-means算法仍适用于较小规模的数据集。

(4)对于二分k-means算法在尝试簇划分过程中可能出现划分后某簇的样本个数为0的问题,图8展示的是丢弃产生的包含0个样本数的簇,图9展示的是如果被划分的簇中包含的样本个数少于2个,则该簇不参与尝试划分。

图8 丢弃划分后产生的0簇

图9 若产生0簇则跳过

从实验过程来开,防止发生切片后样本个数为0,即被划分的簇中包含的样本个数少于2个不参与尝试划分较容易实现,得到的聚类性能更好。丢弃操作的时间消耗更好一点,但是会发生每次实际迭代次数的不均衡性。

4.结果分析

    实验结果表明:对于k-means算法选择不同的距离计算方法和调整簇质心的策略都会对结果产生影响,这也是针对不同的数据集和实验目的,可以优化的两个关键点。k-means算法的优点是容易实现;缺点是在大规模的数据集上收敛较慢,比较耗时,且容易收敛到局部最小值。所以k-means算法适用于数值型的小规模数据集。在降低SSE的后处理中,增加簇数目的方案不具有普遍适用性,但是可以根据此方法找到最合适的聚类簇个数;将最大SSE簇二分的方案得到的二分k-means算法,虽然提升了聚类性能,但是却增加了时间开销,还会产生划分后簇中包含样本个数为0 的问题,在处理样本个数为0的问题中,被划分的簇中包含的样本个数少于2个不参与尝试的策略更好一点。

 

 

附件:

wine数据集

1,14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065
1,13.2,1.78,2.14,11.2,100,2.65,2.76,.26,1.28,4.38,1.05,3.4,1050
1,13.16,2.36,2.67,18.6,101,2.8,3.24,.3,2.81,5.68,1.03,3.17,1185
1,14.37,1.95,2.5,16.8,113,3.85,3.49,.24,2.18,7.8,.86,3.45,1480
1,13.24,2.59,2.87,21,118,2.8,2.69,.39,1.82,4.32,1.04,2.93,735
1,14.2,1.76,2.45,15.2,112,3.27,3.39,.34,1.97,6.75,1.05,2.85,1450
1,14.39,1.87,2.45,14.6,96,2.5,2.52,.3,1.98,5.25,1.02,3.58,1290
1,14.06,2.15,2.61,17.6,121,2.6,2.51,.31,1.25,5.05,1.06,3.58,1295
1,14.83,1.64,2.17,14,97,2.8,2.98,.29,1.98,5.2,1.08,2.85,1045
1,13.86,1.35,2.27,16,98,2.98,3.15,.22,1.85,7.22,1.01,3.55,1045
1,14.1,2.16,2.3,18,105,2.95,3.32,.22,2.38,5.75,1.25,3.17,1510
1,14.12,1.48,2.32,16.8,95,2.2,2.43,.26,1.57,5,1.17,2.82,1280
1,13.75,1.73,2.41,16,89,2.6,2.76,.29,1.81,5.6,1.15,2.9,1320
1,14.75,1.73,2.39,11.4,91,3.1,3.69,.43,2.81,5.4,1.25,2.73,1150
1,14.38,1.87,2.38,12,102,3.3,3.64,.29,2.96,7.5,1.2,3,1547
1,13.63,1.81,2.7,17.2,112,2.85,2.91,.3,1.46,7.3,1.28,2.88,1310
1,14.3,1.92,2.72,20,120,2.8,3.14,.33,1.97,6.2,1.07,2.65,1280
1,13.83,1.57,2.62,20,115,2.95,3.4,.4,1.72,6.6,1.13,2.57,1130
1,14.19,1.59,2.48,16.5,108,3.3,3.93,.32,1.86,8.7,1.23,2.82,1680
1,13.64,3.1,2.56,15.2,116,2.7,3.03,.17,1.66,5.1,.96,3.36,845
1,14.06,1.63,2.28,16,126,3,3.17,.24,2.1,5.65,1.09,3.71,780
1,12.93,3.8,2.65,18.6,102,2.41,2.41,.25,1.98,4.5,1.03,3.52,770
1,13.71,1.86,2.36,16.6,101,2.61,2.88,.27,1.69,3.8,1.11,4,1035
1,12.85,1.6,2.52,17.8,95,2.48,2.37,.26,1.46,3.93,1.09,3.63,1015
1,13.5,1.81,2.61,20,96,2.53,2.61,.28,1.66,3.52,1.12,3.82,845
1,13.05,2.05,3.22,25,124,2.63,2.68,.47,1.92,3.58,1.13,3.2,830
1,13.39,1.77,2.62,16.1,93,2.85,2.94,.34,1.45,4.8,.92,3.22,1195
1,13.3,1.72,2.14,17,94,2.4,2.19,.27,1.35,3.95,1.02,2.77,1285
1,13.87,1.9,2.8,19.4,107,2.95,2.97,.37,1.76,4.5,1.25,3.4,915
1,14.02,1.68,2.21,16,96,2.65,2.33,.26,1.98,4.7,1.04,3.59,1035
1,13.73,1.5,2.7,22.5,101,3,3.25,.29,2.38,5.7,1.19,2.71,1285
1,13.58,1.66,2.36,19.1,106,2.86,3.19,.22,1.95,6.9,1.09,2.88,1515
1,13.68,1.83,2.36,17.2,104,2.42,2.69,.42,1.97,3.84,1.23,2.87,990
1,13.76,1.53,2.7,19.5,132,2.95,2.74,.5,1.35,5.4,1.25,3,1235
1,13.51,1.8,2.65,19,110,2.35,2.53,.29,1.54,4.2,1.1,2.87,1095
1,13.48,1.81,2.41,20.5,100,2.7,2.98,.26,1.86,5.1,1.04,3.47,920
1,13.28,1.64,2.84,15.5,110,2.6,2.68,.34,1.36,4.6,1.09,2.78,880
1,13.05,1.65,2.55,18,98,2.45,2.43,.29,1.44,4.25,1.12,2.51,1105
1,13.07,1.5,2.1,15.5,98,2.4,2.64,.28,1.37,3.7,1.18,2.69,1020
1,14.22,3.99,2.51,13.2,128,3,3.04,.2,2.08,5.1,.89,3.53,760
1,13.56,1.71,2.31,16.2,117,3.15,3.29,.34,2.34,6.13,.95,3.38,795
1,13.41,3.84,2.12,18.8,90,2.45,2.68,.27,1.48,4.28,.91,3,1035
1,13.88,1.89,2.59,15,101,3.25,3.56,.17,1.7,5.43,.88,3.56,1095
1,13.24,3.98,2.29,17.5,103,2.64,2.63,.32,1.66,4.36,.82,3,680
1,13.05,1.77,2.1,17,107,3,3,.28,2.03,5.04,.88,3.35,885
1,14.21,4.04,2.44,18.9,111,2.85,2.65,.3,1.25,5.24,.87,3.33,1080
1,14.38,3.59,2.28,16,102,3.25,3.17,.27,2.19,4.9,1.04,3.44,1065
1,13.9,1.68,2.12,16,101,3.1,3.39,.21,2.14,6.1,.91,3.33,985
1,14.1,2.02,2.4,18.8,103,2.75,2.92,.32,2.38,6.2,1.07,2.75,1060
1,13.94,1.73,2.27,17.4,108,2.88,3.54,.32,2.08,8.90,1.12,3.1,1260
1,13.05,1.73,2.04,12.4,92,2.72,3.27,.17,2.91,7.2,1.12,2.91,1150
1,13.83,1.65,2.6,17.2,94,2.45,2.99,.22,2.29,5.6,1.24,3.37,1265
1,13.82,1.75,2.42,14,111,3.88,3.74,.32,1.87,7.05,1.01,3.26,1190
1,13.77,1.9,2.68,17.1,115,3,2.79,.39,1.68,6.3,1.13,2.93,1375
1,13.74,1.67,2.25,16.4,118,2.6,2.9,.21,1.62,5.85,.92,3.2,1060
1,13.56,1.73,2.46,20.5,116,2.96,2.78,.2,2.45,6.25,.98,3.03,1120
1,14.22,1.7,2.3,16.3,118,3.2,3,.26,2.03,6.38,.94,3.31,970
1,13.29,1.97,2.68,16.8,102,3,3.23,.31,1.66,6,1.07,2.84,1270
1,13.72,1.43,2.5,16.7,108,3.4,3.67,.19,2.04,6.8,.89,2.87,1285
2,12.37,.94,1.36,10.6,88,1.98,.57,.28,.42,1.95,1.05,1.82,520
2,12.33,1.1,2.28,16,101,2.05,1.09,.63,.41,3.27,1.25,1.67,680
2,12.64,1.36,2.02,16.8,100,2.02,1.41,.53,.62,5.75,.98,1.59,450
2,13.67,1.25,1.92,18,94,2.1,1.79,.32,.73,3.8,1.23,2.46,630
2,12.37,1.13,2.16,19,87,3.5,3.1,.19,1.87,4.45,1.22,2.87,420
2,12.17,1.45,2.53,19,104,1.89,1.75,.45,1.03,2.95,1.45,2.23,355
2,12.37,1.21,2.56,18.1,98,2.42,2.65,.37,2.08,4.6,1.19,2.3,678
2,13.11,1.01,1.7,15,78,2.98,3.18,.26,2.28,5.3,1.12,3.18,502
2,12.37,1.17,1.92,19.6,78,2.11,2,.27,1.04,4.68,1.12,3.48,510
2,13.34,.94,2.36,17,110,2.53,1.3,.55,.42,3.17,1.02,1.93,750
2,12.21,1.19,1.75,16.8,151,1.85,1.28,.14,2.5,2.85,1.28,3.07,718
2,12.29,1.61,2.21,20.4,103,1.1,1.02,.37,1.46,3.05,.906,1.82,870
2,13.86,1.51,2.67,25,86,2.95,2.86,.21,1.87,3.38,1.36,3.16,410
2,13.49,1.66,2.24,24,87,1.88,1.84,.27,1.03,3.74,.98,2.78,472
2,12.99,1.67,2.6,30,139,3.3,2.89,.21,1.96,3.35,1.31,3.5,985
2,11.96,1.09,2.3,21,101,3.38,2.14,.13,1.65,3.21,.99,3.13,886
2,11.66,1.88,1.92,16,97,1.61,1.57,.34,1.15,3.8,1.23,2.14,428
2,13.03,.9,1.71,16,86,1.95,2.03,.24,1.46,4.6,1.19,2.48,392
2,11.84,2.89,2.23,18,112,1.72,1.32,.43,.95,2.65,.96,2.52,500
2,12.33,.99,1.95,14.8,136,1.9,1.85,.35,2.76,3.4,1.06,2.31,750
2,12.7,3.87,2.4,23,101,2.83,2.55,.43,1.95,2.57,1.19,3.13,463
2,12,.92,2,19,86,2.42,2.26,.3,1.43,2.5,1.38,3.12,278
2,12.72,1.81,2.2,18.8,86,2.2,2.53,.26,1.77,3.9,1.16,3.14,714
2,12.08,1.13,2.51,24,78,2,1.58,.4,1.4,2.2,1.31,2.72,630
2,13.05,3.86,2.32,22.5,85,1.65,1.59,.61,1.62,4.8,.84,2.01,515
2,11.84,.89,2.58,18,94,2.2,2.21,.22,2.35,3.05,.79,3.08,520
2,12.67,.98,2.24,18,99,2.2,1.94,.3,1.46,2.62,1.23,3.16,450
2,12.16,1.61,2.31,22.8,90,1.78,1.69,.43,1.56,2.45,1.33,2.26,495
2,11.65,1.67,2.62,26,88,1.92,1.61,.4,1.34,2.6,1.36,3.21,562
2,11.64,2.06,2.46,21.6,84,1.95,1.69,.48,1.35,2.8,1,2.75,680
2,12.08,1.33,2.3,23.6,70,2.2,1.59,.42,1.38,1.74,1.07,3.21,625
2,12.08,1.83,2.32,18.5,81,1.6,1.5,.52,1.64,2.4,1.08,2.27,480
2,12,1.51,2.42,22,86,1.45,1.25,.5,1.63,3.6,1.05,2.65,450
2,12.69,1.53,2.26,20.7,80,1.38,1.46,.58,1.62,3.05,.96,2.06,495
2,12.29,2.83,2.22,18,88,2.45,2.25,.25,1.99,2.15,1.15,3.3,290
2,11.62,1.99,2.28,18,98,3.02,2.26,.17,1.35,3.25,1.16,2.96,345
2,12.47,1.52,2.2,19,162,2.5,2.27,.32,3.28,2.6,1.16,2.63,937
2,11.81,2.12,2.74,21.5,134,1.6,.99,.14,1.56,2.5,.95,2.26,625
2,12.29,1.41,1.98,16,85,2.55,2.5,.29,1.77,2.9,1.23,2.74,428
2,12.37,1.07,2.1,18.5,88,3.52,3.75,.24,1.95,4.5,1.04,2.77,660
2,12.29,3.17,2.21,18,88,2.85,2.99,.45,2.81,2.3,1.42,2.83,406
2,12.08,2.08,1.7,17.5,97,2.23,2.17,.26,1.4,3.3,1.27,2.96,710
2,12.6,1.34,1.9,18.5,88,1.45,1.36,.29,1.35,2.45,1.04,2.77,562
2,12.34,2.45,2.46,21,98,2.56,2.11,.34,1.31,2.8,.8,3.38,438
2,11.82,1.72,1.88,19.5,86,2.5,1.64,.37,1.42,2.06,.94,2.44,415
2,12.51,1.73,1.98,20.5,85,2.2,1.92,.32,1.48,2.94,1.04,3.57,672
2,12.42,2.55,2.27,22,90,1.68,1.84,.66,1.42,2.7,.86,3.3,315
2,12.25,1.73,2.12,19,80,1.65,2.03,.37,1.63,3.4,1,3.17,510
2,12.72,1.75,2.28,22.5,84,1.38,1.76,.48,1.63,3.3,.88,2.42,488
2,12.22,1.29,1.94,19,92,2.36,2.04,.39,2.08,2.7,.86,3.02,312
2,11.61,1.35,2.7,20,94,2.74,2.92,.29,2.49,2.65,.96,3.26,680
2,11.46,3.74,1.82,19.5,107,3.18,2.58,.24,3.58,2.9,.75,2.81,562
2,12.52,2.43,2.17,21,88,2.55,2.27,.26,1.22,2,.9,2.78,325
2,11.76,2.68,2.92,20,103,1.75,2.03,.6,1.05,3.8,1.23,2.5,607
2,11.41,.74,2.5,21,88,2.48,2.01,.42,1.44,3.08,1.1,2.31,434
2,12.08,1.39,2.5,22.5,84,2.56,2.29,.43,1.04,2.9,.93,3.19,385
2,11.03,1.51,2.2,21.5,85,2.46,2.17,.52,2.01,1.9,1.71,2.87,407
2,11.82,1.47,1.99,20.8,86,1.98,1.6,.3,1.53,1.95,.95,3.33,495
2,12.42,1.61,2.19,22.5,108,2,2.09,.34,1.61,2.06,1.06,2.96,345
2,12.77,3.43,1.98,16,80,1.63,1.25,.43,.83,3.4,.7,2.12,372
2,12,3.43,2,19,87,2,1.64,.37,1.87,1.28,.93,3.05,564
2,11.45,2.4,2.42,20,96,2.9,2.79,.32,1.83,3.25,.8,3.39,625
2,11.56,2.05,3.23,28.5,119,3.18,5.08,.47,1.87,6,.93,3.69,465
2,12.42,4.43,2.73,26.5,102,2.2,2.13,.43,1.71,2.08,.92,3.12,365
2,13.05,5.8,2.13,21.5,86,2.62,2.65,.3,2.01,2.6,.73,3.1,380
2,11.87,4.31,2.39,21,82,2.86,3.03,.21,2.91,2.8,.75,3.64,380
2,12.07,2.16,2.17,21,85,2.6,2.65,.37,1.35,2.76,.86,3.28,378
2,12.43,1.53,2.29,21.5,86,2.74,3.15,.39,1.77,3.94,.69,2.84,352
2,11.79,2.13,2.78,28.5,92,2.13,2.24,.58,1.76,3,.97,2.44,466
2,12.37,1.63,2.3,24.5,88,2.22,2.45,.4,1.9,2.12,.89,2.78,342
2,12.04,4.3,2.38,22,80,2.1,1.75,.42,1.35,2.6,.79,2.57,580
3,12.86,1.35,2.32,18,122,1.51,1.25,.21,.94,4.1,.76,1.29,630
3,12.88,2.99,2.4,20,104,1.3,1.22,.24,.83,5.4,.74,1.42,530
3,12.81,2.31,2.4,24,98,1.15,1.09,.27,.83,5.7,.66,1.36,560
3,12.7,3.55,2.36,21.5,106,1.7,1.2,.17,.84,5,.78,1.29,600
3,12.51,1.24,2.25,17.5,85,2,.58,.6,1.25,5.45,.75,1.51,650
3,12.6,2.46,2.2,18.5,94,1.62,.66,.63,.94,7.1,.73,1.58,695
3,12.25,4.72,2.54,21,89,1.38,.47,.53,.8,3.85,.75,1.27,720
3,12.53,5.51,2.64,25,96,1.79,.6,.63,1.1,5,.82,1.69,515
3,13.49,3.59,2.19,19.5,88,1.62,.48,.58,.88,5.7,.81,1.82,580
3,12.84,2.96,2.61,24,101,2.32,.6,.53,.81,4.92,.89,2.15,590
3,12.93,2.81,2.7,21,96,1.54,.5,.53,.75,4.6,.77,2.31,600
3,13.36,2.56,2.35,20,89,1.4,.5,.37,.64,5.6,.7,2.47,780
3,13.52,3.17,2.72,23.5,97,1.55,.52,.5,.55,4.35,.89,2.06,520
3,13.62,4.95,2.35,20,92,2,.8,.47,1.02,4.4,.91,2.05,550
3,12.25,3.88,2.2,18.5,112,1.38,.78,.29,1.14,8.21,.65,2,855
3,13.16,3.57,2.15,21,102,1.5,.55,.43,1.3,4,.6,1.68,830
3,13.88,5.04,2.23,20,80,.98,.34,.4,.68,4.9,.58,1.33,415
3,12.87,4.61,2.48,21.5,86,1.7,.65,.47,.86,7.65,.54,1.86,625
3,13.32,3.24,2.38,21.5,92,1.93,.76,.45,1.25,8.42,.55,1.62,650
3,13.08,3.9,2.36,21.5,113,1.41,1.39,.34,1.14,9.40,.57,1.33,550
3,13.5,3.12,2.62,24,123,1.4,1.57,.22,1.25,8.60,.59,1.3,500
3,12.79,2.67,2.48,22,112,1.48,1.36,.24,1.26,10.8,.48,1.47,480
3,13.11,1.9,2.75,25.5,116,2.2,1.28,.26,1.56,7.1,.61,1.33,425
3,13.23,3.3,2.28,18.5,98,1.8,.83,.61,1.87,10.52,.56,1.51,675
3,12.58,1.29,2.1,20,103,1.48,.58,.53,1.4,7.6,.58,1.55,640
3,13.17,5.19,2.32,22,93,1.74,.63,.61,1.55,7.9,.6,1.48,725
3,13.84,4.12,2.38,19.5,89,1.8,.83,.48,1.56,9.01,.57,1.64,480
3,12.45,3.03,2.64,27,97,1.9,.58,.63,1.14,7.5,.67,1.73,880
3,14.34,1.68,2.7,25,98,2.8,1.31,.53,2.7,13,.57,1.96,660
3,13.48,1.67,2.64,22.5,89,2.6,1.1,.52,2.29,11.75,.57,1.78,620
3,12.36,3.83,2.38,21,88,2.3,.92,.5,1.04,7.65,.56,1.58,520
3,13.69,3.26,2.54,20,107,1.83,.56,.5,.8,5.88,.96,1.82,680
3,12.85,3.27,2.58,22,106,1.65,.6,.6,.96,5.58,.87,2.11,570
3,12.96,3.45,2.35,18.5,106,1.39,.7,.4,.94,5.28,.68,1.75,675
3,13.78,2.76,2.3,22,90,1.35,.68,.41,1.03,9.58,.7,1.68,615
3,13.73,4.36,2.26,22.5,88,1.28,.47,.52,1.15,6.62,.78,1.75,520
3,13.45,3.7,2.6,23,111,1.7,.92,.43,1.46,10.68,.85,1.56,695
3,12.82,3.37,2.3,19.5,88,1.48,.66,.4,.97,10.26,.72,1.75,685
3,13.58,2.58,2.69,24.5,105,1.55,.84,.39,1.54,8.66,.74,1.8,750
3,13.4,4.6,2.86,25,112,1.98,.96,.27,1.11,8.5,.67,1.92,630
3,12.2,3.03,2.32,19,96,1.25,.49,.4,.73,5.5,.66,1.83,510
3,12.77,2.39,2.28,19.5,86,1.39,.51,.48,.64,9.899999,.57,1.63,470
3,14.16,2.51,2.48,20,91,1.68,.7,.44,1.24,9.7,.62,1.71,660
3,13.71,5.65,2.45,20.5,95,1.68,.61,.52,1.06,7.7,.64,1.74,740
3,13.4,3.91,2.48,23,102,1.8,.75,.43,1.41,7.3,.7,1.56,750
3,13.27,4.28,2.26,20,120,1.59,.69,.43,1.35,10.2,.59,1.56,835
3,13.17,2.59,2.37,20,120,1.65,.68,.53,1.46,9.3,.6,1.62,840
3,14.13,4.1,2.74,24.5,96,2.05,.76,.56,1.35,9.2,.61,1.6,560

源码:

# !/usr/bin/env python
# -*- coding:utf-8 -*-

__author__ = 'Master.Li'

import numpy as np
import scipy.io as sio
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib
import time

# 将数据文件转化为数据矩阵
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split(',')  # 根据数据集的具体情况修改分隔符
        fltLine = map(float, curLine[1:])
        dataMat.append(fltLine)
    return dataMat

# 求两个向量的欧式距离
def distEclud(vecA, vecB):
    return np.sqrt(np.sum(np.power(vecA - vecB, 2)))

# 根据数据集构建包含k个随机质心的簇
def randCent(dataSet, k):
    # 数据集矩阵的列数,即输入数据的维数
    n = np.shape(dataSet)[1]
    # 创建k * n的0初始矩阵
    centroids = np.mat(np.zeros((k, n))).astype('float')
    # 构建包含k个随机质心的簇
    for j in range(n):
        # 取每列的最小值和取值范围,保证产生的随机数在数据集的上下边界之内
        minJ = min(dataSet[:, j])
        rangeJ = float(max(dataSet[:, j]) - minJ)
        centroids[:, j] = minJ + rangeJ * np.random.rand(k, 1)  # 随机出k*1的0~1二维数组
    return centroids

# k-means聚类
def kMeans(dataSet, k, distMeas = distEclud, createCent = randCent):
    m = np.shape(dataSet)[0]  # 数据集矩阵的行数,即输入数据的样本数
    clusterAssment = np.mat(np.zeros((m, 2))).astype('float')  # 用于存储聚类的所属簇下标和与簇质心的距离
    centroids = createCent(dataSet, k)  # 初始化簇质心
    clusterChanged = True
    while clusterChanged:  # 迭代直到所有的簇稳定不变为止
        clusterChanged = False
        # 将所有的样本点迭代一轮,若所有的样本节点所属簇均未改变【误差可能改变】,则停止迭代
        for i in range(m):
            # 将所有的簇迭代一遍,找出距离最近的
            minDist = np.inf  # 默认正无穷np.inf
            minIndex = -1  # 默认-1
            for j in range(k):
                disJI = distMeas(centroids[j, :], dataSet[i, :])
                if disJI < minDist:
                    minDist = disJI
                    minIndex = j
            # 所有的簇迭代一遍结束后,把该样本点划分到距离最近的簇
            if clusterAssment[i, 0] != minIndex:
                clusterChanged = True
            clusterAssment[i, :] = minIndex, minDist ** 2
        # 一轮迭代后调整簇质心
        for cent in range(k):
            ptsInClust = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]]  # 属于该簇的都在数据集的第多少行
            centroids[cent, :] = np.mean(ptsInClust, axis=0)  # 沿矩阵的列方向计算每列的平均值
    # 返回k个簇的质心和各个样本所属的簇结果
    return centroids, clusterAssment

# 二分k-means聚类
def bit_k_means(dataSet, k, distMeas = distEclud):
    m = np.shape(dataSet)[0]  # 数据集矩阵的行数,即输入数据的样本数
    clusterAssment = np.mat(np.zeros((m, 2))).astype('float')  # 用于存储聚类的所属簇下标和与簇质心的距离
    centroid0 = np.mean(dataSet, axis=0).tolist()[0]  # 创建一个初始簇
    centlist = [centroid0]
    for j in range(m):  # 计算每个样本点到初始簇的距离
        clusterAssment[j, 1] = distMeas(np.mat(centroid0), dataSet[j, :])**2
    while(len(centlist) < k):  # 迭代直到簇的数目等于k
        lowerstSSE = np.inf
        for i in range(len(centlist)):
            # 尝试划分每一簇
            ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:, 0].A == i)[0], :]  # 当前簇对应的数据矩阵
            # 为了防止划分后产生空片,不再对包含样本数目小于1的簇进行分片
            if np.shape(ptsInCurrCluster)[0] < 2:
                continue
            centroidMat, splitClusterAss = kMeans(ptsInCurrCluster, 2, distMeas)
            sseNotSplit = sum(clusterAssment[np.nonzero(clusterAssment[:, 0].A != i)[0], 1])  # 剩余数据集的误差
            sseSplit = sum(splitClusterAss[:, 1])  # 划分产生的误差
            # print "sseSplit, and sseNotSplit:", sseSplit, sseNotSplit
            # 记录总误差最小的那个簇
            if(sseSplit + sseNotSplit) < lowerstSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClusterAss = splitClusterAss.copy()
                lowerstSSE = sseSplit + sseNotSplit
        # 因为二分均值,所以结果簇编号只能是0或1,修改结果簇编号为:原簇号和新增簇号
        bestClusterAss[np.nonzero(bestClusterAss[:, 0].A == 1)[0], 0] = len(centlist)
        bestClusterAss[np.nonzero(bestClusterAss[:, 0].A == 0)[0], 0] = bestCentToSplit
        # print "the bestCentToSplit is:", bestCentToSplit
        # print "the len of bestClusterAss is:", len(bestClusterAss)
        # 更新簇列表centlist和样本点分配簇结果矩阵clusterAssment
        centlist[bestCentToSplit] = bestNewCents[0, :].tolist()[0]
        centlist.append(bestNewCents[1, :].tolist()[0])
        clusterAssment[np.nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :] = bestClusterAss
    return np.array(centlist), clusterAssment


# 测试k-means算法
# start =time.clock()
# averperf = np.mat(np.zeros((14, 2))).astype('float')
# averperf[0, :] = float(0), 1 / 100.0
# averperf[1, :] = float(0), 1 / 100.0
# for iters in range(2, 14):
#     data = np.mat(loadDataSet('wine.data')).astype('float')
#     SSE = 0
#     for i in range(100):
#         mycenlist, myclusterass = kMeans(data, iters)
#         SSE = SSE + sum(myclusterass[:, 1].tolist()[0])
#     averperf[iters, :] = float(iters), SSE / 100.0
#     print 'k:' + str(iters) + '  SSE:' + str(SSE / 100.0)
# end = time.clock()
# print('Running time: %s Seconds' % (end-start))
# 测试二分k-means算法,对于产生空片的处理方式:待划分簇样本个数少于2个时不再参与划分
# start = time.clock()
# averperf_bit = np.mat(np.zeros((14, 2))).astype('float')
# averperf_bit[0, :] = float(0), 1 / 100.0
# averperf_bit[1, :] = float(0), 1 / 100.0
# for iters in range(2, 14):
#     data = np.mat(loadDataSet('wine.data')).astype('float')
#     SSE = 0
#     for i in range(100):
#         data = np.mat(loadDataSet('wine.data')).astype('float')
#         mycenlist, myclusterass = bit_k_means(data, iters)
#         SSE = SSE + sum(myclusterass[:, 1].tolist()[0])
#     averperf_bit[iters, :] = float(iters), SSE/100.0
#     print 'k:' + str(iters) + '  SSE:' + str(SSE/100)
# end = time.clock()
# print('Running time: %s Seconds' % (end-start))
# 测试二分k-means算法,对于产生空片的处理方式:丢弃
# start =time.clock()
# averperf_bit = np.mat(np.zeros((14, 2))).astype('float')
# averperf_bit[0, :] = float(0), 1 / 100.0
# averperf_bit[1, :] = float(0), 1 / 100.0
# for iters in range(2, 14):
#     data = np.mat(loadDataSet('wine.data')).astype('float')
#     SSE = 0
#     count = 0
#     for i in range(100):
#         data = np.mat(loadDataSet('wine.data')).astype('float')
#         mycenlist, myclusterass = bit_k_means(data, iters)
#         if sum(myclusterass[:, 1].tolist()[0]) != 0:
#             count += 1
#             SSE = SSE + sum(myclusterass[:, 1].tolist()[0])
#     averperf_bit[iters, :] = float(iters), SSE/count
#     print 'k:' + str(iters) + '  SSE:' + str(SSE / count)
# end = time.clock()
# print('Running time: %s Seconds' % (end-start))
# 将不同簇数目对应的SSE直观的展示出来
# fig = plt.subplot(111)
# fig.scatter(averperf[:, 0], averperf[:, 1],
#             15.0*(np.array(averperf[:, 0].tolist())+1), 15.0*(np.array(averperf[:, 0].tolist())+1))
# fig.scatter(averperf_bit[:, 0], averperf_bit[:, 1],
#             15.0 * (np.array(averperf_bit[:, 0].tolist())+1), 15.0*(np.array(averperf_bit[:, 0].tolist())+1))
# plt.show()
# 选取第1、13个属性展示聚类效果
data = np.mat(loadDataSet('wine.data')).astype('float')
# mycenlist, myclusterass = kMeans(data, 8)
mycenlist, myclusterass = bit_k_means(data, 8)
fig = plt.subplot(111)
fig.scatter(data[:, 0], data[:, 12],
            15.0*(np.array(myclusterass[:, 0].tolist())+1), 15.0*(np.array(myclusterass[:, 0].tolist())+1))
fig.scatter(mycenlist[:, 0], mycenlist[:, 12],
            15.0 * (np.array(range(8))+1), 15.0 * (np.array(range(8))+1), marker='x')
plt.show()

实验结果:(以环境不同可能会出现差别)

测试k-means算法
k:2   SSE:7641.91586138               k:2   SSE:13338.5763893
k:3   SSE:22272.4230958               k:3   SSE:23225.7524535
k:4   SSE:5849.07637421               k:4   SSE:4941.3780086
k:5   SSE:5294.54585746               k:5   SSE:4866.42057501
k:6   SSE:3631.87680781               k:6   SSE:3124.94152352
k:7   SSE:2516.6484106                k:7   SSE:1647.58008399
k:8   SSE:1494.04134518               k:8   SSE:1314.51826352
k:9   SSE:1046.96055906               k:9   SSE:1002.23248104
k:10  SSE:1222.12618646               k:10  SSE:1067.37514096
k:11  SSE:1108.74398517               k:11  SSE:1133.7470248
k:12  SSE:1205.10417233               k:12  SSE:1035.76301856
k:13  SSE:951.200535812               k:13  SSE:1068.23611687
Running time: 910.735488175 Seconds   Running time: 955.34420581 Seconds
陷入了局部最小值

测试二分k-means算法,对于产生空片的处理方式:待划分簇样本个数少于2个时不再参与划分
k:2   SSE:7606.11344984               k:2   SSE:7659.81706715
k:3   SSE:7586.48613803               k:3   SSE:7624.01465561
k:4   SSE:2230.20397039               k:4   SSE:2325.64440731
k:5   SSE:2492.56069688               k:5   SSE:2316.29277637
k:6   SSE:2137.16660146               k:6   SSE:2343.37727013
k:7   SSE:2349.79551904               k:7   SSE:2411.24071659
k:8   SSE:895.150449955               k:8   SSE:874.302257516
k:9   SSE:679.505833261               k:9   SSE:700.938584219
k:10  SSE:673.235349265               k:10  SSE:691.398023277
k:11  SSE:773.478035836               k:11  SSE:797.148221134
k:12  SSE:886.578796682               k:12  SSE:826.221198934
k:13  SSE:884.798393109               k:13  SSE:897.246551917
Running time: 1392.34699552 Seconds   Running time: 1366.60441493 Seconds

测试二分k-means算法,对于产生空片的处理方式:丢弃
丢弃:用数据个数换取时间效率
测试二分k-means算法,对于产生空片的处理方式:重做
重做:用耗时增加换取数据个数

分析上述数据,最终选择k = 8

 

展开阅读全文
打赏
0
2 收藏
分享
加载中
更多评论
打赏
0 评论
2 收藏
0
分享
返回顶部
顶部