python打卡 day17 常见聚类算法

目录

聚类算法与评估指标笔记

一、聚类性能评估指标

1.1 内部评估指标(无需真实标签)

轮廓系数 (Silhouette Coefficient)

戴维森堡丁指数 (Davies-Bouldin Index)

1.2 外部评估指标(需真实标签)

调整兰德指数 (Adjusted Rand Index)

FMI (Fowlkes-Mallows Index)

二、经典聚类算法详解

2.1 K-Means聚类

算法流程

特点与局限

改进方法

2.2 DBSCAN聚类

算法流程

核心概念

特点与局限

2.3 层次聚类 (Hierarchical Clustering)

算法流程

可视化工具

特点与局限

三、算法对比与选型指南

3.1 关键特性对比

3.2 选型决策树

3.3 参数调优建议

K-Means

DBSCAN

层次聚类

四、实战应用案例

4.1 客户细分(K-Means)

4.2 异常检测(DBSCAN)

4.3 生物分类(层次聚类)

作业:heart 数据集聚类


聚类算法与评估指标笔记

一、聚类性能评估指标

1.1 内部评估指标(无需真实标签)

轮廓系数 (Silhouette Coefficient)
  • 计算公式

    • a(i): 样本i到同簇其他点的平均距离

    • b(i): 样本i到最近其他簇所有点的平均距离

  • 范围:[-1, 1]

  • 解读

    • 接近1表示聚类合理

    • 接近0表示样本在两个簇边界

    • 负值表示可能被分错簇

from sklearn.metrics import silhouette_score
score = silhouette_score(X, labels)
 
戴维森堡丁指数 (Davies-Bouldin Index)
  • 计算公式

  • 特点

    • 值越小表示聚类效果越好

    • 对凸簇效果更好

from sklearn.metrics import davies_bouldin_score
db_score = davies_bouldin_score(X, labels)
 

1.2 外部评估指标(需真实标签)

调整兰德指数 (Adjusted Rand Index)
  • 特点

    • 衡量两个聚类结果的相似度

    • 取值范围[-1,1],1表示完全一致

    • 对随机标签校正为0

from sklearn.metrics import adjusted_rand_score
ari = adjusted_rand_score(true_labels, pred_labels)
 
FMI (Fowlkes-Mallows Index)
  • 计算公式

    • TP: 同属真实簇和预测簇的点对数

    • FP: 仅同属预测簇的点对数

    • FN: 仅同属真实簇的点对数

from sklearn.metrics import fowlkes_mallows_score
fmi = fowlkes_mallows_score(true_labels, pred_labels)
 

二、经典聚类算法详解

2.1 K-Means聚类

算法流程
  1. 初始化:随机选择k个点作为初始质心

  2. 分配步骤:将每个点分配到最近的质心所属簇

  3. 更新步骤:重新计算每个簇的质心

  4. 迭代:重复2-3步直到质心不再变化或达到最大迭代次数

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(X)
 
特点与局限
  • 优点

    • 计算效率高,适合大规模数据

    • 对球形簇效果良好

  • 缺点

    • 需要预先指定k值

    • 对初始质心敏感

    • 对非凸簇效果不佳

改进方法
  • K-Means++:优化初始质心选择

  • Mini-Batch K-Means:适合大数据集的近似算法

2.2 DBSCAN聚类

算法流程
  1. 参数设置:确定邻域半径(eps)和最小点数(min_samples)

  2. 核心点识别:标记邻域内点数≥min_samples的点

  3. 簇扩展

    • 从核心点出发,密度可达的所有点归为同一簇

    • 包括边界点(属于某核心点邻域但自身非核心)

  4. 噪声识别:未归入任何簇的点标记为噪声(-1)

from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.5, min_samples=5)
labels = dbscan.fit_predict(X)
 
核心概念
  • 密度直达:点p在q的邻域内且q是核心点

  • 密度可达:存在一系列核心点使p到q密度直达链

  • 密度相连:存在核心点o使p和q都密度可达于o

特点与局限
  • 优点

    • 无需预先指定簇数

    • 能发现任意形状的簇

    • 可识别噪声点

  • 缺点

    • 对参数敏感

    • 高维数据效果下降

    • 密度不均匀时效果差

2.3 层次聚类 (Hierarchical Clustering)

算法流程
  1. 初始化:将每个样本视为一个簇

  2. 合并最近簇

    • 单连接(Single Linkage):两簇中最近样本的距离

    • 全连接(Complete Linkage):两簇中最远样本的距离

    • 平均连接(Average Linkage):两簇所有样本对平均距离

  3. 更新距离矩阵:重新计算新簇与其他簇的距离

  4. 迭代:重复2-3步直到所有样本聚为一类或达到指定簇数

from sklearn.cluster import AgglomerativeClustering

agg = AgglomerativeClustering(n_clusters=3, linkage='ward')
labels = agg.fit_predict(X)
 
可视化工具

from scipy.cluster.hierarchy import dendrogram, linkage

Z = linkage(X, 'ward')
plt.figure(figsize=(10, 5))
dendrogram(Z)
plt.show()
 
特点与局限
  • 优点

    • 可生成层次化的聚类结果

    • 可视化直观(树状图)

    • 不需要预先指定簇数

  • 缺点

    • 计算复杂度高(O(n³))

    • 对噪声敏感

    • 一旦形成簇不可撤销

三、算法对比与选型指南

3.1 关键特性对比

特性K-MeansDBSCAN层次聚类
簇形状球形任意取决于连接方式
簇大小相近可不同可不同
噪声处理优秀敏感
参数敏感
时间复杂度O(n)O(n log n)O(n³)
空间复杂度
最佳适用场景大规模均匀球形簇含噪声的任意形状簇小规模层次化数据

3.2 选型决策树

是否需要确定簇数?
├── 是 → 数据是否近似球形分布?
│   ├── 是 → K-Means
│   └── 否 → 层次聚类
└── 否 → 数据是否密度不均匀?
    ├── 是 → 层次聚类
    └── 否 → DBSCAN
 

3.3 参数调优建议

K-Means
  • 肘部法则确定最佳k值:

    distortions = []
    for k in range(1, 10):
        kmeans = KMeans(n_clusters=k).fit(X)
        distortions.append(kmeans.inertia_)  # 簇内平方和
    plt.plot(range(1,10), distortions)
     
DBSCAN
  • k-距离图选择eps:

    from sklearn.neighbors import NearestNeighbors
    neigh = NearestNeighbors(n_neighbors=5)
    distances, _ = neigh.fit(X).kneighbors(X)
    plt.plot(np.sort(distances[:, -1]))
     
层次聚类
  • 树状图分析确定切割高度:

    dendrogram(Z, truncate_mode='level', p=3)
     

四、实战应用案例

4.1 客户细分(K-Means)

# 特征标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(customer_data[['age', 'income', 'spending']])

# 确定最佳k值
kmeans = KMeans(n_clusters=4, random_state=42)
kmeans.fit(X_scaled)
customer_data['cluster'] = kmeans.labels_

# 分析簇特征
cluster_profile = customer_data.groupby('cluster').mean()
 

4.2 异常检测(DBSCAN)

# 网络入侵检测
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
X_norm = scaler.fit_transform(network_logs[['duration', 'packets', 'bytes']])

dbscan = DBSCAN(eps=0.3, min_samples=10)
anomalies = dbscan.fit_predict(X_norm)

print(f"检测到异常点比例: {(anomalies == -1).mean():.1%}")
 

4.3 生物分类(层次聚类)

# 基因表达数据聚类
from scipy.cluster.hierarchy import fcluster

Z = linkage(gene_data.T, 'average')  # 对基因(非样本)聚类
clusters = fcluster(Z, t=3, criterion='maxclust')

# 热图可视化
import seaborn as sns
sns.clustermap(gene_data, row_cluster=False, col_linkage=Z)
 

作业:heart 数据集聚类

步骤一:数据预处理

# 先运行之前预处理好的代码
import pandas as pd
import pandas as pd    #用于数据处理和分析,可处理表格数据。
import numpy as np     #用于数值计算,提供了高效的数组操作。
import matplotlib.pyplot as plt    #用于绘制各种类型的图表
import seaborn as sns   #基于matplotlib的高级绘图库,能绘制更美观的统计图形。
import warnings
warnings.filterwarnings("ignore")
 
 # 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号
data = pd.read_csv('heart.csv')    #读取数据
data.head()
# 最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,你如果想要不交叉反而实现起来会麻烦很多
# 所以这里我们还是只划分一次数据集
from sklearn.model_selection import train_test_split
X = data.drop(['fbs'], axis=1)  # 特征,axis=1表示按列删除
y = data['fbs'] # 标签

步骤二:KMeans 聚类

import numpy as np
import pandas as pd
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns

# 标准化数据(聚类前通常需要标准化)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# X_scaled
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import matplotlib.pyplot as plt
import seaborn as sns

# 评估不同 k 值下的指标
k_range = range(2, 21)  # 测试 k 从 2 到 10
inertia_values = []
silhouette_scores = []
ch_scores = []
db_scores = []

for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans_labels = kmeans.fit_predict(X_scaled)
    inertia_values.append(kmeans.inertia_)  # 惯性(肘部法则)
    silhouette = silhouette_score(X_scaled, kmeans_labels)  # 轮廓系数
    silhouette_scores.append(silhouette)
    ch = calinski_harabasz_score(X_scaled, kmeans_labels)  # CH 指数
    ch_scores.append(ch)
    db = davies_bouldin_score(X_scaled, kmeans_labels)  # DB 指数
    db_scores.append(db)
    print(f"k={k}, 惯性: {kmeans.inertia_:.2f}, 轮廓系数: {silhouette:.3f}, CH 指数: {ch:.2f}, DB 指数: {db:.3f}")

# 绘制评估指标图
plt.figure(figsize=(15, 10))

# 肘部法则图(Inertia)
plt.subplot(2, 2, 1)
plt.plot(k_range, inertia_values, marker='o')
plt.title('肘部法则确定最优聚类数 k(惯性,越小越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('惯性')
plt.grid(True)

# 轮廓系数图
plt.subplot(2, 2, 2)
plt.plot(k_range, silhouette_scores, marker='o', color='orange')
plt.title('轮廓系数确定最优聚类数 k(越大越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('轮廓系数')
plt.grid(True)

# CH 指数图
plt.subplot(2, 2, 3)
plt.plot(k_range, ch_scores, marker='o', color='green')
plt.title('Calinski-Harabasz 指数确定最优聚类数 k(越大越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('CH 指数')
plt.grid(True)

# DB 指数图
plt.subplot(2, 2, 4)
plt.plot(k_range, db_scores, marker='o', color='red')
plt.title('Davies-Bouldin 指数确定最优聚类数 k(越小越好)')
plt.xlabel('聚类数 (k)')
plt.ylabel('DB 指数')
plt.grid(True)

plt.tight_layout()
plt.show()

看图方法:

1. 肘部法则图: 找下降速率变慢的拐点,这里都差不多

2. 轮廓系数图:找局部最高点,6,7,9都行

3. CH指数图: 找局部最高点,这里选10之前的都还行

4. DB指数图:找局部最低点,这里选6 7 8 9都行

综上,选择6比较合适。

# 提示用户选择 k 值
selected_k = 6

# 使用选择的 k 值进行 KMeans 聚类
kmeans = KMeans(n_clusters=selected_k, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
X['KMeans_Cluster'] = kmeans_labels

# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# KMeans 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=kmeans_labels, palette='viridis')
plt.title(f'KMeans Clustering with k={selected_k} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()

# 打印 KMeans 聚类标签的前几行
print(f"KMeans Cluster labels (k={selected_k}) added to X:")
print(X[['KMeans_Cluster']].value_counts())

步骤三:DBSCAN聚类

import numpy as np
import pandas as pd
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import matplotlib.pyplot as plt
import seaborn as sns



# 评估不同 eps 和 min_samples 下的指标
# eps这个参数表示邻域的半径,min_samples表示一个点被认为是核心点所需的最小样本数。
# min_samples这个参数表示一个核心点所需的最小样本数。

eps_range = np.arange(0.9, 2.6, 0.1)  # 测试 eps 从 0.3 到 0.7
min_samples_range = range(3, 8)  # 测试 min_samples 从 3 到 7
results = []

for eps in eps_range:
    for min_samples in min_samples_range:
        dbscan = DBSCAN(eps=eps, min_samples=min_samples)
        dbscan_labels = dbscan.fit_predict(X_scaled)
        # 计算簇的数量(排除噪声点 -1)
        n_clusters = len(np.unique(dbscan_labels)) - (1 if -1 in dbscan_labels else 0)
        # 计算噪声点数量
        n_noise = list(dbscan_labels).count(-1)
        # 只有当簇数量大于 1 且有有效簇时才计算评估指标
        if n_clusters > 1:
            # 排除噪声点后计算评估指标
            mask = dbscan_labels != -1
            if mask.sum() > 0:  # 确保有非噪声点
                silhouette = silhouette_score(X_scaled[mask], dbscan_labels[mask])
                ch = calinski_harabasz_score(X_scaled[mask], dbscan_labels[mask])
                db = davies_bouldin_score(X_scaled[mask], dbscan_labels[mask])
                results.append({
                    'eps': eps,
                    'min_samples': min_samples,
                    'n_clusters': n_clusters,
                    'n_noise': n_noise,
                    'silhouette': silhouette,
                    'ch_score': ch,
                    'db_score': db
                })
                print(f"eps={eps:.1f}, min_samples={min_samples}, 簇数: {n_clusters}, 噪声点: {n_noise}, "
                      f"轮廓系数: {silhouette:.3f}, CH 指数: {ch:.2f}, DB 指数: {db:.3f}")
        else:
            print(f"eps={eps:.1f}, min_samples={min_samples}, 簇数: {n_clusters}, 噪声点: {n_noise}, 无法计算评估指标")

# 将结果转为 DataFrame 以便可视化和选择参数
results_df = pd.DataFrame(results)

# 绘制评估指标图,增加点论文中的工作量
plt.figure(figsize=(15, 10))
# 轮廓系数图
plt.subplot(2, 2, 1)
for min_samples in min_samples_range:
    subset = results_df[results_df['min_samples'] == min_samples] # 
    plt.plot(subset['eps'], subset['silhouette'], marker='o', label=f'min_samples={min_samples}')
plt.title('轮廓系数确定最优参数(越大越好)')
plt.xlabel('eps')
plt.ylabel('轮廓系数')
plt.legend()
plt.grid(True)

# CH 指数图
plt.subplot(2, 2, 2)
for min_samples in min_samples_range:
    subset = results_df[results_df['min_samples'] == min_samples]
    plt.plot(subset['eps'], subset['ch_score'], marker='o', label=f'min_samples={min_samples}')
plt.title('Calinski-Harabasz 指数确定最优参数(越大越好)')
plt.xlabel('eps')
plt.ylabel('CH 指数')
plt.legend()
plt.grid(True)

# DB 指数图
plt.subplot(2, 2, 3)
for min_samples in min_samples_range:
    subset = results_df[results_df['min_samples'] == min_samples]
    plt.plot(subset['eps'], subset['db_score'], marker='o', label=f'min_samples={min_samples}')
plt.title('Davies-Bouldin 指数确定最优参数(越小越好)')
plt.xlabel('eps')
plt.ylabel('DB 指数')
plt.legend()
plt.grid(True)

# 簇数量图
plt.subplot(2, 2, 4)
for min_samples in min_samples_range:
    subset = results_df[results_df['min_samples'] == min_samples]
    plt.plot(subset['eps'], subset['n_clusters'], marker='o', label=f'min_samples={min_samples}')
plt.title('簇数量变化')
plt.xlabel('eps')
plt.ylabel('簇数量')
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

# 选择 eps 和 min_samples 值(根据图表选择最佳参数)
selected_eps = 1.5  # 根据图表调整
selected_min_samples = 6  # 根据图表调整

# 使用选择的参数进行 DBSCAN 聚类
dbscan = DBSCAN(eps=selected_eps, min_samples=selected_min_samples)
dbscan_labels = dbscan.fit_predict(X_scaled)
X['DBSCAN_Cluster'] = dbscan_labels

# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# DBSCAN 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=dbscan_labels, palette='viridis')
plt.title(f'DBSCAN Clustering with eps={selected_eps}, min_samples={selected_min_samples} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()

# 打印 DBSCAN 聚类标签的分布
print(f"DBSCAN Cluster labels (eps={selected_eps}, min_samples={selected_min_samples}) added to X:")
print(X[['DBSCAN_Cluster']].value_counts())

步骤四:层次聚类

Agglomerative Clustering 是一种自底向上的层次聚类方法,初始时每个样本是一个簇,然后逐步合并最相似的簇,直到达到指定的簇数量或满足停止条件。由于它需要指定簇数量(类似于 KMeans),我将通过测试不同的簇数量 n_clusters 来评估聚类效果,并使用轮廓系数(Silhouette Score)、CH 指数(Calinski-Harabasz Index)和 DB 指数(Davies-Bouldin Index)作为评估指标。

import numpy as np
import pandas as pd
from sklearn.cluster import AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import matplotlib.pyplot as plt
import seaborn as sns

# 标准化数据
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 评估不同 n_clusters 下的指标
n_clusters_range = range(2, 11)  # 测试簇数量从 2 到 10
silhouette_scores = []
ch_scores = []
db_scores = []

for n_clusters in n_clusters_range:
    agglo = AgglomerativeClustering(n_clusters=n_clusters, linkage='ward')  # 使用 Ward 准则合并簇
    agglo_labels = agglo.fit_predict(X_scaled)
    
    # 计算评估指标
    silhouette = silhouette_score(X_scaled, agglo_labels)
    ch = calinski_harabasz_score(X_scaled, agglo_labels)
    db = davies_bouldin_score(X_scaled, agglo_labels)
    
    silhouette_scores.append(silhouette)
    ch_scores.append(ch)
    db_scores.append(db)
    
    print(f"n_clusters={n_clusters}, 轮廓系数: {silhouette:.3f}, CH 指数: {ch:.2f}, DB 指数: {db:.3f}")

# 绘制评估指标图
plt.figure(figsize=(15, 5))

# 轮廓系数图
plt.subplot(1, 3, 1)
plt.plot(n_clusters_range, silhouette_scores, marker='o')
plt.title('轮廓系数确定最优簇数(越大越好)')
plt.xlabel('簇数量 (n_clusters)')
plt.ylabel('轮廓系数')
plt.grid(True)

# CH 指数图
plt.subplot(1, 3, 2)
plt.plot(n_clusters_range, ch_scores, marker='o')
plt.title('Calinski-Harabasz 指数确定最优簇数(越大越好)')
plt.xlabel('簇数量 (n_clusters)')
plt.ylabel('CH 指数')
plt.grid(True)

# DB 指数图
plt.subplot(1, 3, 3)
plt.plot(n_clusters_range, db_scores, marker='o')
plt.title('Davies-Bouldin 指数确定最优簇数(越小越好)')
plt.xlabel('簇数量 (n_clusters)')
plt.ylabel('DB 指数')
plt.grid(True)

plt.tight_layout()
plt.show()

# 提示用户选择 n_clusters 值(这里可以根据图表选择最佳簇数)
selected_n_clusters = 7  # 示例值,根据图表调整

# 使用选择的簇数进行 Agglomerative Clustering 聚类
agglo = AgglomerativeClustering(n_clusters=selected_n_clusters, linkage='ward')
agglo_labels = agglo.fit_predict(X_scaled)
X['Agglo_Cluster'] = agglo_labels

# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# Agglomerative Clustering 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=agglo_labels, palette='viridis')
plt.title(f'Agglomerative Clustering with n_clusters={selected_n_clusters} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()

# 打印 Agglomerative Clustering 聚类标签的分布
print(f"Agglomerative Cluster labels (n_clusters={selected_n_clusters}) added to X:")
print(X[['Agglo_Cluster']].value_counts())

# 层次聚类的树状图可视化
from scipy.cluster import hierarchy
import matplotlib.pyplot as plt

# 假设 X_scaled 是标准化后的数据
# 计算层次聚类的链接矩阵
Z = hierarchy.linkage(X_scaled, method='ward')  # 'ward' 是常用的合并准则

# 绘制树状图
plt.figure(figsize=(10, 6))
hierarchy.dendrogram(Z, truncate_mode='level',p=3  )  # p 控制显示的层次深度
# hierarchy.dendrogram(Z, truncate_mode='level')  # 不用p这个参数,可以显示全部的深度
plt.title('Dendrogram for Agglomerative Clustering')
plt.xlabel('Cluster Size')
plt.ylabel('Distance')
plt.show()

1. 横坐标代表每个簇对应样本的数据,这些样本数目加一起是整个数据集的样本数目。这是从上到下进行截断,p=3显示最后3层,不用p这个参数会显示全部。

2. 纵轴代表距离 ,反映了在聚类过程中,不同样本或簇合并时的距离度量值。距离越大,意味着两个样本或簇之间的差异越大;距离越小,则差异越小。

@浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值