当前位置:   article > 正文

K-均值聚类算法

对k-均值聚类最小化平方误差描述正确的是:

一.k均值聚类算法

对于样本集。"k均值"算法就是针对聚类划分最小化平方误差: 

其中是簇Ci的均值向量。从上述公式中可以看出,该公式刻画了簇内样本围绕簇均值向量的紧密程度,E值越小簇内样本的相似度越高。

 

工作流程:

 

k-均值算法的描述如下:

创建k个点作为起始质心(通常随机选择)
当任意一个点的簇分配结果发生改变时:
        对数据集中的每个点:
                对每个质心:
                计算质心与数据点之间的距离
         将数据点分配到距离其最近的簇
对每一个簇,计算簇中所有点的均值并将均值作为质心

接下来是对于数据集testSet.txt的代码实现:

  1. 1.658985 4.285136
  2. -3.453687 3.424321
  3. 4.838138 -1.151539
  4. -5.379713 -3.362104
  5. 0.972564 2.924086
  6. -3.567919 1.531611
  7. 0.450614 -3.302219
  8. -3.487105 -1.724432
  9. 2.668759 1.594842
  10. -3.156485 3.191137
  11. 3.165506 -3.999838
  12. -2.786837 -3.099354
  13. 4.208187 2.984927
  14. -2.123337 2.943366
  15. 0.704199 -0.479481
  16. -0.392370 -3.963704
  17. 2.831667 1.574018
  18. -0.790153 3.343144
  19. 2.943496 -3.357075
  20. -3.195883 -2.283926
  21. 2.336445 2.875106
  22. -1.786345 2.554248
  23. 2.190101 -1.906020
  24. -3.403367 -2.778288
  25. 1.778124 3.880832
  26. -1.688346 2.230267
  27. 2.592976 -2.054368
  28. -4.007257 -3.207066
  29. 2.257734 3.387564
  30. -2.679011 0.785119
  31. 0.939512 -4.023563
  32. -3.674424 -2.261084
  33. 2.046259 2.735279
  34. -3.189470 1.780269
  35. 4.372646 -0.822248
  36. -2.579316 -3.497576
  37. 1.889034 5.190400
  38. -0.798747 2.185588
  39. 2.836520 -2.658556
  40. -3.837877 -3.253815
  41. 2.096701 3.886007
  42. -2.709034 2.923887
  43. 3.367037 -3.184789
  44. -2.121479 -4.232586
  45. 2.329546 3.179764
  46. -3.284816 3.273099
  47. 3.091414 -3.815232
  48. -3.762093 -2.432191
  49. 3.542056 2.778832
  50. -1.736822 4.241041
  51. 2.127073 -2.983680
  52. -4.323818 -3.938116
  53. 3.792121 5.135768
  54. -4.786473 3.358547
  55. 2.624081 -3.260715
  56. -4.009299 -2.978115
  57. 2.493525 1.963710
  58. -2.513661 2.642162
  59. 1.864375 -3.176309
  60. -3.171184 -3.572452
  61. 2.894220 2.489128
  62. -2.562539 2.884438
  63. 3.491078 -3.947487
  64. -2.565729 -2.012114
  65. 3.332948 3.983102
  66. -1.616805 3.573188
  67. 2.280615 -2.559444
  68. -2.651229 -3.103198
  69. 2.321395 3.154987
  70. -1.685703 2.939697
  71. 3.031012 -3.620252
  72. -4.599622 -2.185829
  73. 4.196223 1.126677
  74. -2.133863 3.093686
  75. 4.668892 -2.562705
  76. -2.793241 -2.149706
  77. 2.884105 3.043438
  78. -2.967647 2.848696

  

from numpy import *

#load data
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines(): #for each line
        curLine = line.strip().split('\t')
        fltLine = list(map(float,curLine)) #这里和书中不同 和上一章一样修改
        dataMat.append(fltLine)
    return dataMat

#distance func
def distEclud(vecA,vecB):
    return sqrt(sum(power(vecA - vecB, 2)))  # la.norm(vecA-vecB) 向量AB的欧式距离

#init K points randomly
def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))#create centroid mat
    for j in range(n):#create random cluster centers, within bounds of each dimension
        minJ = min(dataSet[:,j])
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))
    return centroids

 

计算出矩阵中的最大值、最小值、距离:

>>> import mykmeans
>>> from numpy import *
>>> datMat=mat(mykmeans.loadDataSet('testSet.txt'))
>>> min(datMat[:,0])
matrix([[-5.379713]])
>>> min(datMat[:,1])
matrix([[-4.232586]])
>>> max(datMat[:,0])
matrix([[4.838138]])
>>> max(datMat[:,1])
matrix([[5.1904]])
>>> mykmeans.randCent(datMat,2)
matrix([[ 0.94526572, -2.73986112],
        [-1.56707725,  4.14438101]])
>>> mykmeans.distEclud(datMat[0],datMat[1])
5.184632816681332

 

接下来是K-均值算法的核心程序:

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))#create mat to assign data points 
                                      #to a centroid, also holds SE of each point
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):#for each data point assign it to the closest centroid
            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):#recalculate centroids
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
            centroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to mean 
    return centroids, clusterAssment

datMat=mat(loadDataSet('testSet.txt'))
myCentroids,clustAssing=kMeans(datMat,4)

 

取k为4,得到4个质心:

[[ 4.80480327  0.7928886 ]
 [ 1.2776812  -3.82602902]
 [ 2.56286444  4.00821365]
 [-1.43428372  3.0160483 ]]
[[ 4.007951   -0.28653243]
 [-0.64297274 -3.04016156]
 [ 2.51964406  3.40459212]
 [-2.605345    2.35623864]]
[[ 3.4363324  -2.41850987]
 [-2.26682904 -2.92971896]
 [ 2.54391447  3.21299611]
 [-2.46154315  2.78737555]]
[[ 2.926737   -2.70147753]
 [-3.19984738 -2.96423548]
 [ 2.6265299   3.10868015]
 [-2.46154315  2.78737555]]
[[ 2.80293085 -2.7315146 ]
 [-3.38237045 -2.9473363 ]
 [ 2.6265299   3.10868015]
 [-2.46154315  2.78737555]]

二.后处理提高聚类性能

聚类算法中,k的值是由用户初始定义的,如何才能判断k值定义是否合适,就需要用误差来评价聚类效果的好坏,误差是各个点与其所属类别质心的距离决定的。K-均值聚类的方法效果较差的原因是会收敛到局部最小值,而且全局最小。一种评价聚类效果的方法是SSE(Sum of Squared Error)误差平方和的方法,取平方的结果是使得远离中心的点变得更加突出。

一种降低SSE的方法是增加簇的个数,即提高k值,但是违背了聚类的目标,聚类的目标是在不改变簇数目的前提下提高簇的质量。可选的改进的方法是对生成的簇进行后处理,将最大SSE值的簇划分成两个(K=2的K-均值算法),然后再进行相邻的簇合并。具体方法有两种:1、合并最近的两个质心(合并使得SSE增幅最小的两个质心)2、遍历簇 合并两个然后计算SSE的值,找到使得SSE最小的情况。

 

三.二分K-均值算法

这个算法的思想是:初始状态所有数据点属于一个大簇,之后每次选择一个簇切分成两个簇,这个切分满足使SSE值最大程度降低,直到簇数目达到k。另一种思路是每次选择SSE值最大的一个簇进行切分。

 

满足使SSE值最大程度降低伪代码如下:

将所有点看成一个簇
   当簇数目小于k时
        对于每一个簇:
            计算总误差
            在给定的簇上面进行K-均值聚类(k=2)
    计算将该簇一分为二后的总误差
    选择使得误差最小的那个簇进行划分操作

代码如下:

def biKmeans(dataSet,k,distMeas=distEclud):
    m=shape(dataSet)[0]
    clusterAssment=mat(zeros((m,2)))
    centroid0 = mean(dataSet, axis=0).tolist()[0]
    centList = [centroid0]  # create a list with one centroid
    for j in range(m):  # calc initial Error for each point
        clusterAssment[j, 1] = distMeas(mat(centroid0), dataSet[j, :]) ** 2
    while (len(centList) < k):
        lowestSSE = inf #init SSE
        for i in range(len(centList)):#for every centroid
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0],:]  # get the data points currently in cluster i
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)# k=2,kMeans
            sseSplit = sum(splitClustAss[:, 1])  # compare the SSE to the currrent minimum
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
            print("sseSplit, and notSplit: ", sseSplit, sseNotSplit)
            if (sseSplit + sseNotSplit) < lowestSSE: #judge the error
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
        #new cluster and split cluster
        bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)  # change 1 to 3,4, or whatever
        bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
        print('the bestCentToSplit is: ', bestCentToSplit)
        print('the len of bestClustAss is: ', len(bestClustAss))
        centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0]  # replace a centroid with two best centroids
        centList.append(bestNewCents[1, :].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0],:] = bestClustAss  # reassign new clusters, and SSE
    return mat(centList), clusterAssment

datMat3=mat(loadDataSet('testSet2.txt'))
centList,myNewAssments=biKmeans(datMat3,3)
centList

运行结果:

[[4.22282288 2.88343006]
 [0.53706656 4.28802843]]
[[ 2.37096343  0.78503778]
 [-1.72960876  1.49902535]]
[[ 2.22969593  1.291217  ]
 [-2.24671409  1.16767909]]
[[ 2.40575527  1.66726781]
 [-2.11802947  0.88737776]]
[[ 2.93386365  3.12782785]
 [-1.70351595  0.27408125]]
sseSplit, and notSplit:  541.2976292649145 0.0
the bestCentToSplit is:  0
the len of bestClustAss is:  60
[[2.17022341 1.96268662]
 [3.12589387 4.12081081]]
[[2.38650938 2.40163825]
 [3.2987665  3.61195425]]
[[2.32716778 2.52893967]
 [3.43025118 3.61782727]]
sseSplit, and notSplit:  27.63707606832937 501.7683305828214
[[-2.80259011  4.25652642]
 [-0.51490661  3.66748635]]
[[-3.14707283  3.35698672]
 [-0.52242395 -2.24829595]]
[[-2.94737575  3.3263781 ]
 [-0.45965615 -2.7782156 ]]
sseSplit, and notSplit:  67.2202000797829 39.52929868209309
the bestCentToSplit is:  1
the len of bestClustAss is:  40

四.应用:对地图上的点进行聚类

portlandClubs.txt

Dolphin II    10860 SW Beaverton-Hillsdale Hwy    Beaverton, OR
Hotties    10140 SW Canyon Rd.    Beaverton, OR
Pussycats    8666a SW Canyon Road    Beaverton, OR
Stars Cabaret    4570 Lombard Ave    Beaverton, OR
Sunset Strip    10205 SW Park Way    Beaverton, OR
Vegas VIP Room    10018 SW Canyon Rd    Beaverton, OR
Full Moon Bar and Grill    28014 Southeast Wally Road    Boring, OR
505 Club    505 Burnside Rd    Gresham, OR
Dolphin    17180 McLoughlin Blvd    Milwaukie, OR
Dolphin III    13305 SE McLoughlin BLVD    Milwaukie, OR
Acropolis    8325 McLoughlin Blvd    Portland, OR
Blush    5145 SE McLoughlin Blvd    Portland, OR
Boom Boom Room    8345 Barbur Blvd    Portland, OR
Bottoms Up    16900 Saint Helens Rd    Portland, OR
Cabaret II    17544 Stark St    Portland, OR
Cabaret Lounge    503 W Burnside    Portland, OR
Carnaval    330 SW 3rd Avenue    Portland, OR
Casa Diablo    2839 NW St. Helens Road    Portland, OR
Chantilly Lace    6723 Killingsworth St    Portland, OR
Club 205    9939 Stark St    Portland, OR
Club Rouge    403 SW Stark    Portland, OR
Dancin' Bare    8440 Interstate Ave    Portland, OR
Devil's Point    5305 SE Foster Rd    Portland, OR
Double Dribble    13550 Southeast Powell Boulevard    Portland, OR
Dream on Saloon    15920 Stark St    Portland, OR
DV8    5003 Powell Blvd    Portland, OR
Exotica    240 Columbia Blvd    Portland, OR
Frolics    8845 Sandy Blvd    Portland, OR
G-Spot Airport    8654 Sandy Blvd    Portland, OR
G-Spot Northeast    3400 NE 82nd Ave    Portland, OR
G-Spot Southeast    5241 SE 72nd Ave    Portland, OR
Glimmers    3532 Powell Blvd    Portland, OR
Golden Dragon Exotic Club    324 SW 3rd Ave    Portland, OR
Heat    12131 SE Holgate Blvd.    Portland, OR
Honeysuckle's Lingerie    3520 82nd Ave    Portland, OR
Hush Playhouse    13560 Powell Blvd    Portland, OR
JD's Bar &amp; Grill    4523 NE 60th Ave    Portland, OR
Jody's Bar And Grill    12035 Glisan St    Portland, OR
Landing Strip    6210 Columbia Blvd    Portland, OR
Lucky Devil Lounge    633 SE Powell Blvd    Portland, OR
Lure    11051 Barbur Blvd    Portland, OR
Magic Garden    217 4th Ave    Portland, OR
Mary's Club    129 Broadway    Portland, OR
Montego's    15826 SE Division    Portland, OR
Mr. Peeps    709 122nd Ave    Portland, OR
Mynt Gentlemen's Club    3390 NE Sandy Blvd    Portland, OR
Mystic    9950 SE Stark St.    Portland, OR
Nicolai Street Clubhouse    2460 24th Ave    Portland, OR
Oh Zone    6218 Columbia Blvd    Portland, OR
Pallas Club    13639 Powell Blvd    Portland, OR
Pirates Cove    7427 Sandy Blvd    Portland, OR
Private Pleasures    10931 53rd Ave    Portland, OR
Pussycats    3414 Northeast 82nd Avenue    Portland, OR
Riverside Corral    545 Tacoma St    Portland, OR
Rooster's    605 Columbia Blvd    Portland, OR
Rose City Strip    3620 35th Pl    Portland, OR
Safari Show Club    3000 SE Powell Blvd    Portland, OR
Sassy's Bar &amp; Grill    927 Morrison St    Portland, OR
Secret Rendezvous    12503 Division St    Portland, OR
Shimmers    7944 Foster Rd    Portland, OR
Soobie's    333 SE 122nd Ave    Portland, OR
Spyce Gentleman's Club    33 NW 2nd Ave    Portland, OR
Sugar Shack    6732 Killingsworth St    Portland, OR
The Hawthorne Strip    1008 Hawthorne Blvd    Portland, OR
Tommy's Too    10335 Foster Rd    Portland, OR
Union Jacks    938 Burnside St    Portland, OR
Video Visions    6723 Killingsworth St    Portland, OR
Stars Cabaret Bridgeport    17939 SW McEwan Rd    Tigard, OR
Jiggles    7455 SW Nyberg St    Tualatin, OR

 

