kmeans总结

2023-11-04


1.关于分类和聚类

kmeans属于聚类算法中的一种。分类和聚类是不同的概念。虽然两者的目的都是对数据进行分类,但是却有一定的区别。

  • 分类是按照某种标准给对象贴标签,再根据标签来区分归类;
  • 聚类是事先没有给出标签,刚开始并不知道如何对数据分类,完全是算法自己来判断各条数据之间的相似性,相似的就放在一起。

在聚类的结论出来之前,不能知道每一类有什么特点,最后一定要根据聚类的结果通过人的经验来分析才能知道聚成的这一类大概有什么特点。简言之,聚类就是“物以类聚、人以群分”的原理。

2.基本概念

简单来说,kmeans算法的原理就是:给定K的值,K代表要将数据分成的类别数,然后根据数据间的相似度将数据分成K个类,也称为K个簇(cluster)。这就是kmeans算法名字中k的由来。

度量数据相似度的方法一般是用数据点间的距离来衡量,比如欧式距离、汉明距离、曼哈顿距离等等。

一般来说,我们使用欧式距离来度量数据间的相似性。所谓的欧式距离便是我们日常使用的距离度量方法。比如对于二维平面上的两个点A(x1,y1)和B(x2,y2),两者间的欧式距离就为二维平面两点间欧式距离公式

而对于每一个簇,我们用簇中所有点的中心来描述,该中心也称为质心(centroid)。我们通过对簇中的所有数据点取均值(mean)的方法来计算质心,这也是kmeans算法名字中mean的由来。

3.算法描述

3.1.自然语言描述

  • 1.创建K个点作为初始质心(通常是随机选择)

  • 2.当任意一个点的簇分类结果发生改变时

    • 2.1对数据的每一个点,计算每一个质心与该数据点的距离,将数据点分配到距其最近的簇

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

3.2.伪代码描述

kmeans(dataset, k)

输入:数据集dataset, k的值(大于0的整数)

输出:包含质心的集合,簇分配结果

(1) 选择k个数据点作为质心(通常是随机选取)

(2) 当任意一个点的簇分类结果发生改变时

a)  For each 数据集中的点

        For each 质心集合的质心

            计算数据点与质心的距离

        将数据点分配到距其最近的簇,更新簇分配结果

b)  For each簇

        计算簇中所有点的均值并将均值作为质心

4.算法实现

4.1.python实现及相关解释

注:以下代码参考自《机器学习实战》

#-*-coding:utf-8-*-
from numpy import *

def distEclud(vecA, vecB):
    '''
    输入:向量A,向量B
    输出:两个向量的欧式距离
    描述:计算两个向量的欧式距离
    '''      
    return sqrt(sum(power(vecA - vecB, 2)))     

def randCent(dataSet, k):
    '''
    输入:数据集、K
    输出:包含K个随机质心(centroid)的集合
    描述:为给定数据集生成一个包含K个随机质心的集合
    '''       
    n = shape(dataSet)[1]          #得到数据集的列数
    centroids = mat(zeros((k,n)))  #得到一个K*N的空矩阵
    for j in range(n):             #对于每一列(对于每一个数据维度)
        minJ = min(dataSet[:,j])   #得到最小值
        rangeJ = float(max(dataSet[:,j]) - minJ) #得到当前列的范围
        centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1)) #在最小值和最大值之间取值
                                   #random模块的rand(a,b)函数返回a行b列的随机数(范围:0-1)
                                   #在这里mat()加不加都无所谓,但是为了避免赋值右边的变量类
                                   #型不是matrix,还是加上比较好
    return centroids

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    '''
    输入:数据集,k,计算向量间距离的函数名,随机生成k个随机质心的函数名
    输出:包含质心的集合,簇分配结果矩阵
    描述:kmeans算法实现
    '''   
    m = shape(dataSet)[0]             #数据集的行数,即数据的个数
    clusterAssment = mat(zeros((m,2)))#簇分配结果矩阵
                                      #第一列储存簇索引值
                                      #第二列储存数据与对应质心的误差
    centroids = createCent(dataSet, k)#先随机生成k个随机质心的集合
    clusterChanged = True
    while clusterChanged:             #当任意一个点的簇分配结果改变时
        clusterChanged = False
        for i in range(m):            #对数据集中的每一个数据
            minDist = inf; minIndex = -1
            for j in range(k):        #对于每一质心
                distJI = distMeas(centroids[j,:],dataSet[i,:])#得到数据与质心间的距离
                if distJI < minDist:  #更新最小值
                    minDist = distJI; minIndex = j
            #若该点的簇分配结果改变
            if clusterAssment[i,0] != minIndex: clusterChanged = True
            clusterAssment[i,:] = minIndex,minDist**2
        #print centroids
        for cent in range(k):         #对于每一个簇
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#通过数组过滤得到簇中所有数据
                                                                         #.A 方法将matrix类型元素转化为array类型
                                                                         #在这里也可以不加
            centroids[cent,:] = mean(ptsInClust, axis=0) #将质心更新为簇中所有数据的均值
                                                         #axis=0表示沿矩阵的列方向计算均值
    return centroids, clusterAssment

以上便是kmeans的python实现。可以看出,代码写得是相当的简洁朴实,有许多地方是值得思考和借鉴的。

比如对于上面的randCent函数的实现,该函数的作用是:为给定数据集生成一个包含K个随机质心的集合。

那么具体要怎么实现该函数呢?对于一般的数据集,我们一般是以每一行代表一个数据样本,每一列代表数据维度(或数据属性)。K个随机质心的集合就需要K*N(N为数据维度数目,也是数据集的列数)的矩阵来存储。那么要如何要随机选取呢?

一种比较简单粗暴的方法就是:对于每个质心的N个维度的值依次进行随机选取,这样需要两个for循环来实现了,当然选取的值要保证在数据集的范围之内(若取在数据集范围之外,比如取在一个距离数据集所有样本都很远的点,那计算欧式距离还有什么意义?)。实现如下:

def randCent(dataSet, k):
    '''
    输入:数据集、K
    输出:包含K个随机质心(centroid)的集合
    描述:为给定数据集生成一个包含K个随机质心的集合
    '''       
    n = shape(dataSet)[1]          #得到数据集的列数
    m = shape(dataSet)[0]          #得到数据集的行数
    centroids = mat(zeros((k,n)))  #得到一个K*N的空矩阵
    for i in range(k):
        for j in range(n):             #对于每一列(对于每一个数据维度)
            minJ = min(dataSet[:,j])   #得到当前列最小值
            rangeJ = float(max(dataSet[:,j]) - minJ) #得到当前列的范围
            centroids[i,j] = minJ + rangeJ * random.rand(1) #在最小值和最大值之间取值
                                   #random模块的rand(a,b)函数返回a行b列的随机数(范围:0-1)
    return centroids

而借用numpy矩阵的方便性,我们可以节省掉一个for循环:

def randCent(dataSet, k):
    '''
    输入:数据集、K
    输出:包含K个随机质心(centroid)的集合
    描述:为给定数据集生成一个包含K个随机质心的集合
    '''       
    n = shape(dataSet)[1]          #得到数据集的列数
    centroids = mat(zeros((k,n)))  #得到一个K*N的空矩阵
    for j in range(n):             #对于每一列(对于每一个数据维度)
        minJ = min(dataSet[:,j])   #得到最小值
        rangeJ = float(max(dataSet[:,j]) - minJ) #得到当前列的范围
        centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1)) #在最小值和最大值之间取值
                                   #random模块的rand(a,b)函数返回a行b列的随机数(范围:0-1)
    return centroids

其次,在kMeans函数中,下面的这行代码可能有点难以理解,其所在的for循环的作用是:对于每一个簇,将质心更新为簇中所有数据的均值。该行代码的作用是:通过数组过滤得到下标为cent的簇中的所有数据。

ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]    

我们可以从外到内来看这行代码。首先,最外层是:ptsInClust =dataSet[something],那么我们可以反推最外层的[]符号里面的代码所起的作用便是:找到被划分在下标为cent的簇的那些数据点(样本)的下标,更为准确的,应该是这些数据点在dataSet中的行下标。那么,我们来看这行代码:

nonzero(clusterAssment[:,0].A==cent)[0]

注:代码中的.A是将matrix类型的变量转换为array变量的方法,在这里有没有都没有影响。

对于nonzero函数,其输入值为:数组或矩阵,返回输入值中非零元素的信息,这些信息中包括 两个array, 包含了相应维度上非零元素所在的行标号,与列标标号。下面举两个例子:

>>> a=mat([ [1,0,0],[0,0,0],[0,0,0]])
>>> nonzero(a)
(array([0], dtype=int64), array([0], dtype=int64))

输出结果表示:输入矩阵a只有1个非零值。 第一个array([0], dtype=int64)表示非零元素在第0行, 第二个array([0], dtype=int64)表示在第0行的第0列。(dtype代表数据类型)

>>> a=mat([[1,0,0],[1,0,0],[0,0,1]])
>>> nonzero(a)
(array([0, 1, 2], dtype=int64), array([0, 0, 2], dtype=int64))

输出结果表示:矩阵a只有3个非零值。 返回值行维度数据array([[0, 1, 2]], dtype=int64)表示非零元素出现在a中的第0行、第1行和第2行;返回值列维度数据array([[0, 0, 2]], dtype=int64)对应表示每行中第几列,即第0行第0列,第1行第0例,第2行第2列。

那么,nonzero(something)[0]的作用就是:找出something中的非零值的行维度下标组成的array。而clusterAssment[:,0].A==cent就是判断clusterAssment[:,0]中元素是否与cent相等的bool表达式,相等时对应位置为True,反之为False。举个例子:

>>> a
array([3, 0, 1, 4, 5, 1])
>>> a==1
array([False, False,  True, False, False,  True], dtype=bool)

至此,问题解决!

4.1.1对应应用(使用kmeans对地图上的点进行聚类)

注:该例参考自《机器学习实战》

一、背景介绍

假设现在给出俄勒冈洲的波特兰地区附件的70个地点的名字和对应所在的经纬度,具体分布如下图所示。现在要求给出将这些地方进行聚类的最佳策略,从而可以安排交通工具到达这些簇的质心,然后步行到每个簇内的地点。
这里写图片描述

二、使用数据说明

1.70个地点的数据存放在places.txt文件中,部分截图如下。其中每一行数据的第一列代表地点的名字,第4列和第5列分别代表对应地点的纬度、经度。
这里写图片描述

2.未标注地点的俄勒冈洲的波特兰地区的图片如下,命名为Portland.png:
这里写图片描述

三、代码及相关解释

代码如下:

 #-*-coding:utf-8-*-
from numpy import *

def distEclud(vecA, vecB):
    '''
    输入:向量A,向量B
    输出:两个向量的欧式距离
    描述:计算两个向量的欧式距离
    '''      
    return sqrt(sum(power(vecA - vecB, 2)))     

def randCent(dataSet, k):
    '''
    输入:数据集、K
    输出:包含K个随机质心(centroid)的集合
    描述:为给定数据集生成一个包含K个随机质心的集合
    '''       
    n = shape(dataSet)[1]          #得到数据集的列数
    centroids = mat(zeros((k,n)))  #得到一个K*N的空矩阵
    for j in range(n):             #对于每一列(对于每一个数据维度)
        minJ = min(dataSet[:,j])   #得到最小值
        rangeJ = float(max(dataSet[:,j]) - minJ) #得到当前列的范围
        centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1)) #在最小值和最大值之间取值
                                   #random模块的rand(a,b)函数返回a行b列的随机数(范围:0-1)
                                   #在这里mat()加不加都无所谓,但是为了避免赋值右边的变量类
                                   #型不是matrix,还是加上比较好
    return centroids

'''randCent函数的粗暴实现
 def randCent(dataSet, k):

    输入:数据集、K
    输出:包含K个随机质心(centroid)的集合
    描述:为给定数据集生成一个包含K个随机质心的集合

    n = shape(dataSet)[1]          #得到数据集的列数
    m = shape(dataSet)[0]          #得到数据集的行数
    centroids = mat(zeros((k,n)))  #得到一个K*N的空矩阵
    for i in range(k):
        for j in range(n):             #对于每一列(对于每一个数据维度)
            minJ = min(dataSet[:,j])   #得到当前列最小值
            rangeJ = float(max(dataSet[:,j]) - minJ) #得到当前列的范围
            centroids[i,j] = minJ + rangeJ * random.rand(1) #在最小值和最大值之间取值
                                   #random模块的rand(a,b)函数返回a行b列的随机数(范围:0-1)
    return centroids

 '''
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    '''
    输入:数据集,k,计算向量间距离的函数名,随机生成k个随机质心的函数名
    输出:包含质心的集合,簇分配结果矩阵
    描述:kmeans算法实现
    '''   
    m = shape(dataSet)[0]             #数据集的行数,即数据的个数
    clusterAssment = mat(zeros((m,2)))#簇分配结果矩阵
                                      #第一列储存簇索引值
                                      #第二列储存数据与对应质心的误差
    centroids = createCent(dataSet, k)#先随机生成k个随机质心的集合
    clusterChanged = True
    while clusterChanged:             #当任意一个点的簇分配结果改变时
        clusterChanged = False
        for i in range(m):            #对数据集中的每一个数据
            minDist = inf; minIndex = -1
            for j in range(k):        #对于每一质心
                distJI = distMeas(centroids[j,:],dataSet[i,:])#得到数据与质心间的距离
                if distJI < minDist:  #更新最小值
                    minDist = distJI; minIndex = j
            #若该点的簇分配结果改变
            if clusterAssment[i,0] != minIndex: clusterChanged = True
            clusterAssment[i,:] = minIndex,minDist**2
        #print centroids
        for cent in range(k):         #对于每一个簇
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#通过数组过滤得到簇中所有数据
                                                                         #.A 方法将matrix类型元素转化为array类型
                                                                         #在这里也可以不加
            centroids[cent,:] = mean(ptsInClust, axis=0) #将质心更新为簇中所有数据的均值
                                                         #axis=0表示沿矩阵的列方向计算均值
    return centroids, clusterAssment


def loadDataSet(fileName):
    '''
    输入:文件名
    输出:包含数据的列表
    描述:从以tab键分隔的txt文件中提取数据
    '''      
    dataMat = []               
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = map(float,curLine)  #将所有数据转换为float类型
        dataMat.append(fltLine)
    return dataMat


def distSLC(vecA, vecB):
    '''
    输入:两点的经纬度
    输出:两点在地球表面的距离(单位为 英里)
    描述:使用球面余弦定理计算地球表面两点间的距离
    '''   
    #pi为圆周率,在导入numpy时就会导入的了
    #sin(),cos()函数输出的是弧度为单位的数据
    #由于输入的经纬度是以角度为单位的,故要将其除以180再乘以pi转换为弧度
    #设所求点A ,纬度β1 ,经度α1 ;点B ,纬度β2 ,经度α2。则距离
    #距离 S=R·arc cos[cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2]
    a = sin(vecA[0,1]*pi/180) * sin(vecB[0,1]*pi/180)
    b = cos(vecA[0,1]*pi/180) * cos(vecB[0,1]*pi/180) * \
                      cos(pi * (vecB[0,0]-vecA[0,0]) /180)
    return arccos(a + b)*6371.0 #6371.0为地球半径

