#谱聚类算法实现
#1、计算距离矩阵(欧氏距离,作为相似度矩阵)
#2、利用KNN计算邻接矩阵A
#3、由邻接矩阵计算都矩阵D和拉普拉斯矩阵L
#4、标准化拉普拉斯矩阵
#5、对拉普拉斯矩阵进行特征值分解得到特征向量
#6、对特征向量进行K-means聚类
#7、得到分类结果
import numpy as np
#距离矩阵的计算
def euclidDistance(x1, x2, sqrt_flag=False):
res = np.sum((x1-x2)**2)
if sqrt_flag:
res = np.sqrt(res)
return res
def calEuclidDistanceMatrix(X):
#X是(500,2)的数据维度
X = np.array(X)
#初始化一个相似度矩阵[len(x),len(x)]
S = np.zeros((len(X), len(X)))
#计算相似度矩阵
for i in range(len(X)):
for j in range(i+1, len(X)):
S[i][j] = 1.0 * euclidDistance(X[i], X[j])
S[j][i] = S[i][j]
return S
#邻接矩阵
#传入的参数是相似度矩阵Similarity和K
def myKNN(S, k, sigma=1.0):
N = len(S)
A = np.zeros((N,N))
# print(S[0,:15])
for i in range(N):
#S[i]为相似都矩阵中的第i行的所有数据 S[i]长度为500
dist_with_index = zip(S[i], range(15))#每一个值赋予一个编号,然后打包,相当于给节点加上编号
#由小到大排序,按照x[0],也就是zip中的第一个值,即相似度值(相似度值,编号)
dist_with_index = sorted(dist_with_index, key=lambda x:x[0])
#选出K个与节点i最相关的节点,相似度矩阵中值越小,表示两点之间距离越近,相似度越大
#所以是由小到大排序,再选择
#得到外循环节点i的邻居节点集合
neighbours_id = [dist_with_index[m][1] for m in range(k+1)] # xi's k nearest neighbours
# print(neighbours_id)
# print(dist_with_index)
# print(len(dist_with_index))
# if i==0:
# break
#用高斯核函数计算邻接矩阵的值
for j in neighbours_id: # xj is xi's neighbour
A[i][j] = np.exp(-S[i][j]/2/sigma/sigma)
A[j][i] = A[i][j] # mutually
return A
#数据加载
from sklearn import datasets
def genTwoCircles(n_samples=1000):
#数据和标签 (500,2)和500的维度
X, y = datasets.make_circles(n_samples, factor=0.5, noise=0.05)
return X, y
#拉普拉斯矩阵标准化
def calLaplacianMatrix(adjacentMatrix):
# 计算邻接矩阵的度
degreeMatrix = np.sum(adjacentMatrix, axis=1)#axis=1逐行相加 axis=0逐列相加
# 将邻接矩阵转化为对角矩阵,再减去邻接矩阵就是拉普拉斯矩阵 L=D-A
laplacianMatrix = np.diag(degreeMatrix) - adjacentMatrix
# normailze拉普拉斯矩阵归一化操作
# D^(-1/2) L D^(-1/2)
sqrtDegreeMatrix = np.diag(1.0 / (degreeMatrix ** (0.5)))
#三部分做矩阵乘法得到归一化的拉普拉斯矩阵
return np.dot(np.dot(sqrtDegreeMatrix, laplacianMatrix), sqrtDegreeMatrix)
#画图
from matplotlib import pyplot as plt
from itertools import cycle, islice
#参数data,y_sp为特征向量聚类的标签,y_km为原始数据聚类的标签
def plot(X, y_sp, y_km):
colors = np.array(list(islice(cycle(['#377eb8', '#ff7f00', '#4daf4a',
'#f781bf', '#a65628', '#984ea3',
'#999999', '#e41a1c', '#dede00']),
int(max(y_km) + 1))))
plt.subplot(121)
plt.scatter(X[:,0], X[:,1], s=10, color=colors[y_sp])
plt.title("Spectral Clustering")
plt.subplot(122)
plt.scatter(X[:,0], X[:,1], s=10, color=colors[y_km])
plt.title("Kmeans Clustering")
plt.show()
def main():
import sys
sys.path.append("..")
from sklearn.cluster import KMeans
np.random.seed(1)
#data维度是(500,2),500个样本,本个样本特征维度是2
#label是(500),表示每一个样本的标签
data, label = genTwoCircles(n_samples=500)
#计算相似度矩阵 (500,500)的维度
Similarity = calEuclidDistanceMatrix(data)
#计算邻接矩阵 k=10为每个节点选择最相似的10个邻居
Adjacent = myKNN(Similarity, k=10)#维度是(500,500)
#计算归一化的拉普拉斯矩阵
Laplacian = calLaplacianMatrix(Adjacent)
#计算拉普拉斯矩阵的特征值和特征向量 x:500 V(500,500)500个节点,每个节点特征为500维
x, V = np.linalg.eig(Laplacian)
#将特征值由小到大排序 对应特征向量
x = zip(x, range(len(x)))
x = sorted(x, key=lambda x: x[0])
#取出特征向量来,因为特征向量是按列排的,所以要转置,用vstack推叠起来
H = np.vstack([V[:, i] for (v, i) in x[:500]]).T
#送入kmeans中聚类 传入的是分解后的特征向量
sp_kmeans = KMeans(n_clusters=2).fit(H)
#原始数据进行kmeans聚类
pure_kmeans = KMeans(n_clusters=2).fit(data)
#传入plot函数作图,原始数据,两种聚类的标签结果
plot(data, sp_kmeans.labels_, pure_kmeans.labels_)
if __name__ == '__main__':
main()