K-means是一种聚类算法,其原理是将数据集划分为k个簇,使得每个数据点都属于最近的簇,并且簇的中心是所有数据点的平均值。这个算法是基于迭代优化的,每个迭代步骤会更新簇的中心点,直到达到收敛条件。
下面是K-means聚类算法的基本原理:
初始化:首先,选择要将数据集分成k个簇,然后随机选择k个数据点作为初始簇中心。
分配:将每个数据点分配到距离其最近的簇中心,每个数据点只能属于一个簇。
更新:根据分配的数据点更新簇中心点,这是通过计算属于每个簇的数据点的平均值来实现的。
重复:重复步骤2和3,直到簇中心点不再发生变化,或者达到预定的迭代次数。
输出:得到k个簇和每个簇的中心点。
K-means聚类算法的优缺点:
优点:
K-means算法易于实现和理解,计算效率高,适用于大规模数据集。
适用于处理高维数据,效果较好。
缺点:
需要手动指定簇的个数k,且该值的选择会影响到最终聚类效果。
对于非凸的簇结构,K-means算法的表现不佳,容易陷入局部最优解。
初始的簇中心点的随机选择可能导致不同的聚类结果。
Iris数据集:这个数据集包含3个不同品种的鸢尾花的测量数据。每个样本有4个属性(花萼长度、花萼宽度、花瓣长度和花瓣宽度)。
Wine数据集:这个数据集包含三个不同来源的葡萄酒的测量数据。每个样本有13个属性,包括酸度、酒精含量等。
Seeds数据集:这个数据集包含三种小麦种子的测量数据。每个样本有7个属性,包括面积、周长等。
Wholesale customers数据集:这个数据集包含批发客户的购买数据。每个样本有8个属性,包括生鲜、牛奶、杂货等。
Abalone数据集:这个数据集包含贝类的测量数据。每个样本有8个属性,包括性别、直径、高度等。
Diabetes数据集:这个数据集包含糖尿病患者的测量数据。每个样本有8个属性,包括血糖、BMI等。
Heart Disease数据集:这个数据集包含心脏病患者的医学数据,每个样本有14个属性,包括年龄、性别、血压等。
Credit Card数据集:这个数据集包含信用卡客户的购买数据,每个样本有17个属性,包括客户年龄、性别、账单金额等。
Breast Cancer数据集:这个数据集包含乳腺癌患者的医学数据,每个样本有30个属性,包括肿块大小、形状、质地等。
Fashion-MNIST数据集:这个数据集包含10种不同类型的时尚服装图像数据,每个样本是一张28*28像素的灰度图像,共70000个样本。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 定义K-means算法函数
def kmeans(X, k, max_iter):
# 随机选择k个数据点作为聚类中心
centroids = X[np.random.choice(len(X), k, replace=False), :]
# 初始化簇类别
labels = np.zeros(len(X))
# 迭代K-means算法
for _ in range(max_iter):
# 计算每个样本到每个聚类中心的距离
distances = np.sqrt(((X - centroids[:, np.newaxis])**2).sum(axis=2))
# 更新样本所属的簇类别
new_labels = np.argmin(distances, axis=0)
# 判断簇类别是否改变
if (new_labels == labels).all():
break
else:
labels = new_labels
# 更新聚类中心
for i in range(k):
centroids[i] = X[labels == i].mean(axis=0)
return centroids, labels
# 读取数据集
iris = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None)
# 设置数据集的列名
iris.columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
# 获取特征数据
X = iris.iloc[:, :-1].values
# 设置K值和最大迭代次数
k = 3
max_iter = 100
# 调用K-means算法函数
centroids, labels = kmeans(X, k, max_iter)
# 绘制聚类结果
colors = ['r', 'g', 'b']
for i in range(len(X)):
plt.scatter(X[i][0], X[i][1], c=colors[labels[i]], s=50)
plt.scatter(kmeans.centroids[:, 0], kmeans.centroids[:, 1], c='k', marker='x', s=100)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.show()
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 读取数据集
iris = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None)
# 设置数据集的列名
iris.columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
# 获取特征数据
X = iris.iloc[:, :-1].values
# 定义K-means算法
class KMeans:
def __init__(self, n_clusters=3, max_iter=300):
self.k = n_clusters # 聚类数量
self.max_iter = max_iter # 最大迭代次数
def fit(self, X):
# 初始化聚类中心点
self.centroids = X[np.random.choice(X.shape[0], self.k, replace=False)]
# 迭代聚类直到收敛或达到最大迭代次数
for i in range(self.max_iter):
# 计算每个样本到聚类中心点的距离
distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
# 分配样本到最近的聚类中心点
self.labels = np.argmin(distances, axis=0)
# 更新聚类中心点
for j in range(self.k):
self.centroids[j] = np.mean(X[self.labels == j], axis=0)
def predict(self, X):
# 预测新样本所属的聚类
distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
return np.argmin(distances, axis=0)
# 初始化K-means聚类器
kmeans = KMeans(n_clusters=3)
# 训练模型
kmeans.fit(X)
# 预测类别
labels = kmeans.predict(X)
# 绘制聚类结果
colors = ['r', 'g', 'b']
for i in range(len(X)):
plt.scatter(X[i][0], X[i][1], c=colors[labels[i]], s=50)
plt.scatter(kmeans.centroids[:, 0], kmeans.centroids[:, 1], c='k', marker='x', s=100)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.show()
X = iris.iloc[:, :-1].values
这行代码将Iris数据集中除了最后一列的所有数据提取出来,也就是样本的特征值,赋值给变量X。其中,:,:-1的意思是取所有行(:),但是取列时只取到最后一列的前一列(:-1),即除了最后一列的所有列,也就是样本的特征值所在的列。values属性则将Dataframe转换成NumPy数组,这样便于后续的计算和处理。
# 随机选择k个数据点作为聚类中心
centroids = X[np.random.choice(len(X), k, replace=False), :]
这行代码是在KMeans类的fit方法中的第一步,它的作用是从数据集中随机选择k个样本作为初始聚类中心。具体解释如下:
import pandas as pd
from sklearn.cluster import KMeans
# 读取数据集
iris_df = pd.read_csv('iris.csv')
# 提取特征值
X = iris_df.values[:, :-1]
# 构建KMeans模型
kmeans = KMeans(n_clusters=3, random_state=0)
# 训练模型
kmeans.fit(X)
np.random.choice(len(X), k, replace=False):np.random.choice()函数是NumPy提供的从给定的序列X中随机选择指定k数量的元素的函数,这里我们使用它从数据集X中随机选择k个样本的索引,这些索引会用来选择k个样本作为聚类中心。
np.random.choice()函数有三个参数,它们的含义如下:
第一个参数:表示从序列X中进行随机采样的样本总数。在这里,在这里,我们想要从X中随机选择k个数据点作为聚类中心,因此我们需要从X中选择样本作为聚类中心,而不是从某个特定的列或行中选择样本。X是一个NumPy数组,它的第一个维度包含了样本的数量,因此len(X)等于数据集中的样本总数,我们可以将其作为np.random.choice函数的第一个参数。
第二个参数:表示需要选择多少个元素。k是聚类的数量,需要根据问题的需要进行指定。在KMeans算法中,聚类数量是必选的一个参数,我们需要根据问题的具体情况来决定聚类的数量。通常我们可以通过试验不同的聚类数量,并评估每个聚类数量的聚类结果的质量,来选择最优的聚类数量。在本例中,我们将k设置为3,因为Iris数据集中有3个不同的品种。
第三个参数:表示是否可以重复选择。如果该参数为True,则可以多次选择同一个元素,否则只能选择一次。在这里,replace=False表示不可以重复选择,即每个选择的元素只能出现一次。这是因为在聚类中心的选择中,我们不希望选择重复的样本。
np.random.choice(len(X), k, replace=False) 返回的是一个长度为k的随机索引数组,其中每个索引值代表X中的一个样本。我们将这个随机索引数组作为X数组的第一个维度索引,从而获取到对应的k个样本的特征值。例如,如果np.random.choice(len(X), k, replace=False)返回了[0, 2, 4]这个数组,那么X[np.random.choice(len(X), k, replace=False), :]就会返回X数组中第0、2、4个样本的特征值。
也就是说:
在对数据进行聚类之前,我们需要先选择 k k k个初始的聚类中心,我们将这些初始的聚类中心存储在centroids数组中。其中,每个聚类中心是一个 n n n维向量, n n n是样本特征的数量。我们从样本中随机选择 k k k个样本,然后从它们的特征中提取出来,组成一个 k × n k \times n k×n的数组。具体地,我们可以先生成一个长度为len(X)的随机索引数组,其中每个索引值代表 X X X中的一个样本,然后从中随机选择 k k k个不重复的索引值,这样就得到了 k k k个随机选择的样本的索引数组。我们将这个随机索引数组作为X数组的第一个维度索引,使用:代表第二个维度索引(即所有的特征维度),从而获取到这 k k k个样本的特征值。最终得到的centroids数组就是这 k k k个样本的特征值组成的数组,它们被作为初始的聚类中心。
k行n列二维数组::
import numpy as np
# 随机选择k个数据点作为聚类中心
centroids = X[np.random.choice(len(X), k, replace=False), :]
k行n列二维数组:写法2
self.centroids = X[np.random.choice(X.shape[0], self.k, replace=False)]
欧式距离是一种常见的距离度量方法,用于计算两个向量之间的距离。它定义为两个向量之间的欧几里得距离,即两个向量对应元素差的平方和的平方根。
假设有两个向量 X = ( x 1 , x 2 , . . . , x n ) X = (x_1, x_2, ..., x_n) X=(x1,x2,...,xn) 和 Y = ( y 1 , y 2 , . . . , y n ) Y = (y_1, y_2, ..., y_n) Y=(y1,y2,...,yn),它们的欧式距离 d ( X , Y ) d(X, Y) d(X,Y) 可以表示为:
其中, n n n 表示向量的长度。在机器学习和数据挖掘中,欧式距离常被用于特征向量之间的相似性度量和聚类算法中。
矩阵的减法是指对应元素相减得到一个新的矩阵。若有两个矩阵 A A A 和 B B B,其中 A A A 的形状为 m × n m\times n m×n, B B B 的形状为 m × n m\times n m×n,则它们的差矩阵 C = A − B C=A-B C=A−B 的形状也为 m × n m\times n m×n。具体而言, C C C 中的每一个元素 c i , j c_{i,j} ci,j 都等于 A A A 和 B B B 中对应位置的元素相减所得,即:
其中 a i , j a_{i,j} ai,j 和 b i , j b_{i,j} bi,j 分别表示 A A A 和 B B B 中第 i i i 行第 j j j 列的元素。
需要注意的是,矩阵减法要求两个矩阵的形状相同,否则无法进行减法运算。
-本例中, X X X 和 s e l f . c e n t r o i d s self.centroids self.centroids 分别是形状为 ( m , n ) (m, n) (m,n) 和 ( k , n ) (k, n) (k,n) 的矩阵,其中 m m m 是样本数, n n n 是特征数, k k k 是聚类中心数。为了进行矩阵减法,需要对其中一个矩阵增加一个新的维度,使其形状变为 ( k , 1 , n ) (k, 1, n) (k,1,n),然后将其与另一个矩阵 ( m , n ) (m, n) (m,n) 进行减法运算。在这里,我们使用了 NumPy 的广播规则,使得在减法运算中, ( k , 1 , n ) (k, 1, n) (k,1,n) 的矩阵会被广播为 ( k , m , n ) (k, m, n) (k,m,n),然后再对应相减,得到一个新的矩阵,其形状为 ( k , m , n ) (k, m, n) (k,m,n)。
其中, X X X 是原始数据矩阵, C C C 是聚类中心矩阵, Y Y Y 是进行减法运算得到的新矩阵, [ : , n p . n e w a x i s ] [:, np.newaxis] [:,np.newaxis] 表示在矩阵 C C C 中增加一个新维度。
在K-Means算法中,每个样本点的特征向量就是由该样本在每个特征维度上的取值所组成的一维向量,假设有 n n n 个特征,则每个样本点的特征向量就是一个 n n n 维向量。
聚类中心的特征向量也是由聚类中心在每个特征维度上的取值所组成的一维向量,因为聚类中心是由一组样本点的特征向量均值计算得到的,所以聚类中心的特征向量也是一个 n n n 维向量。
举一个简单的例子说明一下
假设我们有3个样本点和2个聚类中心。每个样本点有2个特征,那么它们的特征向量就是2维的。每个聚类中心也有2个特征,因为我们的样本点有2个特征。
样本点的特征向量可以表示为 ( x 1 , x 2 ) (x_{1}, x_{2}) (x1,x2) 的形式,聚类中心的特征向量可以表示为 ( c 1 , c 2 ) (c_{1}, c_{2}) (c1,c2) 的形式。
假设我们的样本点和聚类中心的特征向量分别如下:
那么,我们可以将样本点的特征向量和聚类中心的特征向量分别放到一个矩阵中,形状为 ( 3 , 2 ) (3,2) (3,2) 和 ( 2 , 2 ) (2,2) (2,2),分别表示样本点和聚类中心的个数及其特征数量。然后,将样本点矩阵和聚类中心矩阵相减,得到一个新的矩阵,其形状为 ( 2 , 3 , 2 ) (2, 3, 2) (2,3,2),即聚类中心数、样本数、特征数。
这个新的矩阵可以表示为:
其中 ( x i 1 − c j 1 ) (x_{i1}-c_{j1}) (xi1−cj1) 表示第 i i i 个样本点的第一个特征减去第 j j j 个聚类中心的第一个特征, ( x i 2 − c j 2 ) (x_{i2}-c_{j2}) (xi2−cj2) 表示第 i i i 个样本点的第二个特征减去第 j j j 个聚类中心的第二个特征。
因此,在计算距离时,需要将每个样本点的 n n n 维特征向量与每个聚类中心的 n n n 维特征向量进行相减,得到一个新的 n n n 维向量,表示该样本点与该聚类中心在每个特征维度上的距离,最终将这些距离求平方、相加、开根号,即可得到该样本点到该聚类中心的距离。
distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
首先, X X X 和 s e l f . c e n t r o i d s self.centroids self.centroids 都是矩阵。假设 X X X 的形状为 ( m , n ) (m, n) (m,n),即有 m m m 个样本,每个样本有 n n n 个特征; s e l f . c e n t r o i d s self.centroids self.centroids 的形状为 ( k , n ) (k, n) (k,n),即有 k k k 个聚类中心,每个聚类中心也有 n n n 个特征。为了计算每个样本到聚类中心的距离,我们需要进行矩阵减法,即对 X X X 和 s e l f . c e n t r o i d s self.centroids self.centroids 中的每个元素对应相减,得到一个新的矩阵,其形状为 ( k , m , n ) (k, m, n) (k,m,n)。
在 numpy 中,如果我们想要改变数组的维度或者增加一个新的维度,可以使用 np.newaxis。它的作用是在数组的维度中增加一个新的轴。
在这个代码中,我们使用了 self.centroids[:, np.newaxis],这表示在 self.centroids 矩阵的第二个维度上增加了一个新的轴,使得其变成了一个形状为 (k, 1, n) 的三维数组。这样做的目的是为了能够与 X 矩阵 (m, n) 进行相减操作,以计算每个样本点与聚类中心的距离。
在 Numpy 中,如果两个数组的形状不完全相同,它们可能会进行广播 (broadcasting) 操作。广播操作是指将较小的数组沿着缺失的维度进行复制,使得它们形状相同,然后进行相应的计算。在本例中,形状为 (k, 1, n) 的三维数组被复制成形状为 (k, m, n) 的数组,然后才能与形状为 (m, n) 的 X 矩阵进行相减操作。这个过程就是广播操作。
numpy 广播的规则是,两个数组的形状在每个维度上要么相同,要么其中一个为1,才能进行广播。在对形状为 (k, n) 的数组进行广播时,只能在最后一个维度上进行广播,而不能在第二个维度上进行广播,因为第二个维度的大小是 m,不是 1。因此需要添加一个新维度才能进行正确的广播。
具体来说,当进行两个数组相减时,Numpy 会从最后一个维度开始比较它们的形状,如果相同或其中一个为1,则认为是可以广播的。如果两个数组的形状不同,并且它们在某些维度上不兼容,则会在这些维度上进行复制,直到形状匹配为止。因此,形状为 (k, 1, n) 的三维数组被复制成形状为 (k, m, n) 的数组,这样就能与形状为 (m, n) 的 X 矩阵进行相减操作了。
对样本点和聚类中心点进行广播计算,每个样本点都与所有聚类中心进行了减法操作。
计算每个样本点与每个聚类中心之间的差的平方,并将结果保存在一个形状为 (k, m, n) 的三维数组中,其中第一个维度表示聚类中心,第二个维度表示样本点,第三个维度表示特征值。
对第三个维度上的特征值进行求和,得到一个形状为 (k, m) 的二维数组,其中每个元素表示一个样本点与一个聚类中心之间的差的平方的和。
对所有元素取平方根,得到一个形状为 (k, m) 的距离矩阵,其中每个元素表示一个样本点与一个聚类中心之间的欧式距离。
举个例子来讲解。
假设有一个矩阵 X,其形状为 (3, 2),也就是有 3 个样本点,每个样本点有 2 个特征,如下所示:
X = [[1, 2],
[3, 4],
[5, 6]]
现在我们有一个聚类中心数组,其形状为 (2,),也就是有 2 个特征值,如下所示:
centroids = [0, 1]
如果我们想将 centroids 广播到 X 上进行矩阵减法,那么需要将其形状扩展成和 X 相同的形状。由于 X 的形状为 (3, 2),因此需要将 centroids 扩展成形状为 (1, 2) 或者 (2, 1) 或者 (1, 1, 2) 或者 (1, 2, 1) 或者 (2, 1, 1) 或者 (2, 1, 2) 或者 (1, 2, 2) 等等,只要在第二个维度上有一个 2 就可以。这里我们选择形状为 (1, 2) 的数组。
那么扩展后的 centroids 数组为:
centroids_new = [[0, 1]]
减法的结果为:
result = [[ 1, 1],
[ 3, 3],
[ 5, 5]]
可以看到,这里得到的 result 和我们之前的例子中得到的 (2, 2) 矩阵相同。这是因为对于矩阵减法来说,广播的形状只需要在对应的维度上相同即可,其余维度的长度可以自动扩展或者缩减。
在 Numpy 中,可以在任何一个维度上扩展数组的形状,只要符合广播规则即可。在这里,我们需要将形状为 (2,) 的数组扩展为和 X 相同的形状,因此需要在某些维度上进行扩展,以满足广播规则。
具体而言,我们需要将形状为 (2,) 的数组扩展成形状为 (m, 2) 或者 (1, 2) 或者 (k, 2) 或者 (1, k, 2) 或者 (m, k, 2) 等等,只要这些形状满足以下条件:
扩展后的形状中,所有维度的大小都必须相等或者其中一个维度的大小为 1。
对于所有维度大小大于 1 的数组,它们的大小必须相等。
例如,(m, 2) 表示扩展成形状为 (m, 2) 的数组,其中第一个维度的大小与 X 相同,第二个维度的大小为 2。这个形状满足广播规则,因为第一个维度的大小相同,第二个维度的大小为 2,可以匹配 X 的第二个维度的大小为 2。
同样地,(1, 2) 表示扩展成形状为 (1, 2) 的数组,其中第一个维度的大小为 1,第二个维度的大小为 2。这个形状也满足广播规则,因为第一个维度的大小为 1,可以匹配 X 的任意维度的大小为 1 或者 X 的第一个维度的大小为 m。
其他形状也是类似的,只要符合广播规则,就可以扩展为相应的形状。
对于一个形状为 (k, n) 的数组,在进行广播操作时,可以在任何维度上进行广播,只要在其他维度上的大小能够匹配。因此,可以将这个数组扩展成任何一个形状为 (a, b, c, …, k, …, z) 的数组,只要在维度 k 上进行广播即可。
具体来说,以 (k, n) 扩展成形状为 (1, 2) 的数组为例,可以在第二个维度上进行广播,也就是扩展成 (k, 1, n) 的形状,然后再在第二个维度上进行广播,得到形状为 (1, 2) 的数组。同理,也可以在其他维度上进行广播,得到其他形状的数组。
综上所述,扩展数组的形状并不唯一,只要在需要广播的维度上进行扩展即可。
self.mins = np.argmin(distances, axis=0)
for j in range(self.k):
self.centroids[j] = np.mean(X[self.mins== j], axis=0)
在KMeans算法中,每个聚类中心点的位置由其所关联的所有样本的平均值来确定。因此,对于每个聚类中心点,我们需要找到其关联的所有样本,并计算这些样本的平均值。具体地,对于第 j j j 个聚类中心点,我们可以使用以下代码来计算其关联的所有样本的平均值:
X[self.mins == j] # 选出所有属于第 j 个聚类的样本
np.mean(X[self.mins == j], axis=0) # 计算这些样本的平均值
X[self.mins== j] 表示选出所有属于第 j j j 个聚类的样本,这里使用了布尔索引,self.labels == j 会返回一个布尔数组,其中第 i i i 个元素表示样本 i i i 是否属于第 j j j 个聚类。然后,使用 np.mean 函数对这些样本沿第0个轴(即对样本数量进行平均)计算平均值,即可得到第 j j j 个聚类中心点的新位置。
因此 ,在循环中,对于每个聚类中心点,我们都可以使用上述代码来计算其新位置,并将其更新到 self.centroids[j] 中。
聚类算法的目标是将相似的样本分配到同一聚类中,并使聚类内的样本尽可能接近聚类中心点。因此,在更新聚类中心点时,我们可以计算该聚类中所有样本的平均值作为新的聚类中心点,因为该平均值可以代表聚类中所有样本的特征。当我们将新的聚类中心点用于下一轮迭代时,它将更好地代表该聚类中的所有样本,从而更好地将相似的样本分配到同一聚类中,进一步优化聚类效果。
对样本数量进行平均的结果是该聚类所有样本在每个特征上的平均值,这个平均值作为该聚类的新中心点。
例如,假设有一个二维空间中的聚类,其中包含了三个样本点 (1, 2),(3, 4) 和 (5, 6)。那么这三个样本在第一个特征上的平均值为 (1+3+5)/3 = 3,在第二个特征上的平均值为 (2+4+6)/3 = 4。因此,该聚类的新中心点为 (3, 4)。
def predict(self, X):
# 预测新样本所属的聚类
distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
return np.argmin(distances, axis=0)
predict() 函数用于对新的未标记数据进行分类,即将其分配给最近的聚类中心。在聚类算法中,fit()
函数是用来拟合模型的,即通过训练数据学习聚类中心并对数据进行聚类。而 predict()
函数则用于对新数据进行预测。在预测时,使用已经训练好的聚类中心对新数据进行分类。因此,当我们有新的未知数据需要分类时,就可以使用
predict() 函数对其进行分类。
predict 函数是用来对新的样本进行聚类预测的。它接受一个新的样本数据集
X,并返回一个数组,其中包含每个新样本所属的聚类标签。实现过程与 fit
函数中计算样本到聚类中心点距离的方式相同,只不过这里不需要更新聚类中心点,而是根据距离找到最近的聚类中心点并返回其对应的聚类标签。具体来说,代码中distances 变量计算了新样本 X 到聚类中心点的距离,然后 np.argmin(distances, axis=0)找到每个新样本距离哪个聚类中心点最近,从而得到每个新样本所属的聚类标签。
在使用 predict 方法时,不需要再次更新聚类中心点,因为聚类中心点已经在 fit 方法中被计算出来并且保存在 self.centroids 中了。在预测新的样本所属的聚类时,只需要计算新样本与已有聚类中心点之间的距离,并返回距离最近的聚类的标签即可。因此,在 predict 方法中不需要进行聚类中心点的更新。
# 绘制聚类结果
"定义了一个包含三种颜色的列表,用于后续的绘图。"
colors = ['r', 'g', 'b']
"""
循环遍历所有样本,对每个样本绘制一个散点图,其中x轴为样本的第一个特征值,y轴为样本的第二个特征值,点的颜色由该样本所属的聚类决定,s参数控制散点的大小。
"""
for i in range(len(X)):
plt.scatter(X[i][0], X[i][1], c=colors[labels[i]], s=50)
"绘制聚类中心点的散点图,用黑色叉号标记,s参数控制叉号的大小。"
plt.scatter(kmeans.centroids[:, 0], kmeans.centroids[:, 1], c='k', marker='x', s=100)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.show()