import matplotlib
import matplotlib.pyplot as plt
def clusterPlaces(numClust=5):
    '''
    输入:希望得到的簇数目
    输出:绘制的图像
    描述:对数据进行聚类并绘制相关图像
    '''   
    datList = []
    for line in open('places.txt').readlines():               #读取文件的每一行
        lineArr = line.split('\t')
        datList.append([float(lineArr[4]), float(lineArr[3])])#保存经纬度到列表中
    datMat = mat(datList)
                                                              #进行聚类
    myCentroids, clustAssing = kMeans(datMat, numClust, distMeas=distSLC)
    figure_name=str(numClust)+'-cluster'
    fig = plt.figure(figure_name)                                        #创建一幅图
    rect=[0.1,0.1,0.8,0.8]                                    #创建一个矩形
    scatterMarkers=['s', 'o', '^', '8', 'p', \
                    'd', 'v', 'h', '>', '<']                  #用来标识簇的标记
    axprops = dict(xticks=[], yticks=[])
    ax0=fig.add_axes(rect, label='ax0', **axprops)
    imgP = plt.imread('Portland.png')                         #基于图像创建矩阵
    ax0.imshow(imgP)                                          #绘制图像矩阵
    ax1=fig.add_axes(rect, label='ax1', frameon=False)
    for i in range(numClust):                                 #在图像上绘制每一簇
        ptsInCurrCluster = datMat[nonzero(clustAssing[:,0].A==i)[0],:]
        markerStyle = scatterMarkers[i % len(scatterMarkers)]
        ax1.scatter(ptsInCurrCluster[:,0].flatten().A[0], ptsInCurrCluster[:,1].flatten().A[0], marker=markerStyle, s=90)
    ax1.scatter(myCentroids[:,0].flatten().A[0], myCentroids[:,1].flatten().A[0], marker='+', s=300)
    #print(myCentroids[:,0].flatten().A[0])
    #print(myCentroids[:,1].flatten().A[0])
    plt.show()

if __name__ == '__main__':
    for i in range(3,7):
        clusterPlaces(i)

该份代码在上面kmeans的python实现代码的基础之上,添加了如下函数:

函数名 作用
loadDataSet(fileName) 从以tab键分隔的txt文件中提取数据
distSLC(vecA, vecB) (由于给出的地点是位于地球上(球体))使用球面余弦定理计算地球表面两点间的距离,代替原来用于计算两个向量的欧式距离函数distEclud(vecA, vecB)
clusterPlaces(numClust=5) 对数据进行聚类并绘制相关图像 (可视化结果)
运行结果

不同簇的数据点用不同的形状标记,+号所标注的就是对应簇的质心。K=3、4、5、6时的输出图像依次为:
k=3
k=4
k=5
k=6
仔细观察发现,聚类的效果还算可以,不同区域块的地点都大致各自被划分在一起了。但是,细心的朋友可能会发现:上面的图“3-cluster”本应显示3个“+”号(3个不同形状的“小部落”),但是缺只显示了2个。图“5-cluster”、图“6-cluster”也有出现类似的情况。随后,笔者再次运行上面的代码,由于算法中的初始质心是随机选取的缘故,每次的结果的不一样,将每次聚类的结果所得的质心都输出来:

#k=3时质心的经度
[-122.74941346 -122.54432282 -122.66436586]
#k=3时质心的纬度
[ 45.545862    45.52042118  45.48861296]

#k=4时质心的经度
[-122.7718838  -122.52181039 -122.69274678 -122.63248475]
#k=4时质心的纬度
[ 45.5345341   45.50142783  45.43529156  45.5331405 ]

#k=5时质心的经度
[-122.72072414 -122.62813707 -122.4009285  -122.7288018  -122.53692062]
#k=5时质心的纬度
[ 45.59011757  45.52791831  45.46897     45.45886713  45.50548506]

D:\Program Files\Anaconda3\lib\site-packages\numpy\core\_methods.py:59: RuntimeWarning: Mean of empty slice.
  warnings.warn("Mean of empty slice.", RuntimeWarning)
D:\Program Files\Anaconda3\lib\site-packages\numpy\core\_methods.py:68: RuntimeWarning: invalid value encountered in true_divide
  ret, rcount, out=ret, casting='unsafe', subok=False)

#k=6时质心的经度
[-122.59320258 -122.52181039 -122.7003585  -122.842918             nan
 -122.68842409]
 #k=6时质心的纬度
[ 45.55155133  45.50142783  45.58066533  45.646831            nan
  45.48668819]

[注]:为了方便理解数据,上面的注释是人工添加上的
可以看到,在这次运行中,当K=3、4、5时,运行结果正常,质心的坐标数值没出现如K=6时的nan值。当K=6时,运行报错,第五个质心的经纬度的计算出错 (详见上面的报错信息)。

所以,由于初始随机质心选取的缘故,在使用上面的代码时,可通过多次测试来找到没有报错的情况。

说到测试,我这里再运行一次代码,运行后质心的结果如下,注释我就不打了,参考上面的就行了。

[-122.7255474  -122.61159983 -122.50706986]
[ 45.51717564  45.5133606   45.50135379]
[-122.55327667 -122.4009285  -122.7288018  -122.66332621]
[ 45.515949    45.46897     45.45886713  45.54090854]
[-122.761804   -122.49685582 -122.62341067 -122.5784446  -122.68897476]
[ 45.46639582  45.49891482  45.4787138   45.5447184   45.55172129]
[-122.76690133 -122.78288433 -122.65587515 -122.52181039 -122.60946453
 -122.70508429]
