Python实战:用sklearn计算轮廓系数评估你的聚类效果(附完整代码)

当你在处理客户分群、新闻分类或者图像分割任务时,总会遇到一个灵魂拷问:这次聚类结果到底靠不靠谱?别急着看那些花里胡哨的可视化图表,今天教你用轮廓系数这个"聚类的体检报告"来量化评估效果。这个指标就像个严格的教练,能告诉你每个数据点是否"站对了队伍"。

1. 轮廓系数:聚类的"体检医生"

想象你参加了一场相亲大会(数据点),主持人(聚类算法)把所有人分成了几组。轮廓系数就是那个躲在角落的观察员,它会记录两个关键数据:

  • 你与同组其他人的亲密程度(内聚力)
  • 你与其他组最受欢迎人士的距离(分离度)

计算公式

s(i) = (b(i) - a(i)) / max(a(i), b(i))

其中:

  • a(i):样本i到同簇其他样本的平均距离(内聚度)
  • b(i):样本i到最近其他簇所有样本的平均距离(分离度)

这个值域在[-1, 1]之间:

  • 接近1:完美分组
  • 接近0:处在边界线
  • 接近-1:可能分错组了

注意:当数据量较大时,建议使用silhouette_samples逐个查看样本得分,而不仅仅是整体平均分

2. 实战准备:从数据到聚类

我们先准备一个电商用户数据集,包含年消费金额和访问频率:

import pandas as pd
from sklearn.datasets import make_blobs

# 生成模拟数据
X, _ = make_blobs(n_samples=500, 
                 centers=3, 
                 cluster_std=0.8, 
                 random_state=42)

user_data = pd.DataFrame(X, columns=['Annual_Spend', 'Visit_Frequency'])

数据预处理要点

  1. 必须做标准化(不同量纲的特征会扭曲距离计算)
  2. 分类变量需要特殊处理
  3. 缺失值建议用中位数填充

标准化示例:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaled_data = scaler.fit_transform(user_data)

3. 聚类算法选择与实现

不同算法对轮廓系数的影响就像不同裁判的评分标准:

算法类型 适合场景 轮廓系数特点
K-Means 球形簇、均匀大小 对K值敏感
DBSCAN 不规则形状、噪声数据 可能部分样本无得分
层次聚类 嵌套结构 计算复杂度较高

K-Means实现代码

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

kmeans = KMeans(n_clusters=3, random_state=42)
clusters = kmeans.fit_predict(scaled_data)

# 计算轮廓系数
score = silhouette_score(scaled_data, clusters)
print(f"整体轮廓系数:{score:.3f}")

常见踩坑点

  • 随机种子影响结果(建议设置random_state)
  • 初始质心选择不佳导致局部最优(可使用k-means++初始化)
  • 迭代次数不足(max_iter建议300以上)

4. 深度解析轮廓系数结果

拿到0.6的分数先别高兴,让我们拆解看看:

样本级分析

from sklearn.metrics import silhouette_samples

sample_scores = silhouette_samples(scaled_data, clusters)

用这个数据可以画出经典的轮廓分析图:

import matplotlib.pyplot as plt
import numpy as np

plt.figure(figsize=(10, 6))
y_lower = 10

for i in range(3):
    ith_cluster_silhouette_values = sample_scores[clusters == i]
    ith_cluster_silhouette_values.sort()
    
    size_cluster_i = ith_cluster_silhouette_values.shape[0]
    y_upper = y_lower + size_cluster_i
    
    color = plt.cm.nipy_spectral(float(i) / 3)
    plt.fill_betweenx(np.arange(y_lower, y_upper),
                      0, ith_cluster_silhouette_values,
                      facecolor=color, edgecolor=color, alpha=0.7)
    
    y_lower = y_upper + 10

plt.axvline(x=score, color="red", linestyle="--")
plt.title("轮廓分析图")
plt.xlabel("轮廓系数值")
plt.ylabel("簇标签")

解读技巧

  1. 所有簇的宽度应该相近(平衡的簇大小)
  2. 轮廓应该高于平均线(红色虚线)
  3. 没有太多负值的样本

5. 进阶技巧:寻找最佳K值

轮廓系数最实用的场景就是确定最优簇数,试试这个自动化方法:

range_n_clusters = range(2, 8)
best_k = 0
best_score = -1

