python机器学习实战之K均值聚类

时间:2021-10-23 03:32:39

本文实例为大家分享了python K均值聚类的具体代码,供大家参考,具体内容如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#-*- coding:utf-8 -*-
#!/usr/bin/python
 
'''''
k Means K均值聚类
'''
# 测试
# K均值聚类   import kMeans as KM KM.kMeansTest()
# 二分K均值聚类 import kMeans as KM KM.biKMeansTest()
# 地理位置 二分K均值聚类 import kMeans as KM KM.clusterClubs()
from numpy import *
 
# 导入数据集
def loadDataSet(fileName):  
  dataMat = []       
  fr = open(fileName)
  for line in fr.readlines(): # 每一行
    curLine = line.strip().split('\t')# 按 Tab键 分割成 列表
    fltLine = map(float,curLine)   # 映射成 浮点型
    dataMat.append(fltLine)      # 放入数据集里
  return dataMat
 
# 计算欧几里的距离
def distEclud(vecA, vecB):
  return sqrt(sum(power(vecA - vecB, 2))) #la.norm(vecA-vecB)
 
# 初始构建质心(随机) 数据集 质心个数
def randCent(dataSet, k):
  n = shape(dataSet)[1] # 样本特征维度
  centroids = mat(zeros((k,n))) # 初始化 k个 质心
  for j in range(n):  # 每种样本特征
    minJ = min(dataSet[:,j]) # 每种样本特征最小值 需要转换成 numpy 的mat
    rangeJ = float(max(dataSet[:,j]) - minJ)#每种样本特征的幅值范围
    centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))
    # 在每种样本的最大值和最小值间随机生成K个样本特征值
  return centroids
 
# 简单k均值聚类算法 
#    数据集 中心数量  距离算法      初始聚类中心算法 
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
  m = shape(dataSet)[0]       # 样本个数
  clusterAssment = mat(zeros((m,2)))# 样本标记 分配结果 第一列索引 第二列误差
  centroids = createCent(dataSet, 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 # 保存 所属索引 以及距离平方 用以计算误差平方和 SSE
    # 更新质心
    print centroids # 每次迭代打印质心
    for cent in range(k):
      ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]# 数组过滤 得到各个中心所属的样本
      centroids[cent,:] = mean(ptsInClust, axis=0) # 按列求平均 得到新的中心
  return centroids, clusterAssment# 返回质心 和各个样本分配结果
 
def kMeansTest(k=5):
  MyDatMat = mat(loadDataSet("testSet.txt"))
  MyCenters, ClustAssing = kMeans(MyDatMat, k)
 
# bisecting K-means 二分K均值算法 克服局部最优值
def biKmeans(dataSet, k, distMeas=distEclud):
  m = shape(dataSet)[0]       # 样本个数
  clusterAssment = mat(zeros((m,2)))# 样本标记 分配结果 第一列索引 第二列误差
  centroid0 = mean(dataSet, axis=0).tolist()[0]# 创建一个初始质心
  centList =[centroid0] # 一个中心的 列表
  for j in range(m):  # 计算初始误差
    clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2#每个样本与中心的距离平方
  while (len(centList) < k):# 中心数俩个未达到指定中心数量 继续迭代
    lowestSSE = inf    # 最小的 误差平方和 SSE
    for i in range(len(centList)):# 对于每一个中心
      ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:] # 处于当前中心的样本点
      centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas) # 对此中心内的点进行二分类
      # 该样本中心 二分类之后的 误差平方和 SSE
    sseSplit = sum(splitClustAss[:,1])
      # 其他未划分数据集的误差平方和 SSE
      sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
      print "sseSplit, and notSplit: ",sseSplit,sseNotSplit
      # 划分后的误差和没有进行划分的数据集的误差为本次误差
      if (sseSplit + sseNotSplit) < lowestSSE: # 小于上次 的 误差 
        bestCentToSplit = i # 记录应该被划分的中心 的索引
        bestNewCents = centroidMat # 最好的新划分出来的中心
        bestClustAss = splitClustAss.copy()# 新中心 对于的 划分记录 索引(0或1)以及 误差平方
        lowestSSE = sseSplit + sseNotSplit # 更新总的 误差平方和
    # 记录中心 划分 数据
    bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) # 现有中心数量
    bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit# 最应该被划分的中心
    print 'the bestCentToSplit is: ',bestCentToSplit
    print 'the len of bestClustAss is: ', len(bestClustAss)
    # 将最应该被划分的中心 替换为 划分后的 两个 中心(一个替换,另一个 append在最后添加)
    centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]# 替换
    centList.append(bestNewCents[1,:].tolist()[0])      # 添加
    # 更新 样本标记 分配结果 替换 被划分中心的记录
    clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss
  return mat(centList), clusterAssment
 
def biKMeansTest(k=5):
  MyDatMat = mat(loadDataSet("testSet.txt"))
  MyCenters, ClustAssing = biKmeans(MyDatMat, k)
 
####位置数据聚类测试#####
# 利用雅虎的服务器将地址转换为 经度和纬度
import urllib
import json
def geoGrab(stAddress, city):
  apiStem = 'http://where.yahooapis.com/geocode?'
  params = {}
  params['flags'] = 'J'    # 设置返回类型为JSON字符串 
  params['appid'] = 'aaa0VN6k' # 注册 帐号后获得 http://developer.yahoo.com
  params['location'] = '%s %s' % (stAddress, city) # 位置信息
  url_params = urllib.urlencode(params)# 将字典转换成可以通过URL进行传递的字符串格式
  yahooApi = apiStem + url_params   # 加入网络地址 
  print yahooApi            # 打印 URL
  c=urllib.urlopen(yahooApi)      # 打开 URL
  return json.loads(c.read())     # 读取返回的jason字符串  对位置进行了编码 得到经度和纬度 
 
 
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)#延迟1s
  fw.close()
 
# 返回地球表面两点之间的距离 单位英里 输入经纬度(度) 球面余弦定理
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 in 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)# 数据集 numpy的mat类型
  # 进行二分K均值算法聚类
  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()# 显示

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/xiaoxiaowenqiang/article/details/78076896