Python训练营打卡——DAY17(2025.5.6)

目录

一、聚类评估指标介绍

1. 轮廓系数 (Silhouette Score)

2. CH 指数 (Calinski-Harabasz Index)

3. DB 指数 (Davies-Bouldin Index)

二、KMeans 聚类

1. 算法原理

2. 确定簇数的方法:肘部法

3. KMeans 算法的优缺点

三、DBSCAN聚类

四、层次聚类

五、作业

1. 环境准备

2. 数据加载与预处理

3. KMeans聚类

4. DBSCAN聚类

5. 层次聚类


今天的主题是无监督算法中的聚类,常利用聚类来发现数据中的模式,并对每一个聚类后的类别特征进行可视化,以此得到新的特征——赋予实际含义。

上述内容分成2天的内容来说,今天学习聚类算法,明天学习基于聚类进一步推断类型。

# 先运行之前预处理好的代码
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('data.csv')    #读取数据


# 先筛选字符串变量 
discrete_features = data.select_dtypes(include=['object']).columns.tolist()
# Home Ownership 标签编码
home_ownership_mapping = {
    'Own Home': 1,
    'Rent': 2,
    'Have Mortgage': 3,
    'Home Mortgage': 4
}
data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)

# Years in current job 标签编码
years_in_job_mapping = {
    '< 1 year': 1,
    '1 year': 2,
    '2 years': 3,
    '3 years': 4,
    '4 years': 5,
    '5 years': 6,
    '6 years': 7,
    '7 years': 8,
    '8 years': 9,
    '9 years': 10,
    '10+ years': 11
}
data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)

# Purpose 独热编码,记得需要将bool类型转换为数值
data = pd.get_dummies(data, columns=['Purpose'])
data2 = pd.read_csv("data.csv") # 重新读取数据,用来做列名对比
list_final = [] # 新建一个空列表,用于存放独热编码后新增的特征名
for i in data.columns:
    if i not in data2.columns:
       list_final.append(i) # 这里打印出来的就是独热编码后的特征名
for i in list_final:
    data[i] = data[i].astype(int) # 这里的i就是独热编码后的特征名



# Term 0 - 1 映射
term_mapping = {
    'Short Term': 0,
    'Long Term': 1
}
data['Term'] = data['Term'].map(term_mapping)
data.rename(columns={'Term': 'Long Term'}, inplace=True) # 重命名列
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()  #把筛选出来的列名转换成列表
 
 # 连续特征用中位数补全
for feature in continuous_features:     
    mode_value = data[feature].mode()[0]            #获取该列的众数。
    data[feature].fillna(mode_value, inplace=True)          #用众数填充该列的缺失值,inplace=True表示直接在原数据上修改。

# 最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,你如果想要不交叉反而实现起来会麻烦很多
# 所以这里我们还是只划分一次数据集
from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1)  # 特征,axis=1表示按列删除
y = data['Credit Default'] # 标签
# # 按照8:2划分训练集和测试集
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 80%训练集,20%测试集
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

一、聚类评估指标介绍

以下是三种常用的聚类效果评估指标,分别用于衡量聚类的质量和簇的分离与紧凑程度:

1. 轮廓系数 (Silhouette Score)

  • 定义:轮廓系数衡量每个样本与其所属簇的紧密程度以及与最近其他簇的分离程度。
  • 取值范围:[-1, 1]
    • 轮廓系数越接近 1,表示样本与其所属簇内其他样本很近,与其他簇很远,聚类效果越好。
    • 轮廓系数越接近 -1,表示样本与其所属簇内样本较远,与其他簇较近,聚类效果越差(可能被错误分类)。
    • 轮廓系数接近 0,表示样本在簇边界附近,聚类效果无明显好坏。
  • 使用建议:选择轮廓系数最高的 k 值作为最佳簇数量。

2. CH 指数 (Calinski-Harabasz Index)

  • 定义:CH 指数是簇间分散度与簇内分散度之比,用于评估簇的分离度和紧凑度。
  • 取值范围:[0, +∞)
    • CH 指数越大,表示簇间分离度越高,簇内紧凑度越高,聚类效果越好。
    • 没有固定的上限,值越大越好。
  • 使用建议:选择 CH 指数最高的 k 值作为最佳簇数量。

