第11章:聚类分析
聚类分析是无监督学习的重要分支,它在没有标签信息的情况下,根据数据的相似性将样本分组。本章将详细介绍各种聚类算法的原理、实现和应用。
11.1 什么是聚类分析?
聚类分析是一种探索性数据分析技术,目标是将相似的数据点归为一类,不相似的数据点分为不同类。与分类不同,聚类是无监督学习,不需要预先知道类别标签。
11.1.1 聚类的目标
- 组内相似性最大化:同一簇内的样本尽可能相似
- 组间相似性最小化:不同簇间的样本尽可能不同
- 发现数据结构:揭示数据中的隐藏模式
11.1.2 聚类的应用
- 市场细分:根据消费行为对客户分群
- 图像分割:将图像分割成不同区域
- 基因分析:根据表达模式对基因分类
- 推荐系统:发现用户群体和物品类别
- 异常检测:识别不属于任何簇的异常点
11.1.3 聚类算法分类
- 基于距离的聚类:K-means、K-medoids
- 层次聚类:凝聚聚类、分裂聚类
- 基于密度的聚类:DBSCAN、OPTICS
- 基于分布的聚类:高斯混合模型
- 基于网格的聚类:适用于大数据集
11.2 准备环境和数据
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs, make_circles, make_moons, load_iris, load_wine
from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN, SpectralClustering
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import adjusted_rand_score, silhouette_score, calinski_harabasz_score
from sklearn.decomposition import PCA
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial.distance import pdist, squareform
import warnings
warnings.filterwarnings('ignore')
# 设置随机种子
np.random.seed(42)
# 设置图形样式
plt.style.use('seaborn-v0_8')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False11.3 K-means聚类
11.3.1 K-means算法原理
K-means是最经典的聚类算法,通过迭代优化来最小化簇内平方和。
python
def demonstrate_kmeans_principle():
"""演示K-means算法的基本原理"""
# 创建简单的聚类数据
X_demo, y_true = make_blobs(n_samples=300, centers=3, n_features=2,
random_state=42, cluster_std=1.5)
print("K-means算法步骤演示:")
print("1. 随机初始化K个聚类中心")
print("2. 将每个点分配给最近的聚类中心")
print("3. 更新聚类中心为簇内点的均值")
print("4. 重复步骤2-3直到收敛")
# 可视化数据
plt.figure(figsize=(15, 10))
# 原始数据
plt.subplot(2, 3, 1)
plt.scatter(X_demo[:, 0], X_demo[:, 1], c='gray', alpha=0.6)
plt.title('原始数据')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.grid(True, alpha=0.3)
# 手动演示K-means迭代过程
k = 3
# 随机初始化聚类中心
centers = np.random.randn(k, 2) * 2
for iteration in range(5):
if iteration < 4: # 只显示前4次迭代
plt.subplot(2, 3, iteration + 2)
# 计算每个点到聚类中心的距离
distances = np.sqrt(((X_demo - centers[:, np.newaxis])**2).sum(axis=2))
labels = np.argmin(distances, axis=0)
# 绘制数据点和聚类中心
colors = ['red', 'blue', 'green']
for i in range(k):
mask = labels == i
plt.scatter(X_demo[mask, 0], X_demo[mask, 1],
c=colors[i], alpha=0.6, s=30)
plt.scatter(centers[i, 0], centers[i, 1],
c='black', marker='x', s=200, linewidths=3)
plt.title(f'迭代 {iteration + 1}')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.grid(True, alpha=0.3)
# 更新聚类中心
new_centers = np.array([X_demo[labels == i].mean(axis=0) for i in range(k)])
centers = new_centers
# 最终结果
plt.subplot(2, 3, 6)
for i in range(k):
mask = labels == i
plt.scatter(X_demo[mask, 0], X_demo[mask, 1],
c=colors[i], alpha=0.6, s=30)
plt.scatter(centers[i, 0], centers[i, 1],
c='black', marker='x', s=200, linewidths=3)
plt.title('最终结果')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
return X_demo, y_true
X_demo, y_true = demonstrate_kmeans_principle()
```###
11.3.2 使用Scikit-learn实现K-means
```python
# 使用Scikit-learn的K-means
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
y_pred = kmeans.fit_predict(X_demo)
# 获取聚类中心
centers = kmeans.cluster_centers_
print("K-means聚类结果:")
print(f"聚类中心:\n{centers}")
print(f"惯性 (WCSS): {kmeans.inertia_:.2f}")
# 可视化结果
plt.figure(figsize=(15, 5))
# 真实标签
plt.subplot(1, 3, 1)
colors = ['red', 'blue', 'green']
for i in range(3):
mask = y_true == i
plt.scatter(X_demo[mask, 0], X_demo[mask, 1], c=colors[i], alpha=0.6, label=f'真实簇 {i}')
plt.title('真实聚类')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.legend()
plt.grid(True, alpha=0.3)
# K-means结果
plt.subplot(1, 3, 2)
for i in range(3):
mask = y_pred == i
plt.scatter(X_demo[mask, 0], X_demo[mask, 1], c=colors[i], alpha=0.6, label=f'预测簇 {i}')
plt.scatter(centers[:, 0], centers[:, 1], c='black', marker='x', s=200, linewidths=3, label='聚类中心')
plt.title('K-means聚类结果')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.legend()
plt.grid(True, alpha=0.3)
# 对比
plt.subplot(1, 3, 3)
plt.scatter(X_demo[:, 0], X_demo[:, 1], c=y_pred, cmap='viridis', alpha=0.6)
plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='x', s=200, linewidths=3)
plt.title('聚类结果 (颜色编码)')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.colorbar()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()11.3.3 选择最优的K值
python
def find_optimal_k(X, k_range):
"""使用肘部法则和轮廓系数选择最优K值"""
inertias = []
silhouette_scores = []
print("不同K值的聚类性能:")
print("K值\t惯性(WCSS)\t轮廓系数")
print("-" * 35)
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
y_pred = kmeans.fit_predict(X)
inertia = kmeans.inertia_
if k > 1: # 轮廓系数需要至少2个簇
silhouette = silhouette_score(X, y_pred)
else:
silhouette = 0
inertias.append(inertia)
silhouette_scores.append(silhouette)
print(f"{k}\t{inertia:.2f}\t\t{silhouette:.4f}")
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
# 肘部法则
axes[0].plot(k_range, inertias, 'bo-', linewidth=2, markersize=8)
axes[0].set_xlabel('K值')
axes[0].set_ylabel('惯性 (WCSS)')
axes[0].set_title('肘部法则')
axes[0].grid(True, alpha=0.3)
# 轮廓系数
axes[1].plot(k_range[1:], silhouette_scores[1:], 'ro-', linewidth=2, markersize=8)
axes[1].set_xlabel('K值')
axes[1].set_ylabel('轮廓系数')
axes[1].set_title('轮廓系数法')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 找出最佳K值
best_k_silhouette = k_range[1:][np.argmax(silhouette_scores[1:])]
print(f"\n基于轮廓系数的最佳K值: {best_k_silhouette}")
return inertias, silhouette_scores
# 寻找最优K值
k_range = range(1, 11)
inertias, silhouette_scores = find_optimal_k(X_demo, k_range)11.3.4 K-means的变体
python
# 比较不同的K-means变体
from sklearn.cluster import MiniBatchKMeans
def compare_kmeans_variants():
"""比较不同的K-means变体"""
# 创建较大的数据集
X_large, y_large = make_blobs(n_samples=2000, centers=4, n_features=2,
random_state=42, cluster_std=2.0)
# 不同的K-means算法
algorithms = {
'K-means': KMeans(n_clusters=4, random_state=42),
'Mini-batch K-means': MiniBatchKMeans(n_clusters=4, random_state=42, batch_size=100)
}
results = {}
print("K-means变体性能比较:")
print("算法\t\t\t训练时间\t惯性\t\t轮廓系数")
print("-" * 60)
import time
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
for i, (name, algorithm) in enumerate(algorithms.items()):
# 训练时间
start_time = time.time()
y_pred = algorithm.fit_predict(X_large)
train_time = time.time() - start_time
# 性能指标
inertia = algorithm.inertia_
silhouette = silhouette_score(X_large, y_pred)
results[name] = {
'time': train_time,
'inertia': inertia,
'silhouette': silhouette
}
print(f"{name}\t{train_time:.4f}s\t\t{inertia:.2f}\t\t{silhouette:.4f}")
# 可视化
axes[i].scatter(X_large[:, 0], X_large[:, 1], c=y_pred, cmap='viridis', alpha=0.6, s=10)
axes[i].scatter(algorithm.cluster_centers_[:, 0], algorithm.cluster_centers_[:, 1],
c='red', marker='x', s=200, linewidths=3)
axes[i].set_title(f'{name}\n轮廓系数: {silhouette:.3f}')
axes[i].set_xlabel('特征 1')
axes[i].set_ylabel('特征 2')
axes[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
return results
kmeans_results = compare_kmeans_variants()11.4 层次聚类
11.4.1 凝聚层次聚类
python
def demonstrate_hierarchical_clustering():
"""演示层次聚类"""
# 创建小规模数据用于演示
X_hier, y_hier = make_blobs(n_samples=50, centers=3, n_features=2,
random_state=42, cluster_std=1.0)
# 凝聚层次聚类
hierarchical = AgglomerativeClustering(n_clusters=3, linkage='ward')
y_pred_hier = hierarchical.fit_predict(X_hier)
print("层次聚类结果:")
print(f"聚类数: {len(np.unique(y_pred_hier))}")
# 可视化结果
plt.figure(figsize=(15, 5))
# 原始数据
plt.subplot(1, 3, 1)
plt.scatter(X_hier[:, 0], X_hier[:, 1], c='gray', alpha=0.6)
plt.title('原始数据')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.grid(True, alpha=0.3)
# 层次聚类结果
plt.subplot(1, 3, 2)
colors = ['red', 'blue', 'green']
for i in range(3):
mask = y_pred_hier == i
plt.scatter(X_hier[mask, 0], X_hier[mask, 1], c=colors[i], alpha=0.6, label=f'簇 {i}')
plt.title('层次聚类结果')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.legend()
plt.grid(True, alpha=0.3)
# 树状图
plt.subplot(1, 3, 3)
# 计算链接矩阵
linkage_matrix = linkage(X_hier, method='ward')
dendrogram(linkage_matrix, truncate_mode='level', p=5)
plt.title('树状图')
plt.xlabel('样本索引')
plt.ylabel('距离')
plt.tight_layout()
plt.show()
return X_hier, y_pred_hier
X_hier, y_pred_hier = demonstrate_hierarchical_clustering()11.4.2 不同链接方法的比较
python
def compare_linkage_methods():
"""比较不同的链接方法"""
# 创建具有不同形状的聚类数据
X_shapes = []
y_shapes = []
# 圆形聚类
X_circles, y_circles = make_circles(n_samples=200, noise=0.1, factor=0.3, random_state=42)
X_shapes.append(('圆形', X_circles, y_circles))
# 月牙形聚类
X_moons, y_moons = make_moons(n_samples=200, noise=0.1, random_state=42)
X_shapes.append(('月牙形', X_moons, y_moons))
# 球形聚类
X_blobs, y_blobs = make_blobs(n_samples=200, centers=3, random_state=42)
X_shapes.append(('球形', X_blobs, y_blobs))
# 不同的链接方法
linkage_methods = ['ward', 'complete', 'average', 'single']
fig, axes = plt.subplots(len(X_shapes), len(linkage_methods) + 1, figsize=(20, 12))
fig.suptitle('不同链接方法的层次聚类比较', fontsize=16)
for i, (shape_name, X, y_true) in enumerate(X_shapes):
# 原始数据
axes[i, 0].scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', alpha=0.6)
axes[i, 0].set_title(f'{shape_name}数据')
axes[i, 0].set_xlabel('特征 1')
axes[i, 0].set_ylabel('特征 2')
# 不同链接方法
for j, linkage_method in enumerate(linkage_methods):
n_clusters = len(np.unique(y_true))
if linkage_method == 'ward':
# Ward方法只适用于欧几里得距离
hierarchical = AgglomerativeClustering(n_clusters=n_clusters, linkage=linkage_method)
else:
hierarchical = AgglomerativeClustering(n_clusters=n_clusters, linkage=linkage_method)
y_pred = hierarchical.fit_predict(X)
axes[i, j + 1].scatter(X[:, 0], X[:, 1], c=y_pred, cmap='viridis', alpha=0.6)
axes[i, j + 1].set_title(f'{linkage_method}链接')
axes[i, j + 1].set_xlabel('特征 1')
axes[i, j + 1].set_ylabel('特征 2')
plt.tight_layout()
plt.show()
compare_linkage_methods()
```##
11.5 基于密度的聚类 (DBSCAN)
### 11.5.1 DBSCAN算法原理
DBSCAN (Density-Based Spatial Clustering of Applications with Noise) 是一种基于密度的聚类算法,能够发现任意形状的聚类并识别噪声点。
```python
def demonstrate_dbscan_principle():
"""演示DBSCAN算法原理"""
print("DBSCAN算法核心概念:")
print("1. 核心点: 在其ε邻域内至少有min_samples个点")
print("2. 边界点: 不是核心点但在某个核心点的ε邻域内")
print("3. 噪声点: 既不是核心点也不是边界点")
print("4. 密度可达: 通过核心点序列连接的点")
# 创建包含噪声的数据
X_noise, _ = make_blobs(n_samples=200, centers=3, n_features=2,
random_state=42, cluster_std=1.0)
# 添加噪声点
noise_points = np.random.uniform(-8, 8, (20, 2))
X_dbscan = np.vstack([X_noise, noise_points])
# DBSCAN聚类
dbscan = DBSCAN(eps=1.5, min_samples=5)
y_pred_dbscan = dbscan.fit_predict(X_dbscan)
# 统计结果
n_clusters = len(set(y_pred_dbscan)) - (1 if -1 in y_pred_dbscan else 0)
n_noise = list(y_pred_dbscan).count(-1)
print(f"\nDBSCAN聚类结果:")
print(f"聚类数: {n_clusters}")
print(f"噪声点数: {n_noise}")
print(f"核心点数: {len(dbscan.core_sample_indices_)}")
# 可视化结果
plt.figure(figsize=(15, 5))
# 原始数据
plt.subplot(1, 3, 1)
plt.scatter(X_dbscan[:, 0], X_dbscan[:, 1], c='gray', alpha=0.6)
plt.title('原始数据(含噪声)')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.grid(True, alpha=0.3)
# DBSCAN结果
plt.subplot(1, 3, 2)
unique_labels = set(y_pred_dbscan)
colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
for k, col in zip(unique_labels, colors):
if k == -1:
# 噪声点用黑色表示
col = 'black'
marker = 'x'
alpha = 0.5
label = '噪声点'
else:
marker = 'o'
alpha = 0.7
label = f'簇 {k}'
class_member_mask = (y_pred_dbscan == k)
xy = X_dbscan[class_member_mask]
plt.scatter(xy[:, 0], xy[:, 1], c=[col], marker=marker, alpha=alpha, s=50)
plt.title('DBSCAN聚类结果')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.grid(True, alpha=0.3)
# 核心点标记
plt.subplot(1, 3, 3)
# 绘制所有点
plt.scatter(X_dbscan[:, 0], X_dbscan[:, 1], c=y_pred_dbscan,
cmap='Spectral', alpha=0.6, s=50)
# 标记核心点
core_samples = X_dbscan[dbscan.core_sample_indices_]
plt.scatter(core_samples[:, 0], core_samples[:, 1],
facecolors='none', edgecolors='red', s=100, linewidths=2, label='核心点')
plt.title('核心点标记')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
return X_dbscan, y_pred_dbscan
X_dbscan, y_pred_dbscan = demonstrate_dbscan_principle()11.5.2 DBSCAN参数调优
python
def tune_dbscan_parameters():
"""调优DBSCAN参数"""
# 使用月牙形数据测试DBSCAN
X_moons, y_moons = make_moons(n_samples=200, noise=0.1, random_state=42)
# 不同的eps和min_samples组合
eps_values = [0.1, 0.2, 0.3, 0.5]
min_samples_values = [3, 5, 10, 15]
fig, axes = plt.subplots(len(eps_values), len(min_samples_values), figsize=(16, 12))
fig.suptitle('DBSCAN参数调优', fontsize=16)
print("DBSCAN参数调优结果:")
print("eps\tmin_samples\t聚类数\t噪声点数\t轮廓系数")
print("-" * 55)
best_score = -1
best_params = None
for i, eps in enumerate(eps_values):
for j, min_samples in enumerate(min_samples_values):
dbscan = DBSCAN(eps=eps, min_samples=min_samples)
y_pred = dbscan.fit_predict(X_moons)
n_clusters = len(set(y_pred)) - (1 if -1 in y_pred else 0)
n_noise = list(y_pred).count(-1)
# 计算轮廓系数(需要至少2个簇且不全是噪声)
if n_clusters >= 2 and len(set(y_pred)) > 1:
# 排除噪声点计算轮廓系数
mask = y_pred != -1
if np.sum(mask) > 0:
silhouette = silhouette_score(X_moons[mask], y_pred[mask])
else:
silhouette = -1
else:
silhouette = -1
print(f"{eps}\t{min_samples}\t\t{n_clusters}\t{n_noise}\t\t{silhouette:.4f}")
# 记录最佳参数
if silhouette > best_score:
best_score = silhouette
best_params = (eps, min_samples)
# 可视化
axes[i, j].scatter(X_moons[:, 0], X_moons[:, 1], c=y_pred,
cmap='Spectral', alpha=0.7, s=30)
axes[i, j].set_title(f'eps={eps}, min_samples={min_samples}\n'
f'簇数={n_clusters}, 轮廓系数={silhouette:.3f}')
axes[i, j].set_xlabel('特征 1')
axes[i, j].set_ylabel('特征 2')
plt.tight_layout()
plt.show()
print(f"\n最佳参数: eps={best_params[0]}, min_samples={best_params[1]}")
print(f"最佳轮廓系数: {best_score:.4f}")
return best_params
best_dbscan_params = tune_dbscan_parameters()11.6 高斯混合模型 (GMM)
11.6.1 GMM基本原理
python
def demonstrate_gmm():
"""演示高斯混合模型"""
print("高斯混合模型 (GMM) 特点:")
print("1. 假设数据来自多个高斯分布的混合")
print("2. 使用EM算法进行参数估计")
print("3. 提供软聚类(概率分配)")
print("4. 能够处理椭圆形聚类")
# 创建椭圆形聚类数据
X_ellipse, y_ellipse = make_blobs(n_samples=300, centers=3, n_features=2,
random_state=42, cluster_std=2.0)
# 对数据进行变换使其呈椭圆形
transformation_matrix = np.array([[1, 0.5], [0.5, 1]])
X_ellipse = X_ellipse.dot(transformation_matrix)
# 高斯混合模型
gmm = GaussianMixture(n_components=3, random_state=42)
y_pred_gmm = gmm.fit_predict(X_ellipse)
y_proba_gmm = gmm.predict_proba(X_ellipse)
print(f"\nGMM聚类结果:")
print(f"聚类数: {gmm.n_components}")
print(f"收敛: {gmm.converged_}")
print(f"迭代次数: {gmm.n_iter_}")
print(f"对数似然: {gmm.score(X_ellipse):.2f}")
# 可视化结果
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('高斯混合模型聚类', fontsize=16)
# 原始数据
axes[0, 0].scatter(X_ellipse[:, 0], X_ellipse[:, 1], c='gray', alpha=0.6)
axes[0, 0].set_title('原始数据')
axes[0, 0].set_xlabel('特征 1')
axes[0, 0].set_ylabel('特征 2')
axes[0, 0].grid(True, alpha=0.3)
# GMM聚类结果
colors = ['red', 'blue', 'green']
for i in range(3):
mask = y_pred_gmm == i
axes[0, 1].scatter(X_ellipse[mask, 0], X_ellipse[mask, 1],
c=colors[i], alpha=0.6, label=f'簇 {i}')
axes[0, 1].set_title('GMM聚类结果')
axes[0, 1].set_xlabel('特征 1')
axes[0, 1].set_ylabel('特征 2')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# 概率分配(软聚类)
# 显示每个点属于第一个簇的概率
scatter = axes[1, 0].scatter(X_ellipse[:, 0], X_ellipse[:, 1],
c=y_proba_gmm[:, 0], cmap='Reds', alpha=0.7)
axes[1, 0].set_title('属于簇0的概率')
axes[1, 0].set_xlabel('特征 1')
axes[1, 0].set_ylabel('特征 2')
plt.colorbar(scatter, ax=axes[1, 0])
# 高斯分布等高线
axes[1, 1].scatter(X_ellipse[:, 0], X_ellipse[:, 1], c=y_pred_gmm,
cmap='viridis', alpha=0.6)
# 绘制高斯分布的等高线
x_min, x_max = X_ellipse[:, 0].min() - 2, X_ellipse[:, 0].max() + 2
y_min, y_max = X_ellipse[:, 1].min() - 2, X_ellipse[:, 1].max() + 2
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100))
mesh_points = np.c_[xx.ravel(), yy.ravel()]
Z = gmm.score_samples(mesh_points)
Z = Z.reshape(xx.shape)
axes[1, 1].contour(xx, yy, Z, levels=10, alpha=0.5)
axes[1, 1].set_title('GMM等高线')
axes[1, 1].set_xlabel('特征 1')
axes[1, 1].set_ylabel('特征 2')
plt.tight_layout()
plt.show()
return X_ellipse, y_pred_gmm, gmm
X_ellipse, y_pred_gmm, gmm = demonstrate_gmm()11.6.2 模型选择和信息准则
python
def gmm_model_selection():
"""使用信息准则选择GMM的最佳组件数"""
n_components_range = range(1, 11)
aic_scores = []
bic_scores = []
print("GMM模型选择:")
print("组件数\tAIC\t\tBIC")
print("-" * 30)
for n_components in n_components_range:
gmm = GaussianMixture(n_components=n_components, random_state=42)
gmm.fit(X_ellipse)
aic = gmm.aic(X_ellipse)
bic = gmm.bic(X_ellipse)
aic_scores.append(aic)
bic_scores.append(bic)
print(f"{n_components}\t{aic:.2f}\t\t{bic:.2f}")
# 可视化信息准则
plt.figure(figsize=(10, 6))
plt.plot(n_components_range, aic_scores, 'bo-', label='AIC', linewidth=2, markersize=8)
plt.plot(n_components_range, bic_scores, 'ro-', label='BIC', linewidth=2, markersize=8)
plt.xlabel('组件数')
plt.ylabel('信息准则值')
plt.title('GMM模型选择 - AIC vs BIC')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# 找出最佳组件数
best_n_aic = n_components_range[np.argmin(aic_scores)]
best_n_bic = n_components_range[np.argmin(bic_scores)]
print(f"\n基于AIC的最佳组件数: {best_n_aic}")
print(f"基于BIC的最佳组件数: {best_n_bic}")
return best_n_aic, best_n_bic
best_n_aic, best_n_bic = gmm_model_selection()11.7 聚类算法比较
11.7.1 不同算法在各种数据上的表现
python
def comprehensive_clustering_comparison():
"""全面比较不同聚类算法"""
# 创建不同类型的数据集
datasets = []
# 1. 球形聚类
X_blobs, y_blobs = make_blobs(n_samples=200, centers=3, random_state=42)
datasets.append(('球形聚类', X_blobs, y_blobs))
# 2. 月牙形
X_moons, y_moons = make_moons(n_samples=200, noise=0.1, random_state=42)
datasets.append(('月牙形', X_moons, y_moons))
# 3. 圆形
X_circles, y_circles = make_circles(n_samples=200, noise=0.1, factor=0.3, random_state=42)
datasets.append(('圆形', X_circles, y_circles))
# 4. 不同密度
X_varied, y_varied = make_blobs(n_samples=200, centers=3,
cluster_std=[1.0, 2.5, 0.5], random_state=42)
datasets.append(('不同密度', X_varied, y_varied))
# 聚类算法
algorithms = {
'K-means': KMeans(n_clusters=2, random_state=42),
'层次聚类': AgglomerativeClustering(n_clusters=2, linkage='ward'),
'DBSCAN': DBSCAN(eps=0.3, min_samples=5),
'GMM': GaussianMixture(n_components=2, random_state=42),
'谱聚类': SpectralClustering(n_clusters=2, random_state=42)
}
# 创建比较图
fig, axes = plt.subplots(len(datasets), len(algorithms) + 1, figsize=(20, 16))
fig.suptitle('不同聚类算法比较', fontsize=16)
print("聚类算法性能比较:")
print("数据集\t\t算法\t\t\t轮廓系数\tARI")
print("-" * 60)
for i, (dataset_name, X, y_true) in enumerate(datasets):
# 标准化数据
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 原始数据
axes[i, 0].scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', alpha=0.7)
axes[i, 0].set_title(f'{dataset_name}\n(真实标签)')
axes[i, 0].set_xlabel('特征 1')
axes[i, 0].set_ylabel('特征 2')
for j, (alg_name, algorithm) in enumerate(algorithms.items()):
# 调整聚类数
if hasattr(algorithm, 'n_clusters'):
algorithm.n_clusters = len(np.unique(y_true))
elif hasattr(algorithm, 'n_components'):
algorithm.n_components = len(np.unique(y_true))
# 对某些算法使用标准化数据
if alg_name in ['谱聚类']:
X_input = X_scaled
else:
X_input = X
try:
y_pred = algorithm.fit_predict(X_input)
# 计算评估指标
if len(set(y_pred)) > 1:
silhouette = silhouette_score(X_input, y_pred)
ari = adjusted_rand_score(y_true, y_pred)
else:
silhouette = -1
ari = -1
print(f"{dataset_name}\t{alg_name}\t\t{silhouette:.4f}\t\t{ari:.4f}")
# 可视化结果
axes[i, j + 1].scatter(X[:, 0], X[:, 1], c=y_pred, cmap='viridis', alpha=0.7)
axes[i, j + 1].set_title(f'{alg_name}\n轮廓系数: {silhouette:.3f}')
axes[i, j + 1].set_xlabel('特征 1')
axes[i, j + 1].set_ylabel('特征 2')
except Exception as e:
print(f"{dataset_name}\t{alg_name}\t\t错误: {str(e)[:20]}")
axes[i, j + 1].text(0.5, 0.5, '算法失败', ha='center', va='center',
transform=axes[i, j + 1].transAxes)
axes[i, j + 1].set_title(f'{alg_name}\n失败')
plt.tight_layout()
plt.show()
comprehensive_clustering_comparison()
```## 11.
8 聚类评估
### 11.8.1 内部评估指标
```python
def clustering_evaluation_metrics():
"""演示聚类评估指标"""
# 使用鸢尾花数据集
iris = load_iris()
X_iris = iris.data
y_true_iris = iris.target
# 标准化数据
scaler = StandardScaler()
X_iris_scaled = scaler.fit_transform(X_iris)
# 不同的聚类算法
algorithms = {
'K-means': KMeans(n_clusters=3, random_state=42),
'层次聚类': AgglomerativeClustering(n_clusters=3),
'GMM': GaussianMixture(n_components=3, random_state=42)
}
print("聚类评估指标比较:")
print("算法\t\t轮廓系数\tCalinski-Harabasz\tARI\t\t互信息")
print("-" * 70)
results = {}
for name, algorithm in algorithms.items():
# 聚类
y_pred = algorithm.fit_predict(X_iris_scaled)
# 内部评估指标(不需要真实标签)
silhouette = silhouette_score(X_iris_scaled, y_pred)
calinski_harabasz = calinski_harabasz_score(X_iris_scaled, y_pred)
# 外部评估指标(需要真实标签)
from sklearn.metrics import adjusted_mutual_info_score
ari = adjusted_rand_score(y_true_iris, y_pred)
ami = adjusted_mutual_info_score(y_true_iris, y_pred)
results[name] = {
'silhouette': silhouette,
'calinski_harabasz': calinski_harabasz,
'ari': ari,
'ami': ami,
'y_pred': y_pred
}
print(f"{name}\t{silhouette:.4f}\t\t{calinski_harabasz:.2f}\t\t{ari:.4f}\t\t{ami:.4f}")
# 可视化评估结果
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('聚类评估指标比较', fontsize=16)
metrics = ['silhouette', 'calinski_harabasz', 'ari', 'ami']
metric_names = ['轮廓系数', 'Calinski-Harabasz指数', '调整兰德指数', '调整互信息']
for i, (metric, metric_name) in enumerate(zip(metrics, metric_names)):
row = i // 2
col = i % 2
names = list(results.keys())
values = [results[name][metric] for name in names]
bars = axes[row, col].bar(names, values, alpha=0.7,
color=['skyblue', 'lightgreen', 'lightcoral'])
axes[row, col].set_title(metric_name)
axes[row, col].set_ylabel('得分')
axes[row, col].tick_params(axis='x', rotation=45)
# 添加数值标签
for bar, value in zip(bars, values):
height = bar.get_height()
axes[row, col].text(bar.get_x() + bar.get_width()/2., height + height*0.01,
f'{value:.3f}', ha='center', va='bottom')
plt.tight_layout()
plt.show()
return results
evaluation_results = clustering_evaluation_metrics()11.8.2 聚类稳定性分析
python
def clustering_stability_analysis():
"""分析聚类算法的稳定性"""
# 创建数据集
X_stability, y_stability = make_blobs(n_samples=300, centers=3,
n_features=2, random_state=42)
# 多次运行聚类算法
n_runs = 20
algorithms = {
'K-means': KMeans(n_clusters=3),
'层次聚类': AgglomerativeClustering(n_clusters=3),
'GMM': GaussianMixture(n_components=3)
}
stability_results = {}
print("聚类稳定性分析:")
print("算法\t\t平均轮廓系数\t标准差\t\t变异系数")
print("-" * 55)
for name, algorithm in algorithms.items():
silhouette_scores = []
for run in range(n_runs):
# 为了测试稳定性,每次使用不同的随机种子
if hasattr(algorithm, 'random_state'):
algorithm.random_state = run
y_pred = algorithm.fit_predict(X_stability)
silhouette = silhouette_score(X_stability, y_pred)
silhouette_scores.append(silhouette)
mean_silhouette = np.mean(silhouette_scores)
std_silhouette = np.std(silhouette_scores)
cv_silhouette = std_silhouette / mean_silhouette if mean_silhouette != 0 else 0
stability_results[name] = {
'scores': silhouette_scores,
'mean': mean_silhouette,
'std': std_silhouette,
'cv': cv_silhouette
}
print(f"{name}\t{mean_silhouette:.4f}\t\t{std_silhouette:.4f}\t\t{cv_silhouette:.4f}")
# 可视化稳定性
fig, axes = plt.subplots(1, 2, figsize=(15, 6))
# 箱线图
data_for_boxplot = [stability_results[name]['scores'] for name in algorithms.keys()]
axes[0].boxplot(data_for_boxplot, labels=algorithms.keys())
axes[0].set_title('聚类算法稳定性(箱线图)')
axes[0].set_ylabel('轮廓系数')
axes[0].tick_params(axis='x', rotation=45)
axes[0].grid(True, alpha=0.3)
# 变异系数比较
names = list(stability_results.keys())
cvs = [stability_results[name]['cv'] for name in names]
bars = axes[1].bar(names, cvs, alpha=0.7, color=['skyblue', 'lightgreen', 'lightcoral'])
axes[1].set_title('变异系数比较(越小越稳定)')
axes[1].set_ylabel('变异系数')
axes[1].tick_params(axis='x', rotation=45)
# 添加数值标签
for bar, cv in zip(bars, cvs):
height = bar.get_height()
axes[1].text(bar.get_x() + bar.get_width()/2., height + height*0.01,
f'{cv:.4f}', ha='center', va='bottom')
plt.tight_layout()
plt.show()
return stability_results
stability_results = clustering_stability_analysis()11.9 实际应用案例
11.9.1 客户细分分析
python
def customer_segmentation_case():
"""客户细分案例"""
# 创建模拟客户数据
np.random.seed(42)
n_customers = 1000
# 生成客户特征
age = np.random.normal(40, 15, n_customers)
annual_income = np.random.exponential(50000, n_customers)
spending_score = np.random.normal(50, 25, n_customers)
# 添加一些相关性
# 年轻人倾向于有更高的消费分数
spending_score += (50 - age) * 0.5 + np.random.normal(0, 10, n_customers)
# 高收入者倾向于有更高的消费分数
spending_score += annual_income / 2000 + np.random.normal(0, 5, n_customers)
# 确保数据在合理范围内
age = np.clip(age, 18, 80)
annual_income = np.clip(annual_income, 15000, 200000)
spending_score = np.clip(spending_score, 1, 100)
# 创建DataFrame
customer_data = pd.DataFrame({
'年龄': age,
'年收入': annual_income,
'消费分数': spending_score
})
print("客户数据概览:")
print(customer_data.describe())
# 数据标准化
scaler = StandardScaler()
X_customers = scaler.fit_transform(customer_data)
# 使用肘部法则确定最佳聚类数
k_range = range(1, 11)
inertias = []
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
kmeans.fit(X_customers)
inertias.append(kmeans.inertia_)
# 可视化肘部法则
plt.figure(figsize=(15, 10))
plt.subplot(2, 3, 1)
plt.plot(k_range, inertias, 'bo-', linewidth=2, markersize=8)
plt.xlabel('聚类数 K')
plt.ylabel('惯性 (WCSS)')
plt.title('肘部法则')
plt.grid(True, alpha=0.3)
# 选择K=4进行聚类
optimal_k = 4
kmeans_final = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
customer_segments = kmeans_final.fit_predict(X_customers)
# 添加聚类标签到原始数据
customer_data['客户群体'] = customer_segments
# 分析每个客户群体的特征
print(f"\n客户细分结果 (K={optimal_k}):")
segment_analysis = customer_data.groupby('客户群体').agg({
'年龄': ['mean', 'std'],
'年收入': ['mean', 'std'],
'消费分数': ['mean', 'std']
}).round(2)
print(segment_analysis)
# 可视化客户细分
# 年龄 vs 年收入
plt.subplot(2, 3, 2)
colors = ['red', 'blue', 'green', 'orange']
for i in range(optimal_k):
mask = customer_segments == i
plt.scatter(customer_data.loc[mask, '年龄'],
customer_data.loc[mask, '年收入'],
c=colors[i], alpha=0.6, label=f'群体 {i}')
plt.xlabel('年龄')
plt.ylabel('年收入')
plt.title('年龄 vs 年收入')
plt.legend()
plt.grid(True, alpha=0.3)
# 年收入 vs 消费分数
plt.subplot(2, 3, 3)
for i in range(optimal_k):
mask = customer_segments == i
plt.scatter(customer_data.loc[mask, '年收入'],
customer_data.loc[mask, '消费分数'],
c=colors[i], alpha=0.6, label=f'群体 {i}')
plt.xlabel('年收入')
plt.ylabel('消费分数')
plt.title('年收入 vs 消费分数')
plt.legend()
plt.grid(True, alpha=0.3)
# 年龄 vs 消费分数
plt.subplot(2, 3, 4)
for i in range(optimal_k):
mask = customer_segments == i
plt.scatter(customer_data.loc[mask, '年龄'],
customer_data.loc[mask, '消费分数'],
c=colors[i], alpha=0.6, label=f'群体 {i}')
plt.xlabel('年龄')
plt.ylabel('消费分数')
plt.title('年龄 vs 消费分数')
plt.legend()
plt.grid(True, alpha=0.3)
# 客户群体分布
plt.subplot(2, 3, 5)
segment_counts = customer_data['客户群体'].value_counts().sort_index()
plt.pie(segment_counts.values, labels=[f'群体 {i}' for i in segment_counts.index],
autopct='%1.1f%%', colors=colors[:len(segment_counts)])
plt.title('客户群体分布')
# 3D可视化
from mpl_toolkits.mplot3d import Axes3D
ax = plt.subplot(2, 3, 6, projection='3d')
for i in range(optimal_k):
mask = customer_segments == i
ax.scatter(customer_data.loc[mask, '年龄'],
customer_data.loc[mask, '年收入'],
customer_data.loc[mask, '消费分数'],
c=colors[i], alpha=0.6, label=f'群体 {i}')
ax.set_xlabel('年龄')
ax.set_ylabel('年收入')
ax.set_zlabel('消费分数')
ax.set_title('3D客户细分')
ax.legend()
plt.tight_layout()
plt.show()
# 客户群体特征描述
print("\n客户群体特征分析:")
for i in range(optimal_k):
segment_data = customer_data[customer_data['客户群体'] == i]
print(f"\n群体 {i} ({len(segment_data)} 人, {len(segment_data)/len(customer_data)*100:.1f}%):")
print(f" 平均年龄: {segment_data['年龄'].mean():.1f} 岁")
print(f" 平均年收入: ¥{segment_data['年收入'].mean():,.0f}")
print(f" 平均消费分数: {segment_data['消费分数'].mean():.1f}")
# 群体特征标签
if segment_data['年龄'].mean() < 35 and segment_data['消费分数'].mean() > 60:
print(" 特征: 年轻高消费群体")
elif segment_data['年收入'].mean() > 80000 and segment_data['消费分数'].mean() > 60:
print(" 特征: 高收入高消费群体")
elif segment_data['年龄'].mean() > 50 and segment_data['消费分数'].mean() < 40:
print(" 特征: 中老年保守消费群体")
else:
print(" 特征: 普通消费群体")
return customer_data, customer_segments
customer_data, customer_segments = customer_segmentation_case()11.9.2 图像分割应用
python
def image_segmentation_example():
"""图像分割示例"""
# 创建模拟图像数据
from sklearn.datasets import make_checkerboard
# 生成棋盘图案数据
np.random.seed(42)
n_samples = 1000
# 创建三个区域的数据
# 区域1: 低亮度
region1 = np.random.normal(50, 10, (n_samples//3, 3)) # RGB值
# 区域2: 中等亮度
region2 = np.random.normal(128, 15, (n_samples//3, 3))
# 区域3: 高亮度
region3 = np.random.normal(200, 12, (n_samples//3, 3))
# 合并数据
image_data = np.vstack([region1, region2, region3])
# 确保RGB值在有效范围内
image_data = np.clip(image_data, 0, 255)
print("图像分割聚类分析:")
print(f"像素数据形状: {image_data.shape}")
print(f"RGB值范围: [{image_data.min():.0f}, {image_data.max():.0f}]")
# 使用不同聚类算法进行图像分割
algorithms = {
'K-means': KMeans(n_clusters=3, random_state=42),
'GMM': GaussianMixture(n_components=3, random_state=42),
'层次聚类': AgglomerativeClustering(n_clusters=3)
}
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('图像分割聚类比较', fontsize=16)
# 原始数据分布
axes[0, 0].scatter(image_data[:, 0], image_data[:, 1],
c=image_data[:, 2], cmap='viridis', alpha=0.6, s=10)
axes[0, 0].set_title('原始像素分布 (R vs G, 颜色=B)')
axes[0, 0].set_xlabel('红色通道')
axes[0, 0].set_ylabel('绿色通道')
for i, (name, algorithm) in enumerate(algorithms.items()):
row = (i + 1) // 2
col = (i + 1) % 2
# 聚类
labels = algorithm.fit_predict(image_data)
# 计算聚类中心
if hasattr(algorithm, 'cluster_centers_'):
centers = algorithm.cluster_centers_
elif hasattr(algorithm, 'means_'):
centers = algorithm.means_
else:
# 对于层次聚类,手动计算中心
centers = np.array([image_data[labels == k].mean(axis=0) for k in range(3)])
# 可视化分割结果
axes[row, col].scatter(image_data[:, 0], image_data[:, 1],
c=labels, cmap='viridis', alpha=0.6, s=10)
axes[row, col].scatter(centers[:, 0], centers[:, 1],
c='red', marker='x', s=200, linewidths=3)
axes[row, col].set_title(f'{name} 分割结果')
axes[row, col].set_xlabel('红色通道')
axes[row, col].set_ylabel('绿色通道')
# 计算轮廓系数
silhouette = silhouette_score(image_data, labels)
print(f"{name} 轮廓系数: {silhouette:.4f}")
plt.tight_layout()
plt.show()
# 分析分割质量
print("\n分割区域分析:")
best_algorithm = KMeans(n_clusters=3, random_state=42)
best_labels = best_algorithm.fit_predict(image_data)
for i in range(3):
region_pixels = image_data[best_labels == i]
print(f"区域 {i}:")
print(f" 像素数量: {len(region_pixels)}")
print(f" 平均RGB: ({region_pixels[:, 0].mean():.1f}, "
f"{region_pixels[:, 1].mean():.1f}, {region_pixels[:, 2].mean():.1f})")
print(f" RGB标准差: ({region_pixels[:, 0].std():.1f}, "
f"{region_pixels[:, 1].std():.1f}, {region_pixels[:, 2].std():.1f})")
return image_data, best_labels
image_data, image_labels = image_segmentation_example()
```## 11.1
0 聚类算法选择指南
### 11.10.1 算法特点总结
```python
def clustering_algorithm_guide():
"""聚类算法选择指南"""
print("聚类算法选择指南")
print("=" * 50)
algorithms_info = {
'K-means': {
'适用场景': ['球形聚类', '大小相似的簇', '大数据集'],
'优点': ['简单快速', '可扩展性好', '结果稳定'],
'缺点': ['需要预设K值', '对异常值敏感', '假设球形聚类'],
'参数': ['n_clusters', 'init', 'n_init'],
'时间复杂度': 'O(n*k*i*d)',
'最佳使用': '当你知道大概的聚类数量且数据呈球形分布时'
},
'层次聚类': {
'适用场景': ['小到中等数据集', '需要聚类层次结构', '不知道聚类数'],
'优点': ['不需要预设聚类数', '提供层次结构', '确定性结果'],
'缺点': ['计算复杂度高', '对噪声敏感', '难以处理大数据'],
'参数': ['n_clusters', 'linkage', 'distance_threshold'],
'时间复杂度': 'O(n³)',
'最佳使用': '当你需要理解数据的层次结构或数据集较小时'
},
'DBSCAN': {
'适用场景': ['任意形状聚类', '有噪声的数据', '密度不均匀'],
'优点': ['发现任意形状', '自动确定聚类数', '识别噪声点'],
'缺点': ['参数敏感', '密度变化大时效果差', '高维数据困难'],
'参数': ['eps', 'min_samples'],
'时间复杂度': 'O(n log n)',
'最佳使用': '当数据有噪声且聚类形状不规则时'
},
'GMM': {
'适用场景': ['椭圆形聚类', '需要概率分配', '重叠聚类'],
'优点': ['软聚类', '处理椭圆形', '概率解释'],
'缺点': ['需要预设组件数', '对初始化敏感', '计算复杂'],
'参数': ['n_components', 'covariance_type'],
'时间复杂度': 'O(n*k*i*d²)',
'最佳使用': '当你需要概率分配或聚类可能重叠时'
},
'谱聚类': {
'适用场景': ['非凸聚类', '流形数据', '图数据'],
'优点': ['处理非凸形状', '基于图理论', '效果稳定'],
'缺点': ['计算复杂度高', '内存需求大', '参数选择困难'],
'参数': ['n_clusters', 'affinity', 'gamma'],
'时间复杂度': 'O(n³)',
'最佳使用': '当数据在低维流形上或聚类形状复杂时'
}
}
for alg_name, info in algorithms_info.items():
print(f"\n{alg_name}:")
print("-" * 30)
for key, value in info.items():
if isinstance(value, list):
print(f"{key}: {', '.join(value)}")
else:
print(f"{key}: {value}")
return algorithms_info
algorithm_info = clustering_algorithm_guide()11.10.2 决策树指南
python
def clustering_decision_tree():
"""聚类算法选择决策树"""
print("\n聚类算法选择决策树:")
print("=" * 40)
decision_tree = """
开始
│
├─ 你知道聚类数量吗?
│ ├─ 是 ──┐
│ └─ 否 ──┼─ 数据集大小?
│ ├─ 小(<1000) → 层次聚类
│ └─ 大(>1000) → DBSCAN
│
├─ 数据有噪声吗?
│ ├─ 是 → DBSCAN
│ └─ 否 ──┐
│
├─ 聚类形状?
│ ├─ 球形 → K-means
│ ├─ 椭圆形 → GMM
│ ├─ 任意形状 → DBSCAN 或 谱聚类
│ └─ 不确定 → 尝试多种算法
│
├─ 需要概率输出吗?
│ ├─ 是 → GMM
│ └─ 否 → K-means 或 DBSCAN
│
├─ 数据集大小?
│ ├─ 小(<1000) → 任何算法
│ ├─ 中(1000-10000) → K-means, DBSCAN
│ └─ 大(>10000) → K-means, Mini-batch K-means
│
└─ 计算资源限制?
├─ 有限 → K-means
└─ 充足 → 根据数据特点选择
"""
print(decision_tree)
# 创建选择矩阵
selection_matrix = pd.DataFrame({
'数据大小': ['小', '中', '大', '小', '中', '大'],
'聚类形状': ['球形', '球形', '球形', '任意', '任意', '任意'],
'有噪声': ['否', '否', '否', '是', '是', '是'],
'推荐算法': ['K-means/层次', 'K-means', 'K-means', 'DBSCAN', 'DBSCAN', 'DBSCAN'],
'备选算法': ['GMM', 'GMM/DBSCAN', 'Mini-batch K-means', '谱聚类', 'GMM', '谱聚类']
})
print("\n算法选择矩阵:")
print(selection_matrix.to_string(index=False))
clustering_decision_tree()11.11 练习题
练习1:基础聚类
- 使用鸢尾花数据集进行K-means聚类
- 使用肘部法则和轮廓系数确定最佳K值
- 比较聚类结果与真实标签的差异
练习2:算法比较
- 创建三种不同形状的数据集(球形、月牙形、圆形)
- 对每种数据集应用K-means、DBSCAN和GMM
- 分析哪种算法最适合哪种数据形状
练习3:参数调优
- 使用make_circles数据集
- 调优DBSCAN的eps和min_samples参数
- 可视化不同参数组合的聚类效果
练习4:层次聚类
- 使用葡萄酒数据集进行层次聚类
- 绘制树状图并分析聚类层次
- 比较不同链接方法的效果
练习5:实际应用
- 创建一个客户数据集(包含年龄、收入、消费习惯等特征)
- 进行客户细分分析
- 为每个客户群体制定营销策略建议
11.12 小结
在本章中,我们深入学习了聚类分析的各个方面:
核心概念
- 无监督学习:在没有标签的情况下发现数据模式
- 相似性度量:基于距离或密度的相似性定义
- 聚类质量:组内相似性最大化,组间差异最大化
主要算法
- K-means:基于距离的经典聚类算法
- 层次聚类:构建聚类层次结构
- DBSCAN:基于密度的聚类,能处理噪声
- GMM:基于概率分布的软聚类
- 谱聚类:基于图理论的聚类方法
实践技能
- 参数选择:K值选择、参数调优
- 聚类评估:轮廓系数、ARI、信息准则
- 算法选择:根据数据特点选择合适算法
- 实际应用:客户细分、图像分割
关键要点
- 不同算法适用于不同类型的数据和问题
- 聚类评估需要结合多种指标
- 数据预处理对聚类结果有重要影响
- 领域知识在聚类解释中至关重要
算法选择建议
使用K-means当:
- 数据呈球形分布
- 聚类大小相似
- 数据集较大
- 需要快速结果
使用DBSCAN当:
- 数据有噪声
- 聚类形状不规则
- 不知道聚类数量
- 聚类密度不均匀
使用层次聚类当:
- 需要聚类层次结构
- 数据集较小
- 不确定聚类数量
- 需要确定性结果
使用GMM当:
- 需要概率分配
- 聚类可能重叠
- 数据呈椭圆分布
- 需要软聚类
11.13 下一步
现在你已经掌握了聚类分析这个重要的无监督学习技术!在下一章主成分分析中,我们将学习另一个重要的无监督学习方法——降维技术,了解如何在保持数据主要信息的同时减少特征维度。
章节要点回顾:
- ✅ 理解了聚类分析的基本概念和目标
- ✅ 掌握了K-means、层次聚类、DBSCAN等主要算法
- ✅ 学会了聚类评估和参数调优方法
- ✅ 了解了不同算法的适用场景和选择标准
- ✅ 掌握了聚类在实际应用中的使用技巧
- ✅ 能够根据数据特点选择合适的聚类算法