K-means聚类是一种无监督的学习算法,它根据每一个点到一个中心点的欧氏距离对数据进行分组,这个中心点称为中心点。中心点是由处于同一聚类中的所有点的平均值定义的。该算法首先选择随机点作为中心点,然后反复调整它们,直到完全收敛:
在使用K-means时,需要记住的一个重要事情是,聚类的数量是一个超参数,它将在运行模型之前被定义。
K-means可以用Scikit-Learn实现,只需3行代码。Scikit-learn也已经有一个中心点优化方法可用,即kmeans++,它可以帮助模型更快地收敛。
为了应用K-均值聚类算法,让我们加载Palmer Penguins数据集,选择将被聚类的列,并使用Seaborn绘制一个带有彩色编码聚类的散点图。
注意:你可以从这个链接下载数据集。
让我们导入库并加载企鹅数据集,将其修剪为所选的列,并放弃有缺失数据的行(只有2行):
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
df = pd.read_csv('penguins.csv')
print(df.shape) # (344, 9)
df = df[['bill_length_mm', 'flipper_length_mm']]
df = df.dropna(axis=0)
我们可以使用肘部方法来指示我们的数据的集群。它包括解释一个具有肘部形状的线图。群集的数量是肘部弯曲的地方。该图的X轴是聚类的数量,Y轴是每个聚类数量的群内平方和(WCSS):
wcss = []
for i in range(1, 11):
clustering = KMeans(n_clusters=i, init='k-means++', random_state=42)
clustering.fit(df)
wcss.append(clustering.inertia_)
ks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sns.lineplot(x = ks, y = wcss);

肘部方法表明我们的数据有2个聚类。让我们来绘制聚类前后的数据。
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(15,5))
sns.scatterplot(ax=axes[0], data=df, x='bill_length_mm', y='flipper_length_mm').set_title('Without clustering')
sns.scatterplot(ax=axes[1], data=df, x='bill_length_mm', y='flipper_length_mm', hue=clustering.labels_).set_title('Using the elbow method');

这个例子说明了肘部法在用于选择聚类数量时只是一个参考。我们已经知道我们的数据集中有3种企鹅,但是如果我们用Elbow方法来确定它们的数量,2个聚类将是我们的结果。
由于K-means对数据差异很敏感,让我们看一下我们要聚类的列的描述性统计:
df.describe().T # T is to transpose the table and make it easier to read
这样的结果是:
count mean std min 25% 50% 75% max
bill_length_mm 342.0 43.921930 5.459584 32.1 39.225 44.45 48.5 59.6
flipper_length_mm 342.0 200.915205 14.061714 172.0 190.000 197.00 213.0 231.0
注意,平均值与标准差(std)相差甚远,这表明方差很大。让我们尝试通过用标准缩放器对数据进行缩放来减少它:
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
scaled = ss.fit_transform(df)
现在,让我们对缩放后的数据重复肘部方法的过程:
wcss_sc = []
for i in range(1, 11):
clustering_sc = KMeans(n_clusters=i, init='k-means++', random_state=42)
clustering_sc.fit(scaled)
wcss_sc.append(clustering_sc.inertia_)
ks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sns.lineplot(x = ks, y = wcss_sc);

这一次,建议的聚类数量是3个。 我们可以将数据与聚类标签一起再次绘制,与之前的两张图进行比较。
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(15,5))
sns.scatterplot(ax=axes[0], data=df, x='bill_length_mm', y='flipper_length_mm').set_title('Without cliustering')
sns.scatterplot(ax=axes[1], data=df, x='bill_length_mm', y='flipper_length_mm', hue=clustering.labels_).set_title('With the Elbow method')
sns.scatterplot(ax=axes[2], data=df, x='bill_length_mm', y='flipper_length_mm', hue=clustering_sc.labels_).set_title('With the Elbow method and scaled data');

在使用K-means聚类法时,你需要预先确定聚类的数量。正如我们所看到的,当使用一种方法来选择我们的k个聚类数量时,其结果只是一个建议,而且会受到数据中差异量的影响。重要的是,在聚类时要进行深入的分析,并生成一个以上的具有不同_k_s的模型。
如果事先没有说明数据中有多少个聚类,那就把它可视化,测试并解释它,看看聚类结果是否有意义。如果不是,再聚类一次。另外,看一下不止一个指标,并实例化不同的聚类模型--对于K-means,看一下剪影分数,也许还有层次聚类,看看结果是否保持不变。