[ 45.612314    45.4942305   45.5080271   45.50142783  45.5579654
  45.42730186]

这次测试便没有报错,所以还是可能找到没有报错的情况的,可放心使用这份代码。

4.2.基于python-sklearn的应用

scikit-learn的kmeans函数参数的解释,看这里
以下代码参考自:K-means Clustering,这份代码所做的事情是:将sklearn的kmeans函数应用于iris数据集上。关于该数据集,可以参考如下图片:
这里写图片描述
由于数据集中数据有花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性,为了演示数据在3维空间的聚类效果,代码中只选择分析其中3个属性。

#-*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from sklearn.cluster import KMeans
from sklearn import datasets

np.random.seed(5)

centers = [[1, 1], [-1, -1], [1, -1]]

iris = datasets.load_iris()#导入iris数据集
X = iris.data   #得到数据
y = iris.target #得到数据的分类标签(整数0、1、2)
'''参考:
>>>y
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]
'''
#得到分类器
estimators = {'k_means_iris_3': KMeans(n_clusters=3),
              'k_means_iris_8': KMeans(n_clusters=8),
              'k_means_iris_bad_init': KMeans(n_clusters=3, n_init=1,
                                              init='random')}

for name, est in estimators.items():
    fig = plt.figure(name, figsize=(6, 5)) #figsize指定图像的纵向高度和横向宽度
    #plt.clf()      #清空当前图像操作,此处可以不加
    ax = Axes3D(fig)#返回3D图形对象
    #plt.cla()      #清空当前坐标操作,此处可以不加
    est.fit(X)      #用数据对算法进行拟合操作
    labels = est.labels_#得到每一数据点的分类结果
                    #绘制散点图
    ax.scatter(X[:, 3], X[:, 0], X[:, 2], c=labels)
                    #scatter是绘制散点图的函数,前面3个参数对应数据在x,y,z轴的坐标,c代表
                    #更多可参考:http://blog.csdn.net/u013634684/article/details/49646311


    #设置x,y,z轴的刻度标签,[]代表不描绘刻度
    ax.w_xaxis.set_ticklabels([])
    ax.w_yaxis.set_ticklabels([])
    ax.w_zaxis.set_ticklabels([])
    #设置x,y,z轴的标签
    ax.set_xlabel('Petal width')
    ax.set_ylabel('Sepal length')
    ax.set_zlabel('Petal length')

# Plot the ground truth
fig = plt.figure('real_iris', figsize=(6, 5))
#plt.clf()
ax = Axes3D(fig)
#plt.cla()

for name, label in [('Setosa', 0),
                    ('Versicolour', 1),
                    ('Virginica', 2)]:
    #在数据集中心绘制 分类标签的名字
    ax.text3D(X[y == label, 3].mean(),
              X[y == label, 0].mean() + 1.5,
              X[y == label, 2].mean(), name,
              horizontalalignment='center',#center代表text向中间水平对齐
              bbox=dict(alpha=.5, edgecolor='w', facecolor='w'))#bbox用于设置ext背景框
                       #alpha为透明度,edgecolor为边框颜色(w为white之意),facecolor为背景框内部颜色
#绘制散点图
ax.scatter(X[:, 3], X[:, 0], X[:, 2], c=y)

#设置x,y,z轴的刻度标签,[]代表不描绘刻度
ax.w_xaxis.set_ticklabels([])
ax.w_yaxis.set_ticklabels([])
ax.w_zaxis.set_ticklabels([])
#设置x,y,z轴的标签
ax.set_xlabel('Petal width')
ax.set_ylabel('Sepal length')
ax.set_zlabel('Petal length')
#显示图像,没有这句图像就显示不出来
plt.show()

4.3.matlab实现及相关解释

关于kmeans的matlab实现,网上已经有很多人给出了。本文的kmeans的matlab实现为上述的python实现的matlab版本。

distEclud函数为:

function dist = distEclud(vecA, vecB)
    dist = norm(vecA-vecB,2);

randCent函数为:

function centroids = randCent(dataSet, k)
    [~, col] = size(dataSet);
    centroids = zeros(k,col);
    for j = 1:col
        minJ = min(dataSet(:,j));
        rangeJ = double(max(dataSet(:,j))-minJ);%使用float()会出错
        centroids(:,j) = minJ + rangeJ*rand(k,1);
    end

kmeans函数(命名为mykmeans)为:

function [centroids,clusterAssment]=mykmeans(dataSet,k)
    if (nargin==2)
        distMeas=@distEclud;
        createCent=@randCent;
    elseif (nargin==3)
        createCent=@randCent;
    end
    [m,~] = size(dataSet);
    clusterAssment = zeros(m,2);
    centroids = createCent(dataSet,k);
    clusterChanged = true;
    while clusterChanged
        clusterChanged = false;
        for i = 1:m
            minDist = inf; minIndex = -1;
            for j = 1:k
                distJI = distMeas(centroids(j,:),dataSet(i,:));
                if distJI < minDist
                    minDist = distJI; minIndex = j;
                end
            end
            if clusterAssment(i,1) ~= minIndex %使用clusterAssment(i,0)会出错,matlab中下标从1开始
                clusterChanged = true;
            end
            clusterAssment(i,:) = [minIndex,minDist.*minDist];
        end
        for cent = 1:k
            ptsInClust = dataSet(clusterAssment(:,1)==cent,:);
            centroids(cent,:) = mean(ptsInClust, 1);
        end
    end

