day 17 无监督学习之聚类算法

news2025/7/9 20:51:58

一、聚类流程

1. 利用聚类发现数据模式

无监督算法中的聚类,目的就是将数据点划分成不同的组或 “簇”,使得同一簇内的数据点相似度较高,而不同簇的数据点相似度较低,从而发现数据中隐藏的模式。

2. 对聚类后的类别特征进行可视化

通过可视化,比如使用散点图、柱状图、热力图等,能够更直观地理解每个聚类所包含的数据特征。例如,如果是数值型数据,可以通过可视化展示不同聚类在各个特征维度上的分布情况,像均值、中位数、标准差等统计量的差异。

3. 得到新特征并赋予实际含义

通过聚类和可视化分析,可以提取出新的特征。比如,根据聚类结果,可以为每个数据点添加一个 “簇标签” 特征,这个特征表示该数据点所属的聚类类别。同时,结合对聚类类别特征的理解,可以赋予这些新特征实际含义。

二、聚类评估指标介绍

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

1. 轮廓系数 (Silhouette Score)

定义:轮廓系数衡量每个样本与其所属簇的紧密程度以及与最近其他簇的分离程度。

计算过程:对每个样本,首先计算它到同簇内其他样本的平均距离(记为 a),这代表了样本与自己所在簇的紧密程度;然后计算它到其他簇的最小平均距离(记为 b),b 反映了样本与最近其他簇的分离程度。对所有样本的轮廓系数求平均,就得到了整个数据集的轮廓系数。

取值范围:[-1, 1]

  • 轮廓系数越接近 1,表示样本与其所属簇内其他样本很近,与其他簇很远,聚类效果越好。
  • 轮廓系数越接近 -1,表示样本与其所属簇内样本较远,与其他簇较近,聚类效果越差(可能被错误分类)。
  • 轮廓系数接近 0,表示样本在簇边界附近,聚类效果无明显好坏。

使用建议:选择轮廓系数最高时的 k 值作为最佳簇数量。


2. CH 指数 (Calinski-Harabasz Index)

定义:CH 指数是簇间分散度与簇内分散度之比,用于评估簇的分离度和紧凑度。

计算过程:涉及到簇间的协方差矩阵和簇内的协方差矩阵。先计算簇间的平方和(SSB)与簇内的平方和(SSW),SSB 反映了簇间的分散程度,值越大表示簇间差异越大;SSW 反映了簇内的分散程度,值越小表示簇内越紧凑。

其中 k 是簇的数量,n 是样本总数。

取值范围:[0, +∞)

  • CH 指数越大,表示簇间分离度越高,簇内紧凑度越高,聚类效果越好。
  • 没有固定的上限,值越大越好。

使用建议:选择 CH 指数最高时的 k 值作为最佳簇数量。


3. DB 指数 (Davies-Bouldin Index)

定义:DB 指数衡量簇间距离与簇内分散度的比值,用于评估簇的分离度和紧凑度。

计算过程:对于每个簇,先计算它与其他簇的相似度(基于簇内平均距离和簇间距离),然后取所有簇相似度的平均值作为 DB 指数。

取值范围:[0, +∞)

  • DB 指数越小(“麻烦指数”),表示簇间分离度越高,簇内紧凑度越高,聚类效果越好。
  • 没有固定的上限,值越小越好。

使用建议:选择 DB 指数最低的 k 值作为最佳簇数量。

三、常见聚类算法

# 先运行之前预处理好的代码
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)

K-Means 算法

KMeans 是一种基于距离的聚类算法,需要预先指定聚类个数,即 `k`


核心步骤:

1. 随机选择 `k` 个样本点作为初始质心(簇中心)。

2. 计算每个样本点到各个质心的距离,将样本点分配到距离最近的质心所在的簇。

3. 更新每个簇的质心为该簇内所有样本点的均值。

4. 重复步骤 2 和 3,直到质心不再变化或达到最大迭代次数为止。


肘部法(Elbow Method)

它是一种常用的确定 k 值的方法。

原理:通过计算不同 k 值下的簇内平方和(Within-Cluster Sum of Squares, WCSS),绘制 k 与 WCSS 的关系图。

选择标准:在图中找到“肘部”点,即 WCSS 下降速率明显减缓的 k 值,通常认为是最佳簇数。这是因为增加 k 值带来的收益(WCSS 减少)在该点后变得不显著。

本质:K - Means 算法就是通过不断迭代,最小化簇内平方和(WCSS),让每个簇内的样本尽可能紧密地聚集在一起,使得这个值收敛到一个相对最小的值 。


优点:

简单高效:算法实现简单,计算速度快,适合处理大规模数据集。

适用性强:对球形或紧凑的簇效果较好,适用于特征空间中簇分布较为均匀的数据。

易于解释:簇中心有明确意义。例如在客户聚类中,簇中心可以代表该类客户的典型特征。

缺点:

需预先指定 `k` 值:对簇数量 `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)
    # 惯性表示每个样本到其所属簇中心的距离平方和,用于肘部法则来选择最优k值。
    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。
  3. CH指数图: 找局部最高点,这里选7之前的都还行。
  4. DB指数图:找局部最低点,这里选6、7、9、10都行。

为什么选择局部最优点:该点处样本整体聚类效果在不同 k 值下相对最佳,聚类结构更合理。

# 提示用户选择 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 聚类标签
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算法 

DBSCAN(Density - Based Spatial Clustering of Applications with Noise)是一种基于密度的空间聚类算法,无需预先指定聚类个数。


核心步骤:

1.定义参数:确定两个关键参数,邻域半径(eps)和最小点数(minPts)。邻域半径 eps 定义了一个点的邻域范围,最小点数 minPts 规定了在一个点的 eps 邻域内最少需要包含的点数,以确定该点是否为核心点。

2.标记核心点:对于数据集中的每个点,计算在其 eps 邻域内的点数。如果点数大于或等于 minPts,则将该点标记为核心点;否则,标记为非核心点。

3.形成簇:从任意一个核心点出发,将其 eps 邻域内的所有点(包括核心点和非核心点)划分为一个簇。对于这些邻域内的核心点,再继续扩展其邻域内的点,不断将相邻的核心点及其邻域点合并到同一个簇中,直到没有新的点可以加入该簇。

4.标记噪声点:所有不属于任何簇的点被标记为噪声点。

DBSCAN 无需像 K - Means 那样专门确定聚类个数,算法会根据数据的密度分布自动发现不同的簇。每个簇都是基于核心点及其邻域关系自然形成的,聚类个数由数据本身的分布决定。

本质:DBSCAN 通过基于密度的连接性来识别数据集中的簇,将密度相连的数据点归为同一簇,并将低密度区域中的点识别为噪声点,从而实现对数据分布的聚类分析。


优点:

无需指定簇数量:能够根据数据的固有结构自动发现合适的簇数,这在很多实际应用场景中更具灵活性,因为事先往往很难准确预估数据应有的类别数量。

能识别噪声点:可以有效识别数据集中的噪声点,将那些处于低密度区域的数据点标记为噪声,而不是强行将它们划分到某个簇中,这使得聚类结果更能反映数据的真实分布情况,对于包含噪声的数据具有更好的鲁棒性。

适应任意形状簇:对任意形状的簇都能较好地识别,不像 K - Means 假设簇是球形或凸形的。DBSCAN 基于密度连接来定义簇,因此可以处理诸如环形、不规则形状等复杂形状的簇,适用于更广泛的数据分布类型。

缺点:

参数选择敏感:对邻域半径 eps 和最小点数 minPts 这两个参数非常敏感。不同的参数设置可能会导致截然不同的聚类结果。

计算密度复杂:在高维数据集中,计算点的密度(即 eps 邻域内的点数)变得更加复杂和耗时。

不适用于密度变化大的数据:如果数据集中不同簇的密度差异较大,DBSCAN 可能无法很好地处理。因为单一的 eps 和 minPts 参数难以同时适应不同密度区域的簇划分,可能会在低密度区域过度合并,而在高密度区域过度分割。


代码实例:

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_range = np.arange(0.3, 0.8, 0.1)  # 测试不同邻域半径
min_samples_range = range(3, 8)  # 测试不同最小样本数
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)
results_df

# 绘制评估指标图
plt.figure(figsize=(15, 10))
# 轮廓系数图
plt.subplot(2, 2, 1)
for min_samples in min_samples_range:
    # 固定min_samples参数,才可以专注研究eps参数变化时各项评估指标的变化趋势
    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 聚类结果中,标签为 -1 的样本数量高达 7452,而其他簇(标签 0 - 5 )样本数极少 。标签 -1 在 DBSCAN 中代表噪声点,说明大部分样本被判定为噪声,少数簇样本量少,这表明聚类效果不佳,可能存在问题:

参数不合适eps(邻域半径)和 min_samples(最小样本数)设置不当。eps 过小,会使样本难以形成簇,大量样本被划分为噪声;min_samples 过大,也会导致簇难以生成。

数据特性:数据分布可能不适合 DBSCAN 算法。DBSCAN 适用于样本分布有明显密度差异的情况,若数据分布较为均匀或不存在明显的密度相连区域,该算法就难以有效聚类。


层次聚类算法

层次聚类算法是一种基于簇间相似度构建树形聚类结构的算法,不需要预先指定聚类个数,但在实际应用中,常根据具体需求从生成的聚类结构中确定聚类数。


核心步骤:

主要分为凝聚式(自底向上)和分裂式(自顶向下)两种,这里以常用的凝聚式层次聚类为例:

初始化:将每个数据点视为一个单独的簇。此时,簇的数量等于数据点的数量。

计算簇间距离:计算每两个簇之间的距离。常用的距离度量方法有单链接(两个簇中距离最近的两个点之间的距离)、完全链接(两个簇中距离最远的两个点之间的距离)、平均链接(两个簇中所有点对之间距离的平均值)等。

合并簇:找出距离最近的两个簇,并将它们合并为一个新簇。

重复步骤 2 和 3:不断重复计算簇间距离和合并簇的过程,直到所有的数据点都在一个簇中,或者达到某个终止条件(例如簇的数量达到预期值)。这样就生成了一个树形结构,称为树状图(dendrogram)。

在实际应用中,从生成的树状图确定聚类个数并没有一个固定的标准方法,常见思路有:

基于先验知识:根据业务背景或数据特点,预先知道大概的聚类数量,然后在树状图上选择合适的层次进行切割,得到相应数量的簇。

观察树状图:直观地观察树状图,选择在树状图上簇间距离变化较大的位置进行切割,使得切割后得到的簇相对合理。

本质:通过逐步合并(凝聚式)或分裂(分裂式)数据点或簇,构建出一个反映数据点之间亲疏关系的树形结构。


优点:

无需预先指定聚类数:与 K - Means 不同,层次聚类不需要事先确定聚类的数量,而是生成一个包含所有可能聚类情况的树形结构,用户可以根据实际需求在事后灵活选择聚类数。

能够发现数据的层次结构:该算法可以揭示数据内在的层次关系,这对于理解数据的组织结构非常有帮助。例如在生物学中对物种进行分类,层次聚类可以展现出物种之间从低级到高级的分类层次。

对数据分布适应性较好:相对 K - Means 对球形簇的假设,层次聚类可以处理各种形状的数据分布,因为它是基于数据点之间的相似度或距离逐步构建聚类结构,而不是基于质心。

缺点:

计算复杂度高:每一步合并都需要重新计算所有簇之间的距离,随着数据量的增大,计算量会急剧增加,因此不太适合处理大规模数据集。

结果不可逆:一旦两个簇合并(或一个簇分裂),在后续步骤中无法撤销这个操作。如果在某个阶段做出了不恰当的合并(或分裂)决策,可能会导致最终聚类结果不佳。

对噪声敏感:由于是基于距离进行簇的合并或分裂,噪声点可能会对簇间距离的计算产生较大影响,进而干扰聚类结果。例如,噪声点可能会使原本不应合并的簇被合并在一起。

确定聚类数主观性强:从树状图中选择合适的聚类数主要依赖于用户的主观判断,不同的人可能会因为观察角度不同而选择不同的聚类数,缺乏客观统一的标准。


代码实例:

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:
    # 指定聚类数量为当前循环的n_clusters,并使用ward链接准则来合并簇。
    agglo = AgglomerativeClustering(n_clusters=n_clusters, linkage='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 = 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())

# 层次聚类的树状图可视化
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控制显示的层次深度,显示最后3层
# hierarchy.dendrogram(Z, truncate_mode='level')  # 不用p这个参数,可以显示全部的深度
plt.title('Dendrogram for Agglomerative Clustering')
plt.xlabel('Cluster Size')
# 纵轴反映在聚类过程中,不同样本或簇合并时的距离度量值。距离越大,差异越大。
plt.ylabel('Distance')
plt.show()

@浙大疏锦行 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377053.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

时源芯微| KY键盘接口静电浪涌防护方案

KY键盘接口静电浪涌防护方案通过集成ESD保护元件、电阻和连接键&#xff0c;形成了一道有效的防护屏障。当键盘接口受到静电放电或其他浪涌冲击时&#xff0c;该方案能够迅速将过电压和过电流引导至地&#xff0c;从而保护后续电路免受损害。 ESD保护元件是方案中的核心部分&a…

CodeBuddy编程新范式

不会写&#xff1f;不想写&#xff1f; 腾讯推出的CodeBuddy彻底解放双手。 示例 以下是我对CodeBuddy的一个小体验。 我只用一行文字对CodeBuddy说明了一下我的需求&#xff0c;剩下的全部就交给了CodeBuddy&#xff0c;我需要做的就是验收结果即可。 1.首先CodeBuddy会对任…

小刚说C语言刷题—1088求两个数M和N的最大公约数

1.题目描述 求两个正整数 M 和 N 的最大公约数(M&#xff0c;N都在长整型范围内&#xff09; .输入 输入一行&#xff0c;包括两个正整数。 输出 输出只有一行&#xff0c;包括1个正整数。 样例 输入 45 60 输出 15 2.参考代码(C语言版) #include <stdio.h> …

【LLIE专题】基于码本先验与生成式归一化流的低光照图像增强新方法

GLARE: Low Light Image Enhancement via Generative Latent Feature based Codebook Retrieval&#xff08;2024&#xff0c;ECCV&#xff09; 专题介绍一、研究背景二、GLARE方法阶段一&#xff1a;正常光照代码本学习&#xff08;Normal-Light Codebook Learning&#xff09…

[MySQL数据库] SQL优化

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

网络编程epoll和udp

# epoll模型核心要点## 1. epoll核心概念### 1.1 高效IO多路复用- 监视列表与激活列表分离- 内核使用红黑树存储描述符- 边缘触发模式(EPOLLET)支持### 1.2 事件触发机制- **水平触发(LT)**&#xff1a;- 默认模式&#xff0c;类似select/poll- 数据未读完持续触发事件- **边缘…

【iOS】源码阅读(四)——isa与类关联的原理

文章目录 前言OC对象本质探索clang探索对象本质objc_setProperty源码探索 cls与类的关联原理为什么说bits与cls为互斥关系isa的类型isa_t原理探索isa与类的关联 总结 前言 本篇文章主要是笔者在学习和理解类与isa的关联关系时所写的笔记。 OC对象本质探索 在学习和理解类与isa…

uniapp 常用 UI 组件库

1. uView UI 特点&#xff1a; 组件丰富&#xff1a;提供覆盖按钮、表单、图标、表格、导航、图表等场景的内置组件。跨平台支持&#xff1a;兼容 App、H5、小程序等多端。高度可定制&#xff1a;支持主题定制&#xff0c;组件样式灵活。实用工具类&#xff1a;提供时间、数组操…

SCI写作开挂!把Grammarly语法修订嵌入word

详细分享如何把Grammarly嵌入Word&#xff0c;实现英文写作时的实时语法校改。 ①进入Grammarly官网 ②点击右上角的“Get Grammarly Its free”会直接跳转到注册或者登录界面&#xff0c;如果还没有账号先注册。 ③注册或登录后进入这个页面&#xff0c;点击“Support”。 ④…

2025年5月-信息系统项目管理师高级-软考高项-成本计算题

成本计算题挣值分析、成本计算题如何学?1、PV&#xff0c;EV&#xff0c;AC需要理解&#xff0c;根据题目给出的一些个条件需要求得这些值;2、CV&#xff0c;SV&#xff0c;CPI&#xff0c;SPI公式必须记住&#xff0c;需要根据求得的值判断项目的进度和成本的执行情况&#x…

SpringBoot中的Lombok库

一&#xff09;Lombok库简介 Lombok是一个Java库&#xff0c;通过注解的方式简化代码编写&#xff0c;减少样板代码。它能够自动生成getter、setter、构造函数、toString等方法&#xff0c;提升开发效率。Lombok只是一个编译阶段的库&#xff0c;因此不会影响程序的运行。 二…

AI中的MCP是什么?MCP的作用及未来方向预测 (使用go-zero 快速搭建MCP服务器)

AI是当下最热的风。在当今AI技术飞速发展的时代&#xff0c;AI的应用已经渗透到我们日常生活的方方面面。然而&#xff0c;随着AI系统的复杂性不断增加&#xff0c;如何让AI具备更强的自主性和灵活性成为了业界关注的焦点。这就引出了Model Context Protocol&#xff08;MCP&am…

mac安装cast

背景 pycharm本地运行脚本时提示cast没有安装 问题原因 脚本尝试调用cast命令&#xff08;以太坊开发工具foundry中的子命令&#xff09;&#xff0c;但您的系统未安装该工具。 从日志可见&#xff0c;错误发生在通过sysutil.py执行shell命令时。 解决方案 方法1&#xf…

一个简单点的js的h5页面实现地铁快跑的小游戏

以下是一个简化版的"地铁快跑"小游戏H5页面实现。这个游戏包含基本的角色跳跃、障碍物生成和计分系统&#xff0c;使用Canvas绘图技术实现。 完整源码 登录后复制 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-…

Hugging Face 中 LeRobot 使用的入门指南

相关源文件 .github/ISSUE_TEMPLATE/bug-report.yml .github/PULL_REQUEST_TEMPLATE.md README.md examples/1_load_lerobot_dataset.py examples/2_evaluate_pretrained_policy.py examples/3_train_policy.py lerobot/scripts/eval.py lerobot/scripts/train.py 本页面提供 …

零基础入门Hadoop:IntelliJ IDEA远程连接服务器中Hadoop运行WordCount

今天我们来聊一聊大数据&#xff0c;作为一个Hadoop的新手&#xff0c;我也并不敢深入探讨复杂的底层原理。因此&#xff0c;这篇文章的重点更多是从实际操作和入门实践的角度出发&#xff0c;带领大家一起了解大数据应用的基本过程。我们将通过一个经典的案例——WordCounter&…

HTML-3.3 表格布局(学校官网简易布局实例)

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 系列文章目录 HTML-1.1 文本字体样式-字体设置、分割线、段落标签、段内回车以及特殊符号 HTML…

遗传算法求解旅行商问题分析

目录 一、问题分析 二、实现步骤 1&#xff09;初始化种群 2&#xff09;计算适应度 3&#xff09;选择操作 4&#xff09;交叉操作 5&#xff09;变异操作 三、求解结果 四、总结 本文通过一个经典的旅行商问题&#xff0c;详细阐述在实际问题中如何运用遗传算法来进…

打造网络安全堡垒,企业如何应对DDoS、CC、XSS和ARP攻击

网站已经成为企业展示形象、开展业务和实现线上营销的重要平台。然而&#xff0c;随着网络攻击手段的不断升级&#xff0c;DDoS、CC、XSS、ARP等攻击频频出现&#xff0c;严重威胁到企业的信息安全和业务稳定。本文将详细阐述网站被攻击后应采取的应急措施及预防策略&#xff0…

深度解析物理机服务器故障修复时间:影响因素与优化策略

一、物理机故障修复的核心影响因素 物理机作为企业 IT 基础设施的核心载体&#xff0c;其故障修复效率直接关系到业务连续性。故障修复时间&#xff08;MTTR&#xff09;受多重因素交叉影响&#xff1a; 1. 故障类型的复杂性 硬件级故障&#xff1a; 简单故障&#xff1a;内存…