for n_clusters in range_n_clusters:
    clusterer = KMeans(n_clusters=n_clusters, random_state=42)
    preds = clusterer.fit_predict(scaled_data)
    
    score = silhouette_score(scaled_data, preds)
    print(f"对于K={n_clusters},轮廓系数为{score:.3f}")
    
    if score > best_score:
        best_score = score
        best_k = n_clusters

print(f"\n最佳K值为:{best_k},对应轮廓系数{best_score:.3f}")

注意事项

  • K值越大轮廓系数可能虚高(过拟合风险)
  • 可以结合肘部法综合判断
  • 对于密度不均的数据可能失效

6. 真实案例:用户分群评估

最近用轮廓系数优化了一个零售客户分群项目,原始分为5个群组(市场部定的),但分析发现:

  • 两个群组轮廓系数仅为0.12
  • 30%样本得分低于0
  • 合并后整体系数提升到0.51

调整后的分群使营销CTR提升了22%,关键发现是:

  • 高净值客户和小B客户被错误混组
  • 低频高客单群体需要独立划分
  • 某些特征需要重新加权计算
# 业务应用示例:筛选问题样本
problem_samples = user_data[sample_scores < 0]
print(f"需人工复核的样本数:{len(problem_samples)}")

7. 与其他指标的配合使用

轮廓系数不是万能的,聪明的数据科学家会组合使用:

指标对比表

指标名称 优势 局限性 适用场景
轮廓系数 兼顾内聚与分离 计算复杂度O(n^2) 中小规模数据
Calinski-Harabasz 计算速度快 倾向均衡大小的簇 初步筛选
Davies-Bouldin 无需真实标签 对密度变化敏感 密度不均的数据
轮廓系数 可分析单个样本 对异常值敏感 需要细粒度分析时

组合评估代码示例:

from sklearn.metrics import calinski_harabasz_score, davies_bouldin_score

def evaluate_clusters(X, labels):
    return {
        "轮廓系数": silhouette_score(X, labels),
        "CH指数": calinski_harabasz_score(X, labels),
        "DB指数": davies_bouldin_score(X, labels)
    }

print(evaluate_clusters(scaled_data, clusters))

8. 性能优化与大数据处理

当数据量超过1万条时,原始方法会变慢,试试这些技巧:

加速策略

  1. 使用近似算法:
from sklearn.metrics import pairwise_distances
# 预先计算距离矩阵
dists = pairwise_distances(X, metric='euclidean', n_jobs=-1)
  1. 采样评估(保持分布不变):
from sklearn.utils import resample
sample_data = resample(scaled_data, n_samples=1000, stratify=clusters)
  1. 使用更快的距离度量:
score = silhouette_score(scaled_data, clusters, metric='cosine')

GPU加速方案

# 使用RAPIDS库(需NVIDIA GPU)
import cuml
kmeans = cuml.KMeans(n_clusters=3)
clusters = kmeans.fit_predict(scaled_data)

9. 常见问题排雷

Q1:为什么我的轮廓系数是负值?

  • 可能原因:K值过大、数据不适合聚类、特征工程有问题

Q2:如何解释0.5的分数?

  • 参考标准:
    • 0.7+:强聚类结构
    • 0.5-0.7:合理结构
    • 0.25-0.5:弱结构
    • <0.25:无意义聚类

Q3:分类变量怎么处理?

  • 推荐使用k-prototypes算法或Gower距离:
from gower import gower_matrix
dists = gower_matrix(df_with_mixed_types)

10. 完整项目示例

最后分享一个电商用户分析的完整流程:

# 1. 数据准备
df = pd.read_csv('user_behavior.csv')
features = ['purchase_amount', 'visit_count', 'avg_session']

# 2. 预处理
X = df[features].fillna(df[features].median())
X = StandardScaler().fit_transform(X)

# 3. 确定最佳K值
scores = []
for k in range(2, 6):
    kmeans = KMeans(n_clusters=k, random_state=42).fit(X)
    scores.append(silhouette_score(X, kmeans.labels_))

best_k = np.argmax(scores) + 2  # +2因为range从2开始

# 4. 最终聚类
final_model = KMeans(n_clusters=best_k, random_state=42)
df['cluster'] = final_model.fit_predict(X)

# 5. 分析结果
cluster_profile = df.groupby('cluster')[features].mean()
problem_users = df[silhouette_samples(X, df['cluster']) < 0]
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