首先,我们需要将地址转换为经度和纬度

import urllib
import json
def geoGrab(stAddress, city):
    apiStem = 'http://where.yahooapis.com/geocode?'  #create a dict and constants for the goecoder
    params = {}
    params['flags'] = 'J'#JSON return type
    params['appid'] = 'aaa0VN6k'
    params['location'] = '%s %s' % (stAddress, city)
    url_params = urllib.parse.urlencode(params)
    yahooApi = apiStem + url_params      #print url_params
    print (yahooApi)
    c=urllib.parse.urlopen(yahooApi)
    return json.loads(c.read())

from time import sleep
def massPlaceFind(fileName):
    fw = open('places.txt', 'w')
    for line in open(fileName).readlines():
        line = line.strip()
        lineArr = line.split('\t')
        retDict = geoGrab(lineArr[1], lineArr[2])
        if retDict['ResultSet']['Error'] == 0:
            lat = float(retDict['ResultSet']['Results'][0]['latitude'])
            lng = float(retDict['ResultSet']['Results'][0]['longitude'])
            print ("%s\t%f\t%f" % (lineArr[0], lat, lng))
            fw.write('%s\t%f\t%f\n' % (line, lat, lng))
        else: print ("error fetching")
        sleep(1)
    fw.close()
    
massPlaceFind('portlandClubs.txt')

 

这会在工作目录下生成一个places.txt文本文件,接下来将使用这些点进行聚类,并将俱乐部以及他们的簇中心画在城市地图上。

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

import matplotlib
import matplotlib.pyplot as plt
def clusterClubs(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 = biKmeans(datMat, numClust, distMeas=distSLC)
    fig = plt.figure()
    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)
    plt.show()

clusterClubs(5)

 

当 将簇的数目改为6时:

 

当将簇的数目改为7时

 参考:https://blog.csdn.net/sinat_17196995/article/details/70332664

            https://blog.csdn.net/u013266697/article/details/52832215

转载于:https://www.cnblogs.com/yue-guan/p/1072kmeans.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/129057
推荐阅读
相关标签
  

闽ICP备14008679号