3. DB 指数 (Davies-Bouldin Index)

  • 定义:DB 指数衡量簇间距离与簇内分散度的比值,用于评估簇的分离度和紧凑度。
  • 取值范围:[0, +∞)
    • DB 指数越小,表示簇间分离度越高,簇内紧凑度越高,聚类效果越好。
    • 没有固定的上限,值越小越好。
  • 使用建议:选择 DB 指数最低的 k 值作为最佳簇数量。

二、KMeans 聚类

1. 算法原理

KMeans 是一种基于距离的聚类算法,需要预先指定聚类个数,即 k。其核心步骤如下:

  1. 随机选择 k 个样本点作为初始质心(簇中心)。
  2. 计算每个样本点到各个质心的距离,将样本点分配到距离最近的质心所在的簇。
  3. 更新每个簇的质心为该簇内所有样本点的均值。
  4. 重复步骤 2 和 3,直到质心不再变化或达到最大迭代次数为止。

2. 确定簇数的方法:肘部法

  • 肘部法(Elbow Method) 是一种常用的确定 k 值的方法。
  • 原理:通过计算不同 k 值下的簇内平方和(Within-Cluster Sum of Squares, WCSS),绘制 k 与 WCSS 的关系图。
  • 选择标准:在图中找到“肘部”点,即 WCSS 下降速率明显减缓的 k 值,通常认为是最佳簇数。这是因为增加 k 值带来的收益(WCSS 减少)在该点后变得不显著。

3. KMeans 算法的优缺点

优点:

  • 简单高效:算法实现简单,计算速度快,适合处理大规模数据集。
  • 适用性强:对球形或紧凑的簇效果较好,适用于特征空间中簇分布较为均匀的数据。
  • 易于解释:聚类结果直观,簇中心具有明确的物理意义。

缺点:

  • 需预先指定 k 值:对簇数量 k 的选择敏感,不合适的 k 会导致聚类效果较差。
  • 对初始质心敏感:初始质心的随机选择可能导致结果不稳定或陷入局部最优(可通过 KMeans++ 初始化方法缓解)。
  • 对噪声和异常值敏感:异常值可能会显著影响质心的位置,导致聚类结果失真。
  • 不适合非球形簇:对非线性可分或形状复杂的簇效果较差,无法处理簇密度不均的情况。
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, 11)  # 测试 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()
k=2, 惯性: 218529.50, 轮廓系数: 0.320, CH 指数: 479.34, DB 指数: 3.222
k=3, 惯性: 207982.87, 轮廓系数: 0.209, CH 指数: 441.88, DB 指数: 2.906
k=4, 惯性: 200477.28, 轮廓系数: 0.220, CH 指数: 399.12, DB 指数: 2.441
k=5, 惯性: 192940.36, 轮廓系数: 0.224, CH 指数: 384.19, DB 指数: 2.042
k=6, 惯性: 185411.81, 轮廓系数: 0.227, CH 指数: 380.64, DB 指数: 1.733
k=7, 惯性: 178444.49, 轮廓系数: 0.130, CH 指数: 378.31, DB 指数: 1.633
k=8, 惯性: 174920.27, 轮廓系数: 0.143, CH 指数: 352.31, DB 指数: 1.817
k=9, 惯性: 167383.96, 轮廓系数: 0.150, CH 指数: 364.27, DB 指数: 1.636
k=10, 惯性: 159824.84, 轮廓系数: 0.156, CH 指数: 378.43, DB 指数: 1.502

观察图的方法:

  1. 肘部法则图: 找下降速率变慢的拐点,这里都差不多
  2. 轮廓系数图:找局部最高点,这里选6不能选7
  3. CH指数图: 找局部最高点,这里选7之前的都还行
  4. DB指数图:找局部最低点,这里选6 7 9 10都行

综上,选择6比较合适。

  • 为什么选择局部最优的点,因为比如簇间差异,分得越细越好,但是k太细了没价值,所以要有取舍。
  • 比如k可以选3和5,推荐选择5,能包含更多信息
# 提示用户选择 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())

KMeans Cluster labels (k=6) added to X:
KMeans_Cluster
0                 5205
1                 1220
2                  903
3                  128
4                   34
5                   10
dtype: int64

