# KMeans 聚类算法
# 一、数据集
库自带了一些数据集,本次 算法实践就用的 的鸢尾花数据集,下面的这张表记录了一些不需要下载的数据集
导入数据的函数 | 简介 | 任务 | 数据规模 |
---|---|---|---|
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 值结果对比
值为 2:
值为 3:
值为 4: