二分K-Means(Bisecting K-Means)是一种改进的聚类算法
,它是K-Means算法的一种变体。
与传统的K-Means算法一次性生成K个聚类不同,二分K-Means通过递归
地将一个聚类分裂成两个
,直到达到所需的聚类数目。
二分K-Means算法流程:
- 初始化:将所有数据点视为
一个簇
。 - 分裂:从当前的簇中
选择一个簇进行分裂
。选择的标准通常是选择那些分裂后可以最大程度减少误差平方和(SSE)的簇。
- 应用K-Means:对
选定的簇
应用标准的K-Means算法,将其分成两个簇。
- 评估:检查是否达到了所需的聚类数目。如果没有,返回步骤2;如果达到了,算法结束。
涉及到的公式:
在二分K-Means中,最重要的公式是计算误差平方和(SSE)
,它用于决定哪个簇应该被分裂
以及评估聚类的效果。SSE的公式如下:
S S E = ∑ i = 1 k ∑ x ∈ C i ( x − μ i ) 2 SSE = \sum_{i=1}^{k} \sum_{x \in C_i} (x - \mu_i)^2 SSE=i=1∑kx∈Ci∑(x−μi)2
这里:
-
k
k
k 是
聚类的数量;
-
C
i
C_i
Ci 是第
i
i
i 个
聚类中的所有数据点;
-
x
x
x 是聚类
C
i
C_i
Ci 中的
某个数据点;
-
μ
i
\mu_i
μi 是聚类
C
i
C_i
Ci 的
质心
,计算方式为 μ i = 1 ∣ C i ∣ ∑ x ∈ C i x \mu_i = \frac{1}{|C_i|} \sum_{x \in C_i} x μi=∣Ci∣1∑x∈Cix,其中 ∣ C i ∣ |C_i| ∣Ci∣ 表示聚类 C i C_i Ci 中数据点的数量。
对公式中每个字符的解释:
- ∑ \sum ∑:求和符号,表示对一系列项进行累加。
- i i i:聚类的索引。
- k k k:总的聚类数目。
- x x x:数据集中的一点。
- C i C_i Ci:第 i i i个聚类的所有数据点组成的集合。
- μ i \mu_i μi:第 i i i个聚类的质心。
-
(
x
−
μ
i
)
2
(x - \mu_i)^2
(x−μi)2:数据点
x
x
x与质心
μ
i
\mu_i
μi之间的
欧氏距离的平方。
- ∣ C i ∣ |C_i| ∣Ci∣:聚类 C i C_i Ci中数据点的数量。
二分K-Means的目标是通过最小化SSE来获得更加紧凑和分离良好的聚类。
python代码
# 导入所需的库
from sklearn.cluster import KMeans
import numpy as np
# 定义数据点,这是一个二维数组,其中每个子数组代表一个数据点的坐标
data_points = np.array([[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]])
# 定义二分K-Means算法的函数
def bisecting_kmeans(data, k):
# 初始化,将所有数据点视为一个簇
clusters = [data]
# 当前簇的数量小于目标簇的数量时,继续循环
while len(clusters) < k:
# 初始化最大SSE为0,用于记录当前找到的最大SSE值
max_sse = 0
# 初始化要分裂的簇的索引为0
index_to_split = 0
# 遍历当前的所有簇
for i, cluster in enumerate(clusters):
# 对当前簇使用KMeans算法,将其分为两个簇
kmeans = KMeans(n_clusters=2).fit(cluster)
# 计算SSE,即簇内所有点到其所属簇质心距离的平方和
# 使用np.linalg.norm计算每个点到最近质心的欧氏距离,然后平方求和
sse = np.sum(np.linalg.norm(cluster - kmeans.cluster_centers_[kmeans.labels_], axis=1) ** 2)
# 如果当前簇的SSE大于已知的最大SSE,则更新最大SSE和要分裂的簇的索引
if sse > max_sse:
max_sse = sse
index_to_split = i
# 根据找到的具有最大SSE的簇,使用KMeans算法将其分为两个簇
split_cluster = clusters.pop(index_to_split)
kmeans = KMeans(n_clusters=2).fit(split_cluster)
# 将分裂后的簇按标签排序后分割成两个簇
labels_sorted = np.argsort(kmeans.labels_)
# 使用切片操作,根据标签排序后的结果,将split_cluster分为cluster1和cluster2
cluster1 = split_cluster[labels_sorted[:len(labels_sorted)//2]]
cluster2 = split_cluster[labels_sorted[len(labels_sorted)//2:]]
# 将新生成的两个簇添加到clusters列表中
clusters.append(cluster1)
clusters.append(cluster2)
# 当达到指定的簇数k时,返回当前的簇列表
return clusters
# 设置聚类的数量
k = 2
# 调用bisecting_kmeans函数,传入数据点和聚类数量
clusters = bisecting_kmeans(data_points, k)
# 输出最终的聚类结果
for i, cluster in enumerate(clusters):
print(f"Cluster {i+1}:")
print(cluster)