三、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.3, 0.8, 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)
eps=0.3, min_samples=3, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.3, min_samples=4, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.3, min_samples=5, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.3, min_samples=6, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.3, min_samples=7, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.4, min_samples=3, 簇数: 1, 噪声点: 7497, 无法计算评估指标
eps=0.4, min_samples=4, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.4, min_samples=5, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.4, min_samples=6, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.4, min_samples=7, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.5, min_samples=3, 簇数: 21, 噪声点: 7420, 轮廓系数: 0.494, CH 指数: 89.64, DB 指数: 0.613
eps=0.5, min_samples=4, 簇数: 5, 噪声点: 7473, 轮廓系数: 0.463, CH 指数: 83.11, DB 指数: 0.749
eps=0.5, min_samples=5, 簇数: 1, 噪声点: 7495, 无法计算评估指标
eps=0.5, min_samples=6, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.5, min_samples=7, 簇数: 0, 噪声点: 7500, 无法计算评估指标
eps=0.6, min_samples=3, 簇数: 56, 噪声点: 7214, 轮廓系数: 0.267, CH 指数: 58.95, DB 指数: 0.953
eps=0.6, min_samples=4, 簇数: 21, 噪声点: 7356, 轮廓系数: 0.306, CH 指数: 72.52, DB 指数: 0.988
eps=0.6, min_samples=5, 簇数: 7, 噪声点: 7433, 轮廓系数: 0.347, CH 指数: 42.56, DB 指数: 0.988
eps=0.6, min_samples=6, 簇数: 6, 噪声点: 7452, 轮廓系数: 0.414, CH 指数: 48.32, DB 指数: 0.843
eps=0.6, min_samples=7, 簇数: 2, 噪声点: 7486, 轮廓系数: 0.758, CH 指数: 109.90, DB 指数: 0.318
eps=0.7, min_samples=3, 簇数: 90, 噪声点: 6800, 轮廓系数: 0.028, CH 指数: 22.65, DB 指数: 0.918
eps=0.7, min_samples=4, 簇数: 39, 噪声点: 7047, 轮廓系数: -0.026, CH 指数: 20.20, DB 指数: 0.955
eps=0.7, min_samples=5, 簇数: 15, 噪声点: 7214, 轮廓系数: -0.013, CH 指数: 26.35, DB 指数: 1.010
eps=0.7, min_samples=6, 簇数: 5, 噪声点: 7313, 轮廓系数: 0.015, CH 指数: 25.90, DB 指数: 1.101
eps=0.7, min_samples=7, 簇数: 5, 噪声点: 7337, 轮廓系数: 0.035, CH 指数: 26.04, DB 指数: 1.337

results_df
epsmin_samplesn_clustersn_noisesilhouettech_scoredb_score
00.532174200.49393289.6432710.612520
10.54574730.46318383.1122610.748730
20.635672140.26718058.9531850.952780
30.642173560.30634172.5165880.987645
40.65774330.34692242.5564370.988006
50.66674520.41437748.3216910.842974
60.67274860.757909109.8955950.317504
70.739068000.02838222.6521870.917994
80.74397047-0.02636020.1957620.954573
90.75157214-0.01337526.3538481.010414
100.76573130.01527125.9010561.101364
110.77573370.03499326.0389241.337139
# 绘制评估指标图,增加点论文中的工作量
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 = 0.6  # 根据图表调整
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())

DBSCAN Cluster labels (eps=0.6, min_samples=6) added to X:
DBSCAN_Cluster
-1                7452
 0                  12
 2                   9
 3                   8
 4                   7
 1                   6
 5                   6
dtype: int64

从结果来看,这个聚类是失败的,因为没有少数簇的数目太少,也可能是dbscan这个算法不太合适。

四、层次聚类

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=2, 轮廓系数: 0.336, CH 指数: 685.66, DB 指数: 2.659
n_clusters=3, 轮廓系数: 0.242, CH 指数: 659.40, DB 指数: 2.327
n_clusters=4, 轮廓系数: 0.254, CH 指数: 565.74, DB 指数: 2.160
n_clusters=5, 轮廓系数: 0.276, CH 指数: 519.91, DB 指数: 2.110
n_clusters=6, 轮廓系数: 0.284, CH 指数: 494.24, DB 指数: 1.860
n_clusters=7, 轮廓系数: 0.295, CH 指数: 482.64, DB 指数: 1.680
n_clusters=8, 轮廓系数: 0.297, CH 指数: 479.17, DB 指数: 1.435
n_clusters=9, 轮廓系数: 0.301, CH 指数: 481.85, DB 指数: 1.283
n_clusters=10, 轮廓系数: 0.309, CH 指数: 489.27, DB 指数: 1.269

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

# 使用选择的簇数进行 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())

