机器学习实战---读书笔记: 第10章 利用K均值聚类算法对未标注数据分组---1

#!/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

猜你喜欢

转载自blog.csdn.net/qingyuanluofeng/article/details/85040778