4.3.1对应应用

参考这篇文章的测试代码,上述matlab程序的测试代码如下:

“`matlab
close all;
clc;
%第一类数据
mu1=[0 0 0]; %均值
S1=[0.3 0 0;0 0.35 0;0 0 0.3]; %协方差
data1=mvnrnd(mu1,S1,100); %产生高斯分布数据

%%第二类数据
mu2=[1.25 1.25 1.25];
S2=[0.3 0 0;0 0.35 0;0 0 0.3];
data2=mvnrnd(mu2,S2,100);

%第三个类数据
mu3=[-1.25 1.25 -1.25];
S3=[0.3 0 0;0 0.35 0;0 0 0.3];
data3=mvnrnd(mu3,S3,100);

%显示数据
plot3(data1(:,1),data1(:,2),data1(:,3),’+’);
hold on;
plot3(data2(:,1),data2(:,2),data2(:,3),’r+’);
plot3(data3(:,1),data3(:,2),data3(:,3),’g+’);
grid on;

%三类数据合成一个不带标号的数据类
data=[data1;data2;data3]; %这里的data是不带标号的
%k-means聚类
[centroids, clusterAssment]=mykmeans(data,3); %最后产生带标号的数据,标号在所有数据的最后,意思就是数据再加一维度

[m, ~]=size(clusterAssment);

%最后显示聚类后的数据
figure;
hold on;
%绘制 质心的位置
plot3(centroids(:,1),centroids(:,2),centroids(:,3),’kd’,’MarkerSize’,14);
for i=1:m
if clusterAssment(i,1)==1
plot3(data(i,1),data(i,2),data(i,3),’ro’);
elseif clusterAssment(i,1)==2
plot3(data(i,1),data(i,2),data(i,3),’go’);
else
plot3(data(i,1),data(i,2),data(i,3),’bo’);
end
end
grid on;

“`matlab
输出结果:
这里写图片描述
左边为原数据的分布(由不同颜色区分),右图是使用kmeans划分得到的数据分布(中间的黑色菱形代表簇的质心)。

右边那张图实际上是三维的,实际上我们可以通过点击图像上方的“Rotate 3D”按钮将图像旋转至3D视图这里写图片描述

效果如下:
这里写图片描述

可以看到,聚类的效果是相当不错的。

4.4基于SPSS MODELER的应用

软件版本:14.1(破解版)

这里以“Demos”文件夹的“DRUGIn”文件为例,使用modeler自带的kmeans算法进行分析。

DRUGIn”文件的导入:

  1. 点击软件界面下方的“ 源”
  2. 点击可变文件
  3. 选择DRUGIn文件(路径位于: IBM\SPSS\Modeler\14\Demos)

随后,双击“建模”界面下的K-measns,如下图:

这里写图片描述

点击这里写图片描述运行,可得到如下结果:
这里写图片描述
结果提示:字段的指定类型不足,这就要求在可变文件之后修改字段类型,这里选用“字段类型”的“类型”。
这里写图片描述
运行后,结果如下:
这里写图片描述
右键这里写图片描述,选择“编辑”,可得聚类结果:
这里写图片描述
点击“视图”,可更改视图类型:
这里写图片描述
参考自:http://www.docin.com/p-598480949.html

5.算法分析

优点:算法简单,容易理解和实现

缺点:需要提前确定k值,算法根据k的取值会有不同的表现;无法确定哪个属性对聚类的贡献更大;由于使用均值来计算质心 ,故对于异常值敏感。

适用数据范围: 数值型数据,这是由于算法涉及计算数据间距离的操作,而只有数值型数据可以进行该操作。而对于只在有限目标集中取值的标称型数据(如true与false)需要通过一些手段映射为数值型数据才可使用该算法。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

kmeans总结 的相关文章

  • 如何在保留矩阵维度的同时序列化 numpy 数组?

    numpy array tostring似乎没有保留有关矩阵维度的信息 请参阅这个问题 https stackoverflow com q 30697769 1156707 要求用户发出调用numpy array reshape 有没有办法
  • 使用“iloc”时出现“尝试在 DataFrame 切片的副本上设置值”错误

    Jupyter 笔记本返回此警告 C anaconda lib site packages pandas core indexing py 337 SettingWithCopyWarning A value is trying to be
  • 为什么 statsmodels 和 R 的逻辑回归结果不同?

    我正在尝试比较 python 的 statsmodels 和 R 中的逻辑回归实现 Python版本 import statsmodels api as sm import pandas as pd import pylab as pl i
  • 在numpy中,[:,None]选择有什么作用?

    我正在学习 Udacity 的深度学习课程 我遇到了以下代码 def reformat dataset labels dataset dataset reshape 1 image size image size astype np flo
  • OpenPyXL - 如何查询单元格边框?

    python 和 openpyxl 都是新的 编写一个 py 脚本来遍历大量 Excel 工作簿 工作表 并且需要找到由边框格式标识的某些单元格 我在网上看到几个关于如何设置单元格边框的示例 但我需要阅读它们 具体来说 当表内的数据不一致但
  • 为什么 Python 中的无分支函数和内置函数速度较慢?

    我发现了 2 个无分支函数 它们可以在 python 中查找两个数字的最大值 并将它们与 if 语句和内置 max 函数进行比较 我认为无分支或内置函数将是最快的 但最快的是 if 语句函数 有人知道这是为什么吗 以下是功能 If 语句 2
  • Python 2.7从非默认目录打开多个文件(对于opencv)

    我在 64 位 win7 上使用 python 2 7 并拥有 opencv 2 4 x 当我写 cv2 imread pic 时 它会在我的默认 python 路径中打开 pic 即C Users Myname 但是我如何设法浏览不同的目
  • Pandas系列矢量化文本处理

    我想使用矢量化操作改进我的 Pandas 代码 假设我有一个简单的 DataFrame 其中有一个文本列 其中可能包含 url Column1 0 hello http www google com 1 bye www mail com w
  • 带剖面的 3D 曲面图

    基本上 我有一个由一组时间序列组成的曲面图 我想在特定高度添加剖面图 以更好地了解一年中值高于所选阈值的时期 由此 其中显示平面但不是剖面 To This 有什么建议吗 使用 alpha 和相机仰角并没有解决问题 平面似乎仍然在人物的前面
  • 如何在 matplotlib 图中禁用 xkcd?

    您可以通过以下方式打开 xkcd 风格 import matplotlib pyplot as plt plt xkcd 但如何禁用它呢 I try self fig clf 但这行不通 简而言之 要么使用 Valentin 提到的上下文管
  • 如何在 Python 中从 C++/C# 紧密实现 ?: ?

    在 C 中 我可以轻松编写以下内容 string stringValue string IsNullOrEmpty otherString defaultString otherString 有没有一种快速的方法可以在 Python 中做同
  • 如何在Python中使用内联正则表达式修饰符[重复]

    这个问题在这里已经有答案了 我有一个正则表达式 n DOCUMENTATION n n n 2 s 女巫我正在尝试处理这样的一些文件 usr bin python coding utf 8
  • 如何从张量流数据集迭代器返回同一批次两次?

    我正在转换一些旧代码以使用数据集 API 此代码使用feed dict将一批数据送入列车运行 实际上是三次 然后重新计算损失以供显示使用同一批 所以我需要一个迭代器来返回完全相同的批次两次 或多次 不幸的是 我似乎找不到一种使用张量流数据集
  • 如何将复杂的 csv 文件导入到 Matlab 中的数值向量

    我想知道我们应该如何读取由字符串 双精度数和字符等组成的复杂 csv 文件 例如 您能否提供一个可以在此 csv 文件中提取数值的成功命令 Click here http www ecb europa eu stats money yc d
  • 如何导入 boto3 ssm ParameterNotFound 异常?

    我想import the exception当一个boto3 ssm找不到参数get parameter 我正在尝试添加一些额外的内容ssm的功能moto图书馆 但我现在很困惑 gt gt gt import boto3 gt gt gt
  • 在 envoy 中使用 rm *(通配符):没有这样的文件或目录

    我正在使用 Python 和 Envoy 我需要删除目录中的所有文件 除了一些文件外 该目录是空的 在终端中 这将是 rm tmp my silly directory 常识表明 在特使中 这转化为 r envoy run rm tmp m
  • 忽略稀疏矩阵中的重复条目

    我尝试过初始化csc matrix and csr matrix从列表中 data rows cols 值如文档所示 sparse csc matrix data rows cols shape n n 问题是 我实际上拥有的生成方法dat
  • 检查一个数是否是完全平方数

    如何检查一个数是否是完全平方数 速度并不重要 目前 只是工作 See also Integer square root in python https stackoverflow com questions 15390807 依赖任何浮点计
  • 捕获 subprocess.run() 的输入

    我在 Windows 上有一个交互式命令行 exe 文件 是由其他人编写的 当程序出现异常时 它会终止 并且我对程序的所有输入都会丢失 所以我正在编写一个 python 程序 它调用一个阻塞子进程subprocess run 并捕获所有输入
  • Python DNS服务器IP地址查询

    我正在尝试使用 python 获取 DNS 服务器 IP 地址 要在 Windows 命令提示符下执行此操作 我将使用 ipconfig 全部 如下所示 我想使用 python 脚本做同样的事情 有什么方法可以提取这些值吗 我成功提取了设备

