K-Means

原理

  • 聚类是无监督学习,将相似的对象归到同一个簇中,簇内的对象越相似,聚类的效果越好;
  • 首先,随机确定K个初始点作为质心;
  • 然后,将数据集中的每个点分配到一个簇中,具体来讲,为每个点找距其最近的质心,将其分配到改质心对应的簇;
  • 接着,每个簇的质心更新为该簇所有点的平均值

优点:

  • 容易实现

缺点:

  • 可能收敛到局部最小值,在大规模数据集上收敛较慢

适用数据类型:

  • 数值型数据
import numpy as np
#定义加载数据函数
def loadDataSet(fileName):
    dataList = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split()
        fltLine = list(map(float,curLine)) #每个元素,str2float
        dataList.append(fltLine)
    return dataList
dataList = loadDataSet('../../Reference Code/Ch10/testSet.txt')
#查看数据分布
import matplotlib.pyplot as plt
def data2show(dataArr):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(dataArr[:,0],dataArr[:,1],label='raw data')
    ax.legend()
    ax.set_title('Data Distribution')
    ax.set_xlabel('x1')
    ax.set_ylabel('x2')
    plt.show()
dataArr = np.array(dataList)
data2show(dataArr)
output_3_0.png
#定义距离公式(相似度计算)
def distEclud(vecA,vecB):
    distance = np.sqrt(np.sum(np.power(vecA - vecB,2)))
    return distance
#构建质心
def randCent(dataSet,k):
    n = dataSet.shape[1]
    centroids = np.mat(np.zeros((k,n)))

    #每一列,在范围内随机选择三个点
    for j in range(n):#遍历每一列
        minJ = np.min(dataSet[:,j]) #每列最小值
        rangeJ = float(np.max(dataSet[:,j]) - minJ) #每列的范围
        centroids[:,j] = minJ + rangeJ*np.random.rand(k,1)  #np.random.rand  over [0, 1)
        '''
        np.random.rand(3,1)
                array([[0.73830851],
                       [0.55393815],
                       [0.4770937 ]])
        '''
    return centroids
#K-Means聚类算法
def kMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
    m = dataSet.shape[0]
    clusterAssment = np.mat(np.zeros((m,2))) #记录簇索引值和存储误差
    centroids = createCent(dataSet,k) #初始化质心
    clusterChanged = True #是否继续聚类操作
    while clusterChanged:
        clusterChanged = False
        
        #所有样本计算离质心的距离
        for i in range(m):
            minDist = np.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[np.nonzero(clusterAssment[:,0].A == cent)[0]] #该簇所有样本
            centroids[cent,:] = np.mean(ptsInClust,axis=0)
    return centroids,clusterAssment
#画图看聚类结果
def res2show(dataMat,centroids,clusterAssment):
    k = len(centroids)
    names = locals() #局部命名空间,dict形式
    dataMarkerList = ['*','s','o','x']
    
    fig = plt.figure()
    ax = fig.add_subplot(111) 
    for i in range(k):
        #质心
        names['centroids'+str(i)] = centroids[i,:].A
        #各个簇的样本
        names['data'+str(i)] = dataMat[clusterAssment.A[:,0]==i].A
        #画质心
        ax.scatter(names['centroids'+str(i)][:,0],names['centroids'+str(i)][:,1],marker='+',s=200,label=str(i))
        #画样本
        ax.scatter(names['data'+str(i)][:,0],names['data'+str(i)][:,1],marker=dataMarkerList[i],label='cluster '+str(i))
        ax.legend(loc='best')
    
    ax.set_title('Result with K-Means')
    plt.show()
dataList = loadDataSet('../../Reference Code/Ch10/testSet.txt')
dataMat = np.mat(dataList)
centroids,clusterAssment = kMeans(dataMat,4,distMeas=distEclud,createCent=randCent)
res2show(dataMat,centroids,clusterAssment)
output_9_0.png

二分K-Means算法

    1. 所有点作为一个簇;
    1. 将该簇一分为二,选择一个簇进行划分,选择最大程度降低SSE的簇进行划分(最大SSE的簇);
    1. 再选择其中一个簇进行划分,选择最大程度降低SSE的簇进行划分(最大SSE的簇);
    1. 直到簇达到设定的K值为止。
def biKmeans(dataSet,k,distMeas = distEclud):
    m = dataSet.shape[0]
    clusterAssment = np.mat(np.zeros((m,2)))
    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
    
    #建立K个簇
    while (len(centList) < k):
        lowestSSE= np.inf#初始化最低SSE为无穷大
        
        #遍历每一个簇
        for i in range(len(centList)):
            ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:,0].A == i)[0],:] #提取该簇所有样本
            centroidMat,splitClustAss = kMeans(ptsInCurrCluster,k=2,distMeas=distMeas) #对该簇的样本进行K=2的kmeans聚类
            sseSplit = np.sum(splitClustAss[:,1]) #该簇聚类之后的sse
            sseNotSplit = np.sum(clusterAssment[np.nonzero(clusterAssment[:,0].A != i)[0],1])#其他簇的sse
            print('sseSplit, and notsplit:',sseSplit,sseNotSplit)
            
            #寻找划分后有最小SSE的簇
            if (sseSplit+sseNotSplit)<lowestSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit+sseNotSplit
        
        #更新簇的分配结果
        bestClustAss[np.nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
        bestClustAss[np.nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
        clusterAssment[np.nonzero(clusterAssment[:,0].A==bestCentToSplit)[0],:] = bestClustAss
        print('the bestCentToSplit is:',bestCentToSplit)
        print('the len of bestClustAss is:',len(bestClustAss))
        
        #更新质心
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
        centList.append(bestNewCents[1,:].tolist()[0])
    return np.mat(centList),clusterAssment
k-means VS 二分k-means
#k-means
dataMat2= np.mat(loadDataSet('../../Reference Code/Ch10/testSet2.txt'))
centroids,clusterAssment = kMeans(dataMat2,3,distMeas=distEclud,createCent=randCent)
res2show(dataMat2,centroids,clusterAssment)
output_13_0.png
#二分k-means
dataMat3= np.mat(loadDataSet('../../Reference Code/Ch10/testSet2.txt'))
centList,clusterAssment = biKmeans(dataMat3,3)
res2show(dataMat3,centList,clusterAssment)
sseSplit, and notsplit: 453.0334895807502 0.0
the bestCentToSplit is: 0
the len of bestClustAss is: 60
sseSplit, and notsplit: 12.753263136887313 423.8762401366249
sseSplit, and notsplit: 77.59224931775066 29.15724944412535
the bestCentToSplit is: 1
the len of bestClustAss is: 40
output_14_1.png
对地理坐标进行聚类
from numpy import *
import matplotlib
import matplotlib.pyplot as plt

#计算地球表面两点的距离
def distSLC(vecA, vecB):#Spherical Law of Cosines
    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 #pi is imported with numpy

def clusterClubs(numClust=5):
    #读取数据
    datList = []
    for line in open('../../Reference Code/Ch10/places.txt').readlines():
        lineArr = line.split('\t')
        datList.append([float(lineArr[4]), float(lineArr[3])])
    datMat = mat(datList)
    
    #二分k-means
    myCentroids, clustAssing = biKmeans(datMat, numClust, distMeas=distSLC)
    
    
    fig = plt.figure()
    rect=[0.1,0.1,0.8,0.8] #rect=[left, bottom, width, height]
    scatterMarkers=['s', 'o', '^', '8', 'p', \
                    'd', 'v', 'h', '>', '<']
    axprops = dict(xticks=[], yticks=[])
    ax0=fig.add_axes(rect, label='ax0', **axprops)
    
    #基于一张地图来画图
    imgP = plt.imread('../../Reference Code/Ch10/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)] #返回余数作为索引,可以循环使用markers
        #画簇的样本
        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)

    plt.show()
clusterClubs(5)
sseSplit, and notsplit: 3431.621150934527 0.0
the bestCentToSplit is: 0
the len of bestClustAss is: 69
sseSplit, and notsplit: 1230.242092893483 1062.0271973570536
sseSplit, and notsplit: 608.5836216116304 2369.5939535774737
the bestCentToSplit is: 0
the len of bestClustAss is: 53
sseSplit, and notsplit: 197.3863640526132 1892.3442135171072
sseSplit, and notsplit: 515.6100922622932 1230.242092893483
sseSplit, and notsplit: 471.8115193432218 1461.952274090483
the bestCentToSplit is: 1
the len of bestClustAss is: 16
sseSplit, and notsplit: 170.75670898476076 1345.9271084223471
sseSplit, and notsplit: 53.299046126034725 1437.9528665605217
sseSplit, and notsplit: 471.8115193432218 915.5351689957225
sseSplit, and notsplit: 98.33199611762291 1538.141411488738
the bestCentToSplit is: 2
the len of bestClustAss is: 35
output_17_1.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • K-means算法的相关描述 聚类是一种无监督的学习,它将相似的对象归到同一簇中。聚类的方法几乎可以应用所有对象,...
    ghostdogss阅读 1,458评论 0 0
  • 第 10 章 K-Means(K-均值)聚类算法 K-Means 算法 聚类是一种无监督的学习, 它将相似的对象归...
    Joyyx阅读 2,558评论 1 9
  • 大家早安、午安、晚安哈,继续学习机器学习算法,接下来几篇均是无监督学习算法。今天首先学习K-means(K-均值)...
    keepStriving阅读 5,867评论 0 7
  • 一、算法介绍 聚类属于无监督学习,K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即...
    TangCC阅读 2,917评论 0 1
  • 早晨退房的时候突然大雨如注,五分钟后艳阳高照。 约好八点五十,七点四十九的时候,欧桑:不是说日本人很守时吗?怎么还...
    为卿画眉L阅读 499评论 1 0