K-Means
K-Means算法流程:
Input:k值,data[n];
①随机选择k个对象作为初始中心点,例如c[0]=data[0],…c[k-1]=data[k-1];;
②对于data[0]….data[n], 分别与c[0]…c[k-1]比较,假定与c[i]差值最少,就标记为i;
③对于所有标记为i点,重新计算c[i]={ 所有标记为i的data[j]之和}/标记为i的个数;
④ 重复(2)(3),直到所有c[i]值的变化小于给定阈值,或达到指定的迭代次数。
例:
给定4个数据对象,每个object对应有2个features,将其分为2类:
随机选取k(此时k=2)个点作为初始的中心点。假设选A(1,1)、B(2,1)为中心点,则可得距离矩阵D0为:
——矩阵中的值d11、d12、d13、d14依次对应A与A、B、C、D的距离,d21、d22、d23、d24依次对应B与A、B、C、D的距离;
然后分别判断A、B、C、D距离A、B距离的大小;距离小置为1,距离大置为0。
得到矩阵C0:
第一类中只有A,故仍取A为中心点C1,第二类中此时有B、C、D三个,故求三者的均值作为新的中心点C2;
重复上述计算得到矩阵D1、C1:
再利用C1计算的到D2、C2;
此时可看到 C1=C2,计算结束;
即得到如下结果:
Python中实现:
# # K Means算法
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
# #传入数据集X,
def kmeans(X, k, maxIt):
# #获取X行数及列数
numPoints, numDim = X.shape
# #在X后增加1列,初始值为0
dataSet = np.zeros((numPoints, numDim+1))
# #除最后1列外其余列与X相同
dataSet[:,:-1] = X
# #随机选出k行作为初始中心点的行数,列坐标全选
centroids = dataSet[np.random.randint(numPoints,size = k), :]
# #初始化中心点,使中心点标签依次为1、2、3....k
centroids[:, -1] = range(1, k + 1)
iterations = 0
# #旧的中心点
oldCentroids = None
while not shouldStop(oldCentroids, centroids, iterations, maxIt):
print "iteration:\n", iterations
print "dataSet:\n", dataSet
print "centroids:\n", centroids
oldCentroids = np.copy(centroids)
iterations += 1
updateLabels(dataSet, centroids)
centroids = getCentroids(dataSet, k)
return dataSet
# # iterations循环的次数 ; maxIt允许循环的上限
# # 当新的中心点的值与旧的中心点的值相等时,认为收敛
def shouldStop(oldCentroid, centroids, iterations, maxIt):
if iterations > maxIt:
return True
return np.array_equal(oldCentroid, centroids)
# #传入一个矩阵和一个中心点,计算每一行与中心点的距离,
def updateLabels(dataSet, centroids):
numPoints, numDim = dataSet.shape
for i in range(0, numPoints):
# #传入数据集中的一行,对每一列的最后一行进行归类
dataSet[i, -1] = getLabelFromClossestCentroid(dataSet[i, :-1], centroids)
# #对比当前行与中心点的距离,返回距离最近的中心点的label
def getLabelFromClossestCentroid(dataSetRow, centroids):
label = centroids[0,-1]
minDist = np.linalg.norm(dataSetRow - centroids[0, :-1])
for i in range(1,centroids.shape[0]):
dist = np.linalg.norm(dataSetRow - centroids[i, :-1])
# #与最小距离对比,并记录对应的label
if dist < minDist:
minDist = dist
label = centroids[i, -1]
print "minDist:", minDist
return label
# # 输入标签和k值,计算出新的中心点
def getCentroids(dataSet, k):
# 初始化结果,行数为k
result = np.zeros((k, dataSet.shape[1]))
for i in range(1, k+1):
# #获得一个类别里的所有点
oneCluster = dataSet[dataSet[:, -1] == i, :-1]
# #axis=0 对行求平均值;赋值到从开始到导数第二列中
result[i - 1, :-1] = np.mean(oneCluster, axis = 0)
result[i - 1, -1] = i
return result
x1 = np.array([1, 1])
x2 = np.array([2, 1])
x3 = np.array([4, 3])
x4 = np.array([5, 4])
# #将序列排列成矩阵
testX = np.vstack((x1, x2, x3, x4))
result = kmeans(testX, 2, 10)
print "final result:\n", result
打印出结果为:
iteration:
0
dataSet:
[[ 1. 1. 0.]
[ 2. 1. 0.]
[ 4. 3. 0.]
[ 5. 4. 0.]]
centroids:
[[ 5. 4. 1.]
[ 1. 1. 2.]]
minDist: 0.0
minDist: 1.0
minDist: 1.41421356237
minDist: 0.0
iteration:
1
dataSet:
[[ 1. 1. 2.]
[ 2. 1. 2.]
[ 4. 3. 1.]
[ 5. 4. 1.]]
centroids:
[[ 4.5 3.5 1. ]
[ 1.5 1. 2. ]]
minDist: 0.5
minDist: 0.5
minDist: 0.707106781187
minDist: 0.707106781187
final result:
[[ 1. 1. 2.]
[ 2. 1. 2.]
[ 4. 3. 1.]
[ 5. 4. 1.]]
最终结果为CD与AB分别被分为1、2类。
直接调用kmeans()函数:
from pylab import *
from scipy.cluster.vq import *
l1 = [1., 1.]
l2 = [2., 1.]
l3 = [4., 3.]
l4 = [5., 4.]
data = vstack((l1, l2, l3, l4))
centers, _ = kmeans(data, 2)
result, _ = vq(data, centers)
print result
打印结果:
[1 1 0 0]