#!/usr/bin/env python
# encoding: utf-8
import os
from matplotlib import pyplot as plt
from numpy import *
'''
读书笔记之--<<机器学习实战>>--第10章_利用K均值聚类算法对未标注数据分组
关键:
1 聚类:
含义:无监督学习,将相思对象归到同一个簇中
特点: 簇内的对象越相似,聚类效果越好
K-均值聚类: 发现k个不同的簇,且每个簇的中心采用簇中所含的均值计算而成。
簇识别: 给出聚类结果的含义,告诉我们这些簇是什么
聚类与分类的不同: 分类的目标事先已知,聚类的的结果与分类相同,只是类别没有预先定义
聚类=无监督分类
相似: 取决于所选择的相似度计算方法
2 K-均值聚类算法
优点:容易实现
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢
使用数据类型: 数值型数据
含义: 发现给定数据集k个簇的算法,簇个数k是用户给定的。
每个簇通过其质心(簇的中心)来描述
逻辑流程:
步骤1: 任选k个点作为质心
步骤2: 当任意一个点的簇分配结果发生改变时
2.1对每个数据点
2.1.1对每个质心,计算点到质心的距离
2.1.2 将数据点分配到距离最近的簇
2.2对每个簇,计算簇中所有点的均值并将均值作为质心
测试算法:应用聚类算法,观察结果。可使用量化的误差指标如误差平方和来评价算法的结果
使用算法: 簇质心可代表整个簇的数据来做出决策。
3 numpy常用方法
3.1 numpy.shape(arr)
作用:读取矩阵的长度
输入参数: 矩阵
解释:
当输入的是一维数组时,返回的是数组的长度
numpy.shape([1]) 的结果是(1,)
numpy.shape([1,2,3])的结果是(3,) 因为一维数组[1,2,3]的长度是3
当输入的是二维数组时,返回的是二维数组的(行数,列数)
numpy.shape([ [1,1], [2,2], [3,3] ]) 因为是一个二行三列的数组,所以返回结果是
(2,3)
3.2 numpy.zeros(shape, dtype=float)
作用: 创建0矩阵
输入参数:
shape:矩阵形状,是元组,例如(2,3)表示会创建一个2行3列的矩阵
3.3 numpy.mat(data, dtype=None)
作用:将输入参数转换为矩阵
输入参数:
data:类似数组的输入数据
返回:返回矩阵
3.4 numpy.min(axis=None, out=None, keepdims=False)
作用:返回数组的最小值
参数:
axis:如果为None,就返回所有中最小值
如果为0,表示第0个维度,即行,就会导致行消失,这里的意思实际是:
axis=0(行)这条轴取max,得到的结果就是把输入数组的'行'给消除了
如果为1,表示沿着axis=1(列)这条轴取max,得到的结果就是把输入数组的'列'给消除了
3.5 numpy.random.rand(d1, d1,.., dn)
作用: 按照给定的形状生成0~1之间值最后对应的数组
例如: numpy.random.rand(3, 2)
注意不是元组
3.6 numpy.nonzero(arr)
作用:返回数组中非零元素的下标
输入参数:
arr:类似数组的输入
解释:
如果arr是二维数组,那么返回的结果包含
它的第0个元素是数组a中值不为0的元素的第0轴的下标,第1个元素则是第1轴的下标
例如:
>>> b2 = np.array([[True, False, True], [True, False, False]])
>>> np.nonzero(b2)
(array([0, 0, 1]), array([0, 2, 0]))
4
ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A == cent)[0]]
要表达的意思是:把clusterAssment[:,0].A中与cent(在代码中表示簇)一样的元素索引提取出来,又有nonzero()返回值是一个tuple,nonzero()[0]表示的是保证表达式为真值的元素位置的行坐标,然后利用这个行坐标提取出dataset对应的元素。
python中一个matrix矩阵名.A 代表将 矩阵转化为array数组类型
5 总结
kMeans聚类算法:
只要簇分配结果变化:
遍历所有点,对每个点,遍历所有质心,计算点到每个质心的距离,从而找出距离最近的质心,更新该
点的簇分配结果
遍历所有质心,从簇分配结果中找到属于该质心的所有样本点的编号,从数据集中提取出所有属于这个质心的样本点,
计算属于质心的所有样本点各列的平均值作为新的质心。
[1] https://blog.csdn.net/qq_28618765/article/details/78081959
[2] https://blog.csdn.net/DocStorm/article/details/58599124
[3] https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.mat.html
[4] https://blog.csdn.net/Build_Tiger/article/details/79848808
[5] https://blog.csdn.net/yaoqi_isee/article/details/77714570
[6] https://www.cnblogs.com/1zhk/articles/4782812.html
[7] https://blog.csdn.net/ruibin_cao/article/details/83242489
[8] https://blog.csdn.net/andyleo0111/article/details/78918285
[9] 机器学习实战
'''
'''
作用: 加载数据集
'''
def loadDataSet(fileName):
dataMat = []
if not fileName or not os.path.exists(fileName):
return dataMat
with open(fileName, "r") as fr:
for line in fr.readlines():
if not line:
continue
tempData = line.strip().split('\t')
data = map(float, tempData)
dataMat.append(data)
return dataMat
def distEclud(vecA, vecB):
return sqrt(sum(power(vecA - vecB, 2)))
'''
作用: 随机选择k个质心
处理过程:
步骤1: 获取数据集的列数
步骤2: 构造(k,列数)的零矩阵
步骤3: 遍历每一列,
3.1 获取数据集在该列的最小值和最大值,
3.2 计算最小值和最大值之间的差值
3.3 令零矩阵的在该列的值为该最小值和最大值之间的值(随机生成)
最终就得到了一个:
k个质心,每个质心拥有和数据集一样的列数,并且每个质心在任一列的大小都在
数据集对应列的最小值和最大值之间,这样就是符合要求的质心
解释:
把dataSet看做矩阵,那dataSet[:,j]是指第j列,min(dataSet[:,j])是指第j列最小值
'''
def randCent(dataSet, k):
columnNum = shape(dataSet)[1]
cores = mat(zeros((k, columnNum)))
for j in range(columnNum):
minValue = min(dataSet[:, j])
maxValue = max(dataSet[:, j])
print "In column {column}, min value: {minValue}, max value: {maxValue} ".format(
column=j, minValue=minValue, maxValue=maxValue
)
diff = float(maxValue - minValue)
cores[:, j] = minValue + diff * random.rand(k, 1)
return cores
'''
kmeans算法主要思想:
将每个点分配到最近的质心,重新计算质心,直到数据点的簇分配结果不再改变
处理过程:
步骤1: 获取数据集的样本个数m,属性个数n,创建簇分配结果的m行2列的零矩阵
该簇分配结果矩阵如下:
[
[第1个样本点的到距离最短的质心编号, 第1个样本点到距离最短质心的距离]
[第2个样本点的到距离最短的质心编号, 第2个样本点到距离最短质心的距离]
......
[第n个样本点的到距离最短的质心编号, 第n个样本点到距离最短质心的距离]
]
步骤2: 获取质心矩阵,即k行n列组成的矩阵
步骤3: 只要簇分配结果不改变,就进行如下处理:
3.1 先设置簇分配结果默认没有修改
3.2 遍历m个样本点,对每个样本点
3.2.1 遍历k个质心,对每个质心,计算样本点距离质心的距离,
1)如果距离比最短距离小,就更新最短距离,并更新该样本点的簇分配结果为当前质心
3.2.2 如果簇分配结果中当前样本距离的最短质心的编号不等于 计算出来的最短质心编号,说明该样本点簇分配结果发生变化,
则设置簇分配结果发生改变;
3.2.3 设置当前样本点的簇分配结果为当前最短的质心编号,以及最短距离
3.3 遍历每个质心,
3.3.1 找到簇分配结果(n行,2列,每一行第1个元素是质心编号,第二列是距离)中第一列的值和当前质心编号
相同的值对应的样本行号列表,用该样本行号列表到数据集中找到属于这个质心的所有样本点,
然后计算这些样本点的在行维度(消除行,即取所有样本点各列的平均值)的平均值,作为该质心新的值
总结:
只要簇分配结果变化:
遍历所有点,对每个点,遍历所有质心,计算点到每个质心的距离,从而找出距离最近的质心,更新该
点的簇分配结果
遍历所有质心,从簇分配结果中找到属于该质心的所有样本点的编号,从数据集中提取出所有属于这个质心的样本点,
计算属于质心的所有样本点各列的平均值作为新的质心。
'''
def kMeans(dataSet, k, distMeasure=distEclud, createCent=randCent):
rowNum, columnNum = shape(dataSet)
clusterResult = mat(zeros((rowNum, 2)))
cores = createCent(dataSet, k)
isResultChanged = True
while isResultChanged:
isResultChanged = False
# 计算每个点到每个质心的距离,更新簇分配结果
for i in range(rowNum):
minDist = inf
minIndex = -1
for j in range(k):
dist = distMeasure(dataSet[i, :], cores[j, :])
if dist < minDist:
minDist = dist
minIndex = j
if clusterResult[i, 0] != minIndex:
isResultChanged = True
clusterResult[i, :] = minIndex, minDist ** 2
# 更新质心
for centIndex in range(k):
filterResult = clusterResult[:, 0].A==centIndex
indexes = nonzero(filterResult)
nums = indexes[0]
pointsInCluster = dataSet[ nums ]
result = mean(pointsInCluster, axis=0)
cores[centIndex, :] = result
return clusterResult, cores
def showResult(clusterResult, dataSet):
#画出密度聚类后的结果
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
num = len(mark)
for i, points in enumerate(clusterResult):
arr = points.A
label = int(arr[0][0])
dataPoint = dataSet[i].A
x = dataPoint[0][0]
y = dataPoint[0][1]
color = mark[label % num]
plt.plot(x, y, color)
plt.show()
def process():
dataMat = mat(loadDataSet('testSet.txt'))
# cores = randCent(dataMat, 2)
# print cores
clusterResult, cores = kMeans(dataMat, 4)
print "簇分配结果: {clusterResult}".format(clusterResult=clusterResult)
print "质心结果: {cores}".format(cores=cores)
showResult(clusterResult, dataMat)
if __name__ == "__main__":
process()
其中数据集文件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