随机推荐

  • AD20(Altium designer) PCB图元件位号不显示

    别人发来了一份PCB图 元件位号不显示记录下原因 1 单击红框中黄色的方块 2 弹出的窗口中点击Texts的小眼睛就可以啦
  • 四、vim高级用法配置

    四 vim高级用法配置 4 1 vim的三种模式 vim是全球两大流行文本编辑器之一 vim file 直接编辑文件内容 vim wq 文件存在 wq file 文件不存在 vim模式 1 命令模式 浏览模式 此模式下只能对文件内容浏览 对
  • v-for里面再嵌套一个v-for的写法

    v for里面再嵌套一个v for的写法 list id goods images product desc div class listContent span item create time i formatDate span div
  • 什么是深度学习?怎么学好深度学习?

    深度学习 是一种强大的多层架构 可以用于模式识别 信号检测以及分类或预测等多个领域 深度学习在过去十年获得了极高的关注 这归功于计算能力的不断发展和训练模型不断涌现出更有效的新方法 也源于可使用的数据量不断增加 什么是深度学习 为了理解深度
  • HBase启动RegionServer时报UnknownHostException错误的解决方法

    HBase启动RegionServer时报错 ERROR main regionserver HRegionServer Failed construction RegionServer java lang IllegalArgumentE
  • C++ string 类中方法 size() 和 length() 的区别

    首先说明 在C string类中 size 和length 方法是没有任何区别的 我们可以看 Microsoft Visual Studio 10 0 VC include xstring 文件 转到此两个方法的定义 size type l
  • Python3——matplotlib条形图的绘制

    实验环境 python 3 6 matplotlib 2 2 3 条形图的绘制 matplotlib pyplot bar left height alpha 1 width 0 8 color edgecolor label lw 3 1
  • 比df更好用的命令!

    大家好 我是良许 对于分析磁盘使用情况 有两个非常好用的命令 du 和 df 简单来说 这两个命令的作用是这样的 du 命令 它是英文单词 disk usage 的简写 主要用于查看文件与目录占用多少磁盘空间 df 命令 它是英文单词 di
  • 【SSH框架】慎用hibernate中的saveOrUpdate()方法,解决方案!

    今天写的项目中遇到一个异常 org springframework orm hibernate5 HibernateOptimisticLockingFailureException Batch update returned unexpe
  • 【马克思主义基本原理】--第二章--实践与认识及其发展规律

    实践与认识及其发展规律 文章目录 实践与认识及其发展规律 科学实践观 实践的本质与基本结构 真理和价值的辩证 科学实践观 马克思科学阐明了人类实践的本质和作用 创立了科学的实践观 科学的实践观是不断丰富发展的 总之 科学实践观从主观和客观
  • VS2017评估期已过的处理方法

    Visual Studio 2017 VS2017 企业版 Enterprise 注册码 NJVYC BMHX2 G77MM 4XJMR 6Q8QF Visual Studio 2017 VS2017 专业版Professional 激活码
  • r730xd服务器文档,r730xd配置服务器远程

    r730xd配置服务器远程 内容精选 换一换 如果默认的yum apt zypper源不可用 工具安装过程中会从华为开源镜像站匹配对应的镜像文件 并给出下载地址 如果没有匹配到 请自行获取对应操作系统版本的镜像文件 镜像文件名称请参见鲲鹏开
  • request.getRequestDispatcher(url) /error 404

    二 使用语法 request getRequestDispatcher 资源URI forward request response response sendRedirect web应用 资源URI 在进行web开发时 跳转是最常见的 包
  • Next 主题配置

    当前用得最多的是next主题 那为什么用得多呢 当然是符合大多数人的审美 我使用的是next v7 8 0 下载地址 theme next hexo theme next 1 基本设置 1 1 主题设置 打开博客根目录 Blog 文件夹 右
  • proteus仿真STM32串口的各种问题和解决办法

    最近在学习STM32串口通信 想试试能不能用proteus仿真 发现还是有挺多问题的 刚一开始在原理图放个STM32就报错 通过查阅资料才知道 选择 Design gt Configure Power Rails 添加到VCC VDD里头既
  • 测试开发必备10大技能,你达标了吗?

    一个人到底要走多少弯路 才能成为一名合格的测试开发工程师 近年来 随着敏捷开发 微服务架构 DevOps逐渐深入人心 软件行业发生了翻天覆地的变化 相应地 软件测试行业也洗牌加剧 软件测试的准入门槛 也从以前的是个人就行 逐渐变成了 科班出
  • JAVA————一门强大的面向对象编程语言

    JAVA 一门强大的面向对象编程语言 Java是一门面向对象编程语言 不仅吸收了C 语言的各种优点 还摒弃了C 里难以理解的多继承 指针等概念 因此Java语言具有功能强大和简单易用两个特征 Java语言作为静态面向对象编程语言的代表 极好
  • 卷积神经网络实现人脸表情识别

    文章目录 一 实现过程 1 1 下载数据集 1 2 根据猫狗数据集训练的方法来训练笑脸数据集 1 2 图片分类 1 3 作为健全性检查 计算一下在每个训练分割中我们有多少图片 训练 验证 测试 1 4 卷积网络模型搭建 1 5 图像生成器读
  • ClassCastException: java.math.BigInteter cannot be cast to java.math.BigDecimal

    原文链接 https blog csdn net huxiaochao 6053 java article details 84750905 java Math BigInteger转 int类型 使用spring data jpa 查询数
  • kmeans总结

    1 关于分类和聚类 kmeans属于聚类算法中的一种 分类和聚类是不同的概念 虽然两者的目的都是对数据进行分类 但是却有一定的区别 分类是按照某种标准给对象贴标签 再根据标签来区分归类 聚类是事先没有给出标签 刚开始并不知道如何对数据分类