Agglomerative Cluster labels (n_clusters=10) added to X:
Agglo_Cluster
4                5230
1                 778
2                 771
9                 409
5                 127
6                  96
0                  37
3                  34
7                  10
8                   8
dtype: int64

# 层次聚类的树状图可视化
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. 纵轴代表距离 ,反映了在聚类过程中,不同样本或簇合并时的距离度量值。距离越大,意味着两个样本或簇之间的差异越大;距离越小,则差异越小。

五、作业

对心脏病数据集进行聚类

1. 环境准备

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA
from scipy.cluster.hierarchy import dendrogram, linkage

2. 数据加载与预处理

# 1. 数据加载与预处理
# 读取数据集(假设文件在当前目录下)
df = pd.read_csv('heart.csv')

# 检查数据
print("数据前5行:")
print(df.head())

# 提取特征(排除目标变量)
X = df.drop('target', axis=1)

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

输出结果:

数据前5行:
   age  sex  cp  trestbps  chol  fbs  restecg  thalach  exang  oldpeak  slope  \
0   63    1   3       145   233    1        0      150      0      2.3      0   
1   37    1   2       130   250    0        1      187      0      3.5      0   
2   41    0   1       130   204    0        0      172      0      1.4      2   
3   56    1   1       120   236    0        1      178      0      0.8      2   
4   57    0   0       120   354    0        1      163      1      0.6      2   

   ca  thal  target  
0   0     1       1  
1   0     2       1  
2   0     2       1  
3   0     2       1  
4   0     2       1  

3. KMeans聚类

# KMeans聚类
# 肘部法则选择K值
sse = []
k_range = range(2, 10)
for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X_scaled)
    sse.append(kmeans.inertia_)

# 绘制肘部图
plt.figure(figsize=(8, 5))
plt.plot(k_range, sse, 'bo-')
plt.xlabel('簇数量 (K)')
plt.ylabel('SSE')
plt.title('KMeans肘部法则')
plt.show()

# 选择K=3进行聚类
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)

# PCA降维可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

plt.figure(figsize=(8, 5))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=kmeans_labels, cmap='viridis', edgecolor='k')
plt.title('KMeans聚类结果 (PCA降维)')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.colorbar(label='簇标签')
plt.show()

# 计算轮廓系数
print("KMeans轮廓系数:", silhouette_score(X_scaled, kmeans_labels))

KMeans轮廓系数: 0.11385106870356003


4. DBSCAN聚类

# DBSCAN聚类
# 寻找最优eps(通过k-距离图)
from sklearn.neighbors import NearestNeighbors
nn = NearestNeighbors(n_neighbors=4)
nbrs = nn.fit(X_scaled)
distances, _ = nbrs.kneighbors(X_scaled)
distances = np.sort(distances[:, -1], axis=0)

plt.figure(figsize=(8, 5))
plt.plot(distances)
plt.xlabel('样本')
plt.ylabel('距离')
plt.title('DBSCAN k-距离图')
plt.show()

# 选择eps=3, min_samples=4
dbscan = DBSCAN(eps=3, min_samples=4)
dbscan_labels = dbscan.fit_predict(X_scaled)

# 可视化结果
plt.figure(figsize=(8, 5))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels, cmap='viridis', edgecolor='k')
plt.title('DBSCAN聚类结果 (PCA降维)')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.colorbar(label='簇标签')
plt.show()

# 计算轮廓系数(排除噪声点)
mask = dbscan_labels != -1
print("DBSCAN轮廓系数(排除噪声):", silhouette_score(X_scaled[mask], dbscan_labels[mask]))

DBSCAN轮廓系数(排除噪声): 0.07772378631602891


5. 层次聚类

# 层次聚类
# 绘制树状图
plt.figure(figsize=(12, 7))
linked = linkage(X_scaled, method='ward')
dendrogram(linked, truncate_mode='level', p=3)
plt.title('层次聚类树状图')
plt.xlabel('样本索引')
plt.ylabel('距离')
plt.show()

# 选择3个簇进行聚类
agg = AgglomerativeClustering(n_clusters=3)
agg_labels = agg.fit_predict(X_scaled)

# 可视化结果
plt.figure(figsize=(8, 5))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=agg_labels, cmap='viridis', edgecolor='k')
plt.title('层次聚类结果 (PCA降维)')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.colorbar(label='簇标签')
plt.show()

# 计算轮廓系数
print("层次聚类轮廓系数:", silhouette_score(X_scaled, agg_labels))

层次聚类轮廓系数: 0.09737426014314339


@浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值