优点:容易实现。
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。
适用数据类型:数值型数据
K-均值算法:
先随机确定k个初始点作为质心,然后将数据集中的每个点分配到一个簇中,具体来讲为每个点找距其最近的质心,并将其分配给该质心所对应的簇。
再每个簇更新质心为该簇所有点的平均值。
import numpy as np
#加载数据
def loadDataSet(fileName):
dataMat = []
fr = open(fileName)
for line in fr:
curLine = line.strip().split('\t')
fltLine = list(map(float, curLine))
dataMat.append(fltLine)
return dataMat
#欧氏距离
def distEclud(vecA, vecB):
#这里直接用系统自带的sum方法会出错
return np.sqrt(np.sum(np.power(vecA - vecB,2)))
def randCent(dataSet, k):
n = np.shape(dataSet)[1]
centroids = np.mat(np.zeros((k,n)))
for j in range(n):
mins = min(dataSet[:,j])
ranges = float(max(dataSet[:,j]) - mins)
centroids[:,j] = mins + np.random.rand(k,1)*ranges
'''
dataSet = matrix([[ 1., 0., 0., 0., 0., 0.],
[ 0., 1., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0., 0.],
[ 0., 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 1., 0.],
[ 0., 0., 0., 0., 0., 1.]])
k = 4
||
||
\ /
\/
centroids = matrix([[ 0.88338869, 0.94654424, 0.86819242, 0.73792633, 0.88794344,
0.82862507],
[ 0.28036179, 0.73843775, 0.96017969, 0.65308858, 0.10413186,
0.13752974],
[ 0.34094064, 0.27335544, 0.87242206, 0.89623225, 0.67307911,
0.33853315],
[ 0.99018662, 0.72860503, 0.31156304, 0.29371852, 0.45232003,
0.09021325]])
'''
return centroids
#kMeans算法
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = np.shape(dataSet)[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
#只要有一条记录的簇中心变化了,则改动clusterChanged为True
#如果clusterChanged一直没变,也就是说算法已经收敛了
if clusterAssment[i,0] != minIndex:
clusterChanged = True
#记录这条记录最近的簇中心位置和距离
clusterAssment[i,:] = minIndex, minDist**2
print(centroids)
#改变簇中心位置为:
# 以这个簇中心为中心的所有记录的距离的平均值;
# centroids = mean(以这个为中心的数据集.distences)
for cent in range(k):
ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A==cent)[0]]
centroids[cent,:] = np.mean(ptsInClust, axis = 0)
return centroids, clusterAssment
#二分K-均值算法
def biKmeans(dataSet, k, distMeas=distEclud):
m = np.shape(dataSet)[0]
#用来记录每条记录的簇中心位置和距离
clusterAssment = np.mat(np.zeros((m,2)))
#将整个数据集划分为一个簇
#由于np.mat需要后面取[0]
centroids0 = np.mean(dataSet, axis=0).tolist()[0]
#记录
centList = [centroids0]
#初始化数据集的簇中心位置标记和距离
for j in range(m):
clusterAssment[j,1] = distMeas(dataSet[j,1],np.mat(centroids0))**2
while len(centList) < k:
lowestSEE = np.inf
for i in range(len(centList)):
#得到当前的簇的数据集
ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:,0].A==i)[0],:]
#用之前的kmeans算法将这个数据集划分为两个簇
#得到划分好的簇中心和数据集
centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
#误差平方和
#也就是所有记录与簇中心的距离平方和
#计算当前已经划分好的数据集的误差平方和
sseSplit = sum(splitClustAss[:,1])
#计算剩下的数据集的误差平方和
sseNotSplit = sum(clusterAssment[np.nonzero(clusterAssment[:,0].A!=i)[0],1])
#对比+替换
if (sseSplit + sseNotSplit) < lowestSEE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSEE = sseNotSplit + sseSplit
#将最优划分的两个簇的第一个簇数据集标记为原本centList的长度
bestClustAss[np.nonzero(bestClustAss[:,0].A == 1)[0], 0] = len(centList)
#第二个簇数据集标记为原本的簇的标记
bestClustAss[np.nonzero(bestClustAss[:,0].A == 0)[0], 0] = bestCentToSplit
#将原本划分的簇替代为最新划分的两个簇的前一个
centList[bestCentToSplit] = bestNewCents[0,:]
#追加一个簇为最新划分的后一个
centList.append(bestNewCents[1,:])
#将clusterAssment中bestCentToSplit簇数据集
#替换为划分完的两个簇标记的数据集
clusterAssment[np.nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] = bestClustAss
print(centList)
return centList, clusterAssment