聚类分析学习笔记(三)
此笔记参考了数据挖掘导论、周志华的机器学习以及机器学习实战三本书
4.二分K-means算法
4.1 基本概念
二分K-means算法是对K-means算法的一种改进,它基于一种简单的思想:为了得到k个簇,将所有点的集合分裂成两个簇,从这些簇中选取一个继续分裂,如此下去,直到产生k个簇。
度量聚类质量的目标函数:误差的平方和SSE,即计算每个样本到所在簇的质心的距离的平方和,SSE越小越好。
SSE=∑i=1k∑x∈Cidist(μi,x)2
4.2 二分K-means算法过程
初始化簇表,将所有数据点看成一个簇
repeat
从簇表中取出每一个簇
for i=1 to 此时簇的个数 do
使用K-means算法,二分选定的簇
end for
从二分试验中选取具有最小SSE的两个簇
删除原簇,将由原簇划分的两个簇添加进簇表中
until 簇表中包含k个簇
4.3 K-means算法和二分K-means算法源码
下面是K-means算法和二分K-means算法的python源码,参考了机器学习实战一书,计算距离采用的是欧式距离,当然也可以用其他参数来度量(例如明可夫斯基距离,曼哈顿距离以及person相关系数等等),除了实现欧式距离,下面的代码还实现了person相关系数的求取方法。
# -*- coding:utf-8 -*-
import numpy as np
import math
# 欧式距离
def oushi_distance(vec1, vec2):
distance = math.sqrt(sum([math.pow(vec1[0, i]-vec2[0, i], 2) for i in range(0, np.shape(vec2)[1])]))
return distance
# person相关
def person_distace(vec1, vec2):
n = len(vec1)
sum1_sq = sum([math.pow(vec1[0, i], 2) for i in range(0, np.shape(vec2)[1])])
sum2_sq = sum([math.pow(vec2[0, i], 2) for i in range(0, np.shape(vec2)[1])])
sum1 = np.sum(vec1)
sum2 = np.sum(vec2)
psum = sum([vec1[0, i]*vec2[0, i] for i in range(0, np.shape(vec2)[1])])
num = psum-(sum1*sum2/n)
den = math.sqrt((sum1_sq-math.pow(sum1, 2)/n)*(sum2_sq-math.pow(sum2, 2)/n))
distance = num/den
return distance
# 初始化质心
def randCent(dataSet, k):
n = np.shape(dataSet)[1] # 特征数
centers = np.mat(np.zeros((k, n))) # k*n(k个中心向量)
for j in range(n):
min_j = min(dataSet[:, j])
range_j = float(max(dataSet[:, j]) - min_j)
centers[:,j] = np.mat(min_j + range_j * np.random.rand(k, 1))
return centers
def kmean(dataSet, k):
m = np.shape(dataSet)[0] # 样本数
cluster= np.mat(np.zeros((m, 2))) # 存放样本所属簇以及最短距离
centers = randCent(dataSet, k) # k*n(k个中心向量)
clusterChange = True # 判断聚类停止的条件
while clusterChange:
clusterChange = False
minDist=np.inf
minIndex = -1
for i in range(m):
for j in range(k):
dist_ji = oushi_distance(centers[j, :], dataSet[i, :])
if dist_ji<minDist:
minDist=dist_ji**2
minIndex=j
if cluster[i,0]!=minIndex:
clusterChange = True
cluster[i, 0]=minIndex
cluster[i, 1]=minDist
# 更新中心点,centers(k*n)
for center in range(k):
Cluster_k = dataSet[np.nonzero(cluster[:, 0].A == center)[0]]
centers[center, :]=np.mean(Cluster_k, axis=0) # 按列相加
return centers, cluster
def bikeanms(dataSet, k):
m=np.shape(dataSet)[0]
cluster = np.mat(np.zeros((m, 2))) # 存放样本属于哪个簇以及最短距离
center0=np.mean(dataSet, axis=0).tolist()[0] # 求每列的平均
centerlist=[center0] # 将第一个中心放入centerlist中
for j in range(m):
cluster[j, 1]=(oushi_distance(np.mat(center0), dataSet[j, :]))**2
while(len(centerlist)<k):
minSSE=np.inf
for i in range(len(centerlist)):
CurrCluster = dataSet[np.nonzero(cluster[:, 0].A == i)[0], :] # 第i个簇里所有样本
splitCenter, splitCluster=kmean(CurrCluster, 2)
sseSplit = sum(splitCluster[:, 1])
sseNotSplit = sum(cluster[np.nonzero(cluster[:, 0].A != i)[0], 1])
if (sseSplit + sseNotSplit) < minSSE:
bestsplitCenter=i
bestNewCenter = splitCenter
bestNewCluster = splitCluster.copy()
minSSE = sseSplit + sseNotSplit
bestNewCluster[np.nonzero(bestNewCluster[:, 0].A == 1)[0], 0] = len(centerlist)
bestNewCluster[np.nonzero(bestNewCluster[:, 0].A == 0)[0], 0] = bestsplitCenter
centerlist[bestsplitCenter] = bestNewCenter[0, :].tolist()[0]
centerlist.append(bestNewCenter[1, :].tolist()[0])
cluster[np.nonzero(cluster[:, 0].A == bestsplitCenter)[0], :] = bestNewCluster
return np.mat(centerlist), cluster