# KMeans 聚类算法

# 一、数据集

sklearnsklearn 库自带了一些数据集,本次KmeanK-mean 算法实践就用的sklean.datasetssklean.datasets 的鸢尾花数据集,下面的这张表记录了一些不需要下载的数据集

导入数据的函数简介任务数据规模
load_boston()加载和返回一个 boston 房屋价格的数据集回归506*13
load_iris()加载和返回一个鸢尾花数据集分类150*4
load_diabetes()加载和返回一个糖尿病数据集回归442*10
load_digits()加载和返回一个手写字数据集分类1797*64
load_linnerud()加载和返回一个健身数据集多分类20

# 二、手写实现 K-Means 算法

# 1、导入前置库

import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans 
from sklearn import datasets

# 2、导入数据集

# 直接从 sklearn 中获取数据集
iris = datasets.load_iris()
X = iris.data[:, :4]    # 表示我们取特征空间中的 4 个维度
print(X.shape)
-> output = (150,4)

# 3、取前两个维度(萼片长度、萼片宽度), 绘制数据分布图

plt.scatter(X[:, 0], X[:, 1], c="red", marker='o', label='see')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend(loc=2)
plt.show()

# 4、具体算法代码

# 计算欧式距离
def distEclud(x,y):
	return np.sqrt(np.sum((x-y)**2))
# 为给定数据集构建一个包含 K 个随机质心 centroids 的集合
def randCent(dataSet,k):
	m,n = dataSet.shape # m=150,n=4
	centroids = np.zeros((k,n)) # k*4
	for i in range(k): # 执行 4 次
		index = int(np.random.uniform(0,m)) # 产生 0 到 150 的随机数 (即在数据集中随机挑选一个向量作为质心的初始值)
		centroids[i,:] = dataSet[index,:] # 把对应的四个维度传给质心的集合
	return centroids
# k 均值聚类算法
def KMeans(dataSet,k):
	m = np.shape(dataSet)[0] # 行数 150
	# 第一列存每个样本属于哪一个簇
	# 第二列存每个样本到簇中心的误差
	clusterAssment = np.mat(np.zeros((m,2))) # mat () 创建 150*2 的矩阵
	clusterChange = True
	
	#1、初始化质心 centroids
	centroids = randCent(dataSet,k) # k*4
	while clusterChange:
		# 样本所属簇不再更新时停止迭代
		clusterChange = False
		#遍历所有的样本 (行数 150)
		for i in range(m):
			minDist = 1000000.0
			minIndex = -1
			# 遍历所有质心
			# 2、找出最近的质心
			for j in range(k):
				# 计算每个样本到 k 个质心的距离
				distance = disEclud(centroids[j,:],dataSet[i,:])
				if distance < minDist:
					minDist = distance
					minIndex = j
			# 3、更新该行样本的所属的簇
			if clusterAssment[i,0] != minIndex:
				clusterChange = True
				clusterAssment[i,:] = minIndex,minDist**2
		# 更新质心
		for j in range(k):
			# np.nonzero (x) 返回值不为零的元素的下标,它的返回值是一个长度为 x.ndim (x 的轴数) 的元组
            # 元组的每个元素都是一个整数数组,其值为非零元素的下标在对应轴上的值。
            # 矩阵名.A 代表将 矩阵转化为 array 数组类型
            
            # 这里取矩阵 clusterAssment 所有行的第一列,转为一个 array 数组,与 j(簇类标签值)比较,返回 true or false
            # 通过 np.nonzero 产生一个 array,其中是对应簇类所有的点的下标值(x 个)
            # 再用这些下标值求出 dataSet 数据集中的对应行,保存为 pointsInCluster(x*4)
            pointsInCluster = dataSet[np.nonzero(clusterAssment[:,0].A == j)[0]]  # 获取对应簇类所有的点(x*4)
            centroids[j,:] = np.mean(pointsInCluster,axis=0)   # 求均值,产生新的质心
            # axis=0,那么输出是 1 行 4 列,求的是 pointsInCluster 每一列的平均值,即 axis 是几,那就表明哪一维度被压缩成 1
	print("cluster complete")
	return centroids,clusterAssment
def draw(data,center,assment):
    length=len(center)
    fig=plt.figure
    data1=data[np.nonzero(assment[:,0].A == 0)[0]]
    data2=data[np.nonzero(assment[:,0].A == 1)[0]]
    data3=data[np.nonzero(assment[:,0].A == 2)[0]]
    # 选取前两个维度绘制原始数据的散点图
    plt.scatter(data1[:,0],data1[:,1],c="red",marker='o',label='label0')
    plt.scatter(data2[:,0],data2[:,1],c="green", marker='*', label='label1')
    plt.scatter(data3[:,0],data3[:,1],c="blue", marker='+', label='label2')
    # 绘制簇的质心点
    for i in range(length):
        plt.annotate('center',xy=(center[i,0],center[i,1]),xytext=\
        (center[i,0]+1,center[i,1]+1),arrowprops=dict(facecolor='yellow'))
        #  plt.annotate('center',xy=(center[i,0],center[i,1]),xytext=\
        # (center[i,0]+1,center[i,1]+1),arrowprops=dict(facecolor='red'))
    plt.show()
    
    # 选取后两个维度绘制原始数据的散点图
    plt.scatter(data1[:,2],data1[:,3],c="red",marker='o',label='label0')
    plt.scatter(data2[:,2],data2[:,3],c="green", marker='*', label='label1')
    plt.scatter(data3[:,2],data3[:,3],c="blue", marker='+', label='label2')
    # 绘制簇的质心点
    for i in range(length):
        plt.annotate('center',xy=(center[i,2],center[i,3]),xytext=\
        (center[i,2]+1,center[i,3]+1),arrowprops=dict(facecolor='yellow'))
    plt.show()
dataSet = X
k = 3
centroids,clusterAssment = KMeans(dataSet,k)
draw(dataSet,centroids,clusterAssment)

# 5、结果截图:

# 三、利用 sklearn 的库进行 K-Means 算法

前置库和数据的导入和手写板是一样的,区别在于后面具体算法实现

# 1、具体算法代码

def Model(n_clusters):
    estimator = KMeans(n_clusters) # 构造聚类器
    return estimator
def train(estimator):
    estimator.fit(X)
# 训练模型
estimator=Model(3)
train(estimator)
# 画出分类结果
label_pred = estimator.labels_ #获取聚类标签
# 绘制 k-means 结果
x0 = X[label_pred == 0]
x1 = X[label_pred == 1]
x2 = X[label_pred == 2]
plt.scatter(x0[:,0],x0[:,1],c='red',marker='o',label='label0')
plt.scatter(x1[:,0],x1[:,1],c='green',marker='*',label='label1')
plt.scatter(x2[:,0],x2[:,1],c='blue',marker='+',label='label2')
plt.xlabel('seqal length')
plt.ylabel('seqal length')
plt.legend(loc=2)
plt.show()

# 2、结果截图:

# 四、不同 k 值结果对比

KK 值为 2:

KK 值为 3:

KK 值为 4: