KMeans案例
KMeans聚类基于python有两种实现方式,一种是手动写算法实现聚类,另一种是采用写好的算法自动实现聚类,下面针对两种方法进行代码实现
一、数据准备 文件 testSet.txt 数据如下:
1.658985 4.285136
-3.453687 3.424321
4.838138 -1.151539
-5.379713 -3.362104
0.972564 2.924086
-3.567919 1.531611
0.450614 -3.302219
-3.487105 -1.724432
2.668759 1.594842
-3.156485 3.191137
3.165506 -3.999838
-2.786837 -3.099354
4.208187 2.984927
-2.123337 2.943366
0.704199 -0.479481
-0.392370 -3.963704
2.831667 1.574018
-0.790153 3.343144
2.943496 -3.357075
-3.195883 -2.283926
2.336445 2.875106
-1.786345 2.554248
2.190101 -1.906020
-3.403367 -2.778288
1.778124 3.880832
-1.688346 2.230267
2.592976 -2.054368
-4.007257 -3.207066
2.257734 3.387564
-2.679011 0.785119
0.939512 -4.023563
-3.674424 -2.261084
2.046259 2.735279
-3.189470 1.780269
4.372646 -0.822248
-2.579316 -3.497576
1.889034 5.190400
-0.798747 2.185588
2.836520 -2.658556
-3.837877 -3.253815
2.096701 3.886007
-2.709034 2.923887
3.367037 -3.184789
-2.121479 -4.232586
2.329546 3.179764
-3.284816 3.273099
3.091414 -3.815232
-3.762093 -2.432191
3.542056 2.778832
-1.736822 4.241041
2.127073 -2.983680
-4.323818 -3.938116
3.792121 5.135768
-4.786473 3.358547
2.624081 -3.260715
-4.009299 -2.978115
2.493525 1.963710
-2.513661 2.642162
1.864375 -3.176309
-3.171184 -3.572452
2.894220 2.489128
-2.562539 2.884438
3.491078 -3.947487
-2.565729 -2.012114
3.332948 3.983102
-1.616805 3.573188
2.280615 -2.559444
-2.651229 -3.103198
2.321395 3.154987
-1.685703 2.939697
3.031012 -3.620252
-4.599622 -2.185829
4.196223 1.126677
-2.133863 3.093686
4.668892 -2.562705
-2.793241 -2.149706
2.884105 3.043438
-2.967647 2.848696
4.479332 -1.764772
-4.905566 -2.911070
二、python代码实现
1、手动写算法实现聚类, KMeansOnHand.py代码如下:
1 # encoding:utf-8 2 3 import numpy as np 4 """ 5 手动实现聚类 6 """ 7 8 # 将每行数据放入一个数组内列表,返回一个二维列表 9 10 def loadDataSet(fileName): 11 # 创建空列表 12 dataMat = [] 13 fr = open(fileName, "rb") 14 for line in fr.readlines(): 15 # 按照制表符切割每行,返回一个列表list 16 curLine = line.decode("utf-8").strip().split('\t') 17 # 将切分后的每个列表中的元素,以float形式返回,map()内置函数,返回一个map object【注意,在python2.7版本中map直接返回list】,这里需要再包装个list 18 fltLine = list(map(float, curLine)) # 通过map(float, curLine)将列表内的每一个数据转换为float类型,然后放入list中 19 dataMat.append(fltLine) 20 return dataMat 21 22 23 # 两点欧式距离 24 def distEclud(vecA, vecB): 25 # np.power(x1,x2) 对x1中的每个元素求x2次方,不会改变x1。 26 return np.sqrt(np.sum(np.power(vecA - vecB, 2))) 27 28 29 # 随机找到3个中心点的位置坐标,返回一个3*2的矩阵 30 def randCent(dataSet, k): 31 # 返回dataSet列数,2列 32 n = np.shape(dataSet)[1] 33 ''' 34 centerids是一个3*2的矩阵,用于存储三个中心点的坐标 35 ''' 36 centerids = np.mat(np.zeros((k, n))) 37 for j in range(n): 38 # 统计所有行每一列的最小值 39 minJ = min(dataSet[:, j]) 40 # 所有行每列最大值与最小值的差值 41 rangeJ = float(max(dataSet[:, j]) - minJ) 42 # np.random.rand(k,1) 产生k行1列的数组,里面的数据是0~1的浮点型 随机数。 43 array2 = minJ + rangeJ * np.random.rand(k, 1) 44 # 转换成k*1矩阵 赋值给centerids 45 # centerids[:, j] = np.mat(array2) 46 centerids[:, j] = array2 47 return centerids 48 49 50 def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent): 51 # 计算矩阵所有 行数 m=80 52 m = np.shape(dataSet)[0] 53 # zeros((m,2)) 创建一个80行,2列的二维数组 54 # numpy.mat 将二维数组转换成矩阵 【类别号,当前点到类别号中心点的距离】 55 clusterAssment = np.mat(np.zeros((m, 2))) 56 # createCent找到K个随机中心点坐标 57 centerids = createCent(dataSet, k) 58 # print centerids 59 clusterChanged = True 60 while clusterChanged: 61 clusterChanged = False 62 # 遍历80个数据到每个中心点的距离 63 for i in range(m): 64 # np.inf float的最大值,无穷大 65 minDist = np.inf 66 # 当前点属于的类别号 67 minIndex = -1 68 # 每个样本点到三个中心点的距离 69 for j in range(k): 70 # x = centerids[j,:] 71 # print x 72 # 返回两点距离的值 centerids[j, :]是三个中心点,dataSet[i, :]是样本中的每个点 73 distJI = distMeas(centerids[j, :], dataSet[i, :]) 74 if distJI < minDist: 75 # 当前最小距离的值 76 minDist = distJI 77 # 当前最小值属于哪个聚类 78 minIndex = j 79 # 有与上次迭代计算的当前点的类别不相同的点 80 if clusterAssment[i, 0] != minIndex: 81 clusterChanged = True 82 # 将当前点的类别号和最小距离 赋值给clusterAssment的一行 83 clusterAssment[i, :] = minIndex, minDist 84 for cent in range(k): 85 # array = clusterAssment[:,0].A==cent 86 # result = np.nonzero(clusterAssment[:,0].A==cent)[0] 87 # clusterAssment[:,0].A 将0列 也就是类别号转换成数组 88 # clusterAssment[:,0].A==cent 返回的是一列,列中各个元素是 True或者False,True代表的是当前遍历的cent类别 89 # np.nonzero(clusterAssment[:,0].A==cent) 返回数组中值不为False的元素对应的行号下标数组 和列号下标数组 90 # currNewCenter 取出的是对应是当前遍历cent类别的 所有行数据组成的一个矩阵 91 currNewCenter = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]] # clusterAssment[:, 0].A是将clusterAssment[:, 0]转成array数组,[:, 0]表示对所有的行取第一列 92 # numpy.mean 计算矩阵的均值,axis=0计算每列的均值,axis=1计算每行的均值。 93 # 这里是每经过一次while计算都会重新找到各个类别中中心点坐标的位置 ,axis = 0 是各个列求均值 94 centerids[cent, :] = np.mean(currNewCenter, axis=0) 95 # 返回 【 当前三个中心点的坐标】 【每个点的类别号,和到当前中心点的最小距离】 96 return centerids, clusterAssment 97 98 if __name__ == '__main__': 99 # numpy.mat 将数据转换成80*2的矩阵 100 dataMat = np.mat(loadDataSet('./testSet.txt')) 101 k = 3 102 # centerids 三个中心点的坐标。clusterAssment 每个点的类别号|到当前中心点的最小距离 103 centerids, clusterAssment = kMeans(dataMat, k, distMeas=distEclud, createCent=randCent) 104 print(centerids) 105 print(clusterAssment)
运行结果:
[[-2.46154315 2.78737555]
[ 2.87916148 2.54065409]
[-0.68321373 -2.968607 ]]
[[1. 2.12886068]
[0. 1.17900336]
[1. 4.17969841]
[2. 4.71295505]
[1. 1.94477093]
[0. 1.67362246]
[2. 1.18188929]
[2. 3.06753609]
[1. 0.96893225]
[0. 0.80372102]
[2. 3.98448001]
[2. 2.10768253]
[1. 1.40131626]
[0. 0.37244653]
[2. 2.84967758]
[2. 1.03672953]
[1. 0.96780218]
[0. 1.76136981]
[2. 3.64745539]
[2. 2.60428396]
[1. 0.63749452]
[0. 0.71431155]
[2. 3.06349942]
[2. 2.7268031 ]
[1. 1.73446256]
[0. 0.95299726]
[2. 3.40136033]
[2. 3.33258554]
[1. 1.05044206]
[0. 2.01403167]
[2. 1.93550277]
[2. 3.07374815]
[1. 0.85533934]
[0. 1.24263474]
[1. 3.67962042]
[2. 1.96850502]
[1. 2.82869331]
[0. 1.76834366]
[2. 3.53336343]
[2. 3.16752963]
[1. 1.55634792]
[0. 0.28264306]
[2. 4.05601598]
[2. 1.91474539]
[1. 0.84293455]
[0. 0.95587941]
[2. 3.86840903]
[2. 3.12525834]
[1. 0.70438474]
[0. 1.62430415]
[2. 2.81032715]
[2. 3.76748552]
[1. 2.75102005]
[0. 2.39406258]
[2. 3.3201695 ]
[2. 3.32609886]
[1. 0.69395963]
[0. 0.154283 ]
[2. 2.55604156]
[2. 2.56020016]
[1. 0.05368144]
[0. 0.14007598]
[2. 4.28753047]
[2. 2.11157349]
[1. 1.51214357]
[0. 1.15372603]
[2. 2.99193835]
[2. 1.97261219]
[1. 0.82976405]
[0. 0.79065148]
[2. 3.77095664]
[2. 3.99386969]
[1. 1.93235148]
[0. 0.44855365]
[2. 5.3674754 ]
[2. 2.26336341]
[1. 0.50280822]
[0. 0.50980516]
[1. 4.59317314]
[2. 4.22274427]]
2、采用写好的算法自动实现聚类,KMeansByScikitlearn.py代码如下:
1 # encoding:utf-8 2 3 import numpy as np 4 """ 5 手动实现聚类 6 """ 7 8 # 将每行数据放入一个数组内列表,返回一个二维列表 9 10 def loadDataSet(fileName): 11 # 创建空列表 12 dataMat = [] 13 fr = open(fileName, "rb") 14 for line in fr.readlines(): 15 # 按照制表符切割每行,返回一个列表list 16 curLine = line.decode("utf-8").strip().split('\t') 17 # 将切分后的每个列表中的元素,以float形式返回,map()内置函数,返回一个map object【注意,在python2.7版本中map直接返回list】,这里需要再包装个list 18 fltLine = list(map(float, curLine)) # 通过map(float, curLine)将列表内的每一个数据转换为float类型,然后放入list中 19 dataMat.append(fltLine) 20 return dataMat 21 22 23 # 两点欧式距离 24 def distEclud(vecA, vecB): 25 # np.power(x1,x2) 对x1中的每个元素求x2次方,不会改变x1。 26 return np.sqrt(np.sum(np.power(vecA - vecB, 2))) 27 28 29 # 随机找到3个中心点的位置坐标,返回一个3*2的矩阵 30 def randCent(dataSet, k): 31 # 返回dataSet列数,2列 32 n = np.shape(dataSet)[1] 33 ''' 34 centerids是一个3*2的矩阵,用于存储三个中心点的坐标 35 ''' 36 centerids = np.mat(np.zeros((k, n))) 37 for j in range(n): 38 # 统计所有行每一列的最小值 39 minJ = min(dataSet[:, j]) 40 # 所有行每列最大值与最小值的差值 41 rangeJ = float(max(dataSet[:, j]) - minJ) 42 # np.random.rand(k,1) 产生k行1列的数组,里面的数据是0~1的浮点型 随机数。 43 array2 = minJ + rangeJ * np.random.rand(k, 1) 44 # 转换成k*1矩阵 赋值给centerids 45 # centerids[:, j] = np.mat(array2) 46 centerids[:, j] = array2 47 return centerids 48 49 50 def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent): 51 # 计算矩阵所有 行数 m=80 52 m = np.shape(dataSet)[0] 53 # zeros((m,2)) 创建一个80行,2列的二维数组 54 # numpy.mat 将二维数组转换成矩阵 【类别号,当前点到类别号中心点的距离】 55 clusterAssment = np.mat(np.zeros((m, 2))) 56 # createCent找到K个随机中心点坐标 57 centerids = createCent(dataSet, k) 58 # print centerids 59 clusterChanged = True 60 while clusterChanged: 61 clusterChanged = False 62 # 遍历80个数据到每个中心点的距离 63 for i in range(m): 64 # np.inf float的最大值,无穷大 65 minDist = np.inf 66 # 当前点属于的类别号 67 minIndex = -1 68 # 每个样本点到三个中心点的距离 69 for j in range(k): 70 # x = centerids[j,:] 71 # print x 72 # 返回两点距离的值 centerids[j, :]是三个中心点,dataSet[i, :]是样本中的每个点 73 distJI = distMeas(centerids[j, :], dataSet[i, :]) 74 if distJI < minDist: 75 # 当前最小距离的值 76 minDist = distJI 77 # 当前最小值属于哪个聚类 78 minIndex = j 79 # 有与上次迭代计算的当前点的类别不相同的点 80 if clusterAssment[i, 0] != minIndex: 81 clusterChanged = True 82 # 将当前点的类别号和最小距离 赋值给clusterAssment的一行 83 clusterAssment[i, :] = minIndex, minDist 84 for cent in range(k): 85 # array = clusterAssment[:,0].A==cent 86 # result = np.nonzero(clusterAssment[:,0].A==cent)[0] 87 # clusterAssment[:,0].A 将0列 也就是类别号转换成数组 88 # clusterAssment[:,0].A==cent 返回的是一列,列中各个元素是 True或者False,True代表的是当前遍历的cent类别 89 # np.nonzero(clusterAssment[:,0].A==cent) 返回数组中值不为False的元素对应的行号下标数组 和列号下标数组 90 # currNewCenter 取出的是对应是当前遍历cent类别的 所有行数据组成的一个矩阵 91 currNewCenter = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]] # clusterAssment[:, 0].A是将clusterAssment[:, 0]转成array数组,[:, 0]表示对所有的行取第一列 92 # numpy.mean 计算矩阵的均值,axis=0计算每列的均值,axis=1计算每行的均值。 93 # 这里是每经过一次while计算都会重新找到各个类别中中心点坐标的位置 ,axis = 0 是各个列求均值 94 centerids[cent, :] = np.mean(currNewCenter, axis=0) 95 # 返回 【 当前三个中心点的坐标】 【每个点的类别号,和到当前中心点的最小距离】 96 return centerids, clusterAssment 97 98 if __name__ == '__main__': 99 # numpy.mat 将数据转换成80*2的矩阵 100 dataMat = np.mat(loadDataSet('./testSet.txt')) 101 k = 3 102 # centerids 三个中心点的坐标。clusterAssment 每个点的类别号|到当前中心点的最小距离 103 centerids, clusterAssment = kMeans(dataMat, k, distMeas=distEclud, createCent=randCent) 104 print(centerids) 105 print(clusterAssment)
运行结果:
x= [[-5.19811282e+00 6.41869316e-01] [-5.75229538e+00 4.18627111e-01] [-1.08448984e+01 -7.55352273e+00] ... [ 1.36105255e+00 -9.07491863e-01] [-3.54141108e-01 7.12241630e-01] [ 1.88577252e+00 1.41185693e-03]] <class 'numpy.ndarray'> y= [1 1 0 ... 2 2 2] <class 'numpy.ndarray'> y_pred : [2 2 1 ... 0 0 0] dataArray type = <class 'numpy.ndarray'>
图形绘制效果: