7.9 聚类模型评估

聚类评估指标详解

  如果有了类别标签,那么聚类结果也可以像分类那样计算准确率和召回率。但是不应该将分类标签作为聚类结果的评价指标,除非你有相关的先验知识或某种假设,知道这种分类类内差距更小。但是它还是给出了几种评价标准。

7.9.1 调整兰德系数 (Adjusted Rand index)

1. 数学原理

  兰德系数(Rand index)需要给定实际类别信息C,假设K是聚类结果,a表示在C与K中都是同类别的元素对数,b表示在C与K中都是不同类别的元素对数,则兰德指数为:


  对于以上公式,

  • 分子:属性一致的样本数,即同属于这一类或都不属于这一类。a是真实在同一类、预测也在同一类的样本数;b是真实在不同类、预测也在不同类的样本数;
  • 分母:任意两个样本为一类有多少种组合,是数据集中可以组成的总元素对数;
  • RI取值范围为[0,1],值越大意味着聚类结果与真实情况越吻合。

  对于随机结果,RI并不能保证分数接近零。为了实现“在聚类结果随机产生的情况下,指标应该接近零”,调整兰德系数(Adjusted rand index)被提出,它具有更高的区分度:


  ARI取值范围为[-1,1],值越大意味着聚类结果与真实情况越吻合。从广义的角度来讲,ARI衡量的是两个数据分布的吻合程度。

2. 优缺点

优点:

  1. 对任意数量的聚类中心和样本数,随机聚类的ARI都非常接近于0;
  2. 取值在[-1,1]之间,负数代表结果不好,越接近于1越好;
  3. 可用于聚类算法之间的比较。

缺点:

  • ARI需要真实标签

3. python代码实现

labels_true, labels_pred = check_clusterings(labels_true, labels_pred)
n_samples = labels_true.shape[0]
classes = np.unique(labels_true)
clusters = np.unique(labels_pred)
# Special limit cases: no clustering since the data is not split;
# or trivial clustering where each document is assigned a unique cluster.
# These are perfect matches hence return 1.0.
if (classes.shape[0] == clusters.shape[0] == 1 or classes.shape[0] == clusters.shape[0] == 0 or classes.shape[0] == clusters.shape[0] == len(labels_true)):
    return 1.0

contingency = contingency_matrix(labels_true, labels_pred)

# Compute the ARI using the contingency data
sum_comb_c = sum(comb2(n_c) for n_c in contingency.sum(axis=1)) 
sum_comb_k = sum(comb2(n_k) for n_k in contingency.sum(axis=0))
sum_comb = sum(comb2(n_ij) for n_ij in contingency.flatten())
prod_comb = (sum_comb_c * sum_comb_k) / float(comb(n_samples, 2))
mean_comb = (sum_comb_k + sum_comb_c) / 2.
return ((sum_comb - prod_comb) / (mean_comb - prod_comb))

4. metrics类使用方法

  设定已知先验知识的标签为labels_true,利用聚类算法预测的样本标签为label_pred,Adjusted Rand index函数是在计算样本预测值和真实值之间的相似度similarity:同属于这一类或都不属于这一类,而不考虑数据元素顺序和归一化。示例代码:

>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]

>>> metrics.adjusted_rand_score(labels_true, labels_pred)  
0.24...

  我们也可以调整预测集label_pred中元素0和1的位置,以及将数据集中为2的属性改名为3,其结果不受影响,示例代码:

>>> labels_pred = [1, 1, 0, 0, 3, 3]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)  
0.24...

  此外,调整adjusted_rand_score函数中labels_true和labels_pred的位置,对结果没有影响,示例代码:

>>> metrics.adjusted_rand_score(labels_pred, labels_true)  
0.24...

  利用此函数评估模型最好的值为1,示例代码:

>>> labels_pred = labels_true[:]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)
1.0

  评估模型最差的值(与labels_true不相关),其结果为负值或接近0值,示例代码:

>>> labels_true = [0, 1, 2, 0, 3, 4, 5, 1]
>>> labels_pred = [1, 1, 0, 0, 2, 2, 2, 2]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)  
-0.12...

7.9.2 互信息评分(Mutual Information based scores)

1. 数学原理

  互信息(Mutual Information)也是用来衡量两个数据分布的吻合程度。假设U与V是对N个样本标签的分配情况,则两种分布的熵(熵表示的是不确定程度)分别为:




其中:




U与V之间的互信息(MI)定义为:



其中:



标准化后的互信息(Normalized mutual information)为:



  不管标签分配之间的“互信息”的实际数量如何,互信息或者标准化后的值不会因此而调整,而会随着标签(簇)数量的增加而增加。
互信息的期望值可以用如下公式来计算:



其中:




ai和bj分别对应着元素属于Ui和Vj的数量。
  与ARI类似,调整互信息( Adjusted mutual information)定义为:



  利用基于互信息的方法来衡量聚类效果需要实际类别信息,MI与NMI取值范围为[0,1],AMI取值范围为[-1,1],它们都是值越大意味着聚类结果与真实情况越吻合。

2. 优缺点

  • 优点:除取值范围在[0,1]之间,其他同ARI,可用于聚类模型选择;
  • 缺点:需要先验知识。

3. python代码实现

if contingency is None:
        labels_true, labels_pred = check_clusterings(labels_true, labels_pred)
        contingency = contingency_matrix(labels_true, labels_pred)
contingency = np.array(contingency, dtype='float')
contingency_sum = np.sum(contingency)
pi = np.sum(contingency, axis=1)
pj = np.sum(contingency, axis=0)
outer = np.outer(pi, pj)
nnz = contingency != 0.0
# normalized contingency
contingency_nm = contingency[nnz]
log_contingency_nm = np.log(contingency_nm)
contingency_nm /= contingency_sum
# log(a / b) should be calculated as log(a) - log(b) for
# possible loss of precision
log_outer = -np.log(outer[nnz]) + log(pi.sum()) + log(pj.sum())
mi = (contingency_nm * (log_contingency_nm - log(contingency_sum))+ contingency_nm * log_outer)
return mi.sum()

4. metrics类使用方法

  设定已知先验知识的标签为labels_true,利用聚类算法预测的样本标签为label_pred,互信息是衡量两种预测的一致性,忽略排列的顺序。互信息评估有两种方法,标准化的互信息Normalized Mutual Information(NMI) 和调整后的互信息Adjusted Mutual Information(AMI)。示例代码:

>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]

>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)  
0.22504...

  我们也可以调整预测集label_pred中元素0和1的位置,以及将数据集中为2的属性改名为3,其结果不受影响,示例代码:

>>> labels_pred = [1, 1, 0, 0, 3, 3]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)  
0.22504..

  互信息评分中mutual_info_score,adjusted_mutual_info_score和normalized_mutual_info_score函数其参数都是对称的,交换的参数位置不会改变评分值,示例代码:

>>> metrics.adjusted_mutual_info_score(labels_pred, labels_true)  
0.22504...

  利用此函数评估模型最好的值为1,示例代码:

>>> labels_pred = labels_true[:]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)
1.0

>>> metrics.normalized_mutual_info_score(labels_true, labels_pred)
1.0

  评估模型最差的值(与labels_true不相关),其结果为非正值,示例代码:

>>> labels_true = [0, 1, 2, 0, 3, 4, 5, 1]
>>> labels_pred = [1, 1, 0, 0, 2, 2, 2, 2]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)  
-0.10526...

7.9.3 同质性Homogeneity 完整性completeness 调和平均V-measure

1. 数学原理

  • 同质性homogeneity:每个群集只包含单个类的成员;
  • 完整性completeness:给定类的所有成员都分配给同一个群集。

  同质性和完整性分数基于以下公式得出:




  其中H(C|K)是给定给定簇赋值的类的条件熵,由以下公式求得:



  H(C)是类熵,公式为:



  其中,n是样本总数,nc和nk分别属于类c和类k的样本数,而nc,k是从类c划分到类k的样本数量。
  条件熵H(K|C)和类熵H(K),根据以上公式对称求得。
  V-measure是同质性homogeneity和完整性completeness的调和平均数,公式:


2. 优缺点

优点:

  • 分数明确:从0到1反应出最差到最优的表现;
  • 解释直观:差的调和平均数可以在同质性和完整性方面做定性的分析;
  • 对簇结构不作假设:可以比较两种聚类算法如k均值算法和谱聚类算法的结果。

缺点:

  • 以前引入的度量在随机标记方面没有规范化,这意味着,根据样本数,集群和先验知识,完全随机标签并不总是产生相同的完整性和均匀性的值,所得调和平均值V-measure也不相同。特别是,随机标记不会产生零分,特别是当簇的数量很大时。

当样本数大于一千,聚类数小于10时,可以安全地忽略该问题。对于较小的样本量或更大数量的集群,使用经过调整的指数(如调整兰德指数)更为安全。


  • 这些指标要求的先验知识,在实践中几乎不可用或需要手动分配的人作注解者(如在监督学习环境中)。

3. sklearn实现方法

  sklearn.metrics类的homogeneity_score和completeness_score方法分别用来求得同质性和完整性分数,示例代码:

>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]

>>> metrics.homogeneity_score(labels_true, labels_pred)  
0.66...

>>> metrics.completeness_score(labels_true, labels_pred) 
0.42...

  两者的调和平均V-measure,示例代码:

>>> metrics.v_measure_score(labels_true, labels_pred)    
0.51...

  metrics类的homogeneity_completeness_v_measure融合了以上方法,分别能求得相关值,示例代码:

>>> metrics.homogeneity_completeness_v_measure(labels_true, labels_pred)
...                                                      
(0.66..., 0.42..., 0.51...)

  以下预测的评分略好,体现在同质性而非完整性,示例代码:

>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 0, 1, 2, 2]
>>> metrics.homogeneity_completeness_v_measure(labels_true, labels_pred)
...                                                      
(1.0, 0.68..., 0.81...)

7.9.4 Fowlkes-Mallows scores

  Fowlkes-Mallows指数是针对训练集和验证集数据之间求得的查全率和查准率的几何平均值,其公式为:



  示例代码:

>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]
>>>
>>> metrics.fowlkes_mallows_score(labels_true, labels_pred)  
0.47140...

7.9.5 轮廓系数 Silhouette Coefficient

  轮廓系数适用于实际类别信息未知的情况。对于单个样本,设a是与它同类别中其他样本的平均距离,b是与它距离最近不同类别中样本的平均距离,其轮廓系数为:



  对于一个样本集合,它的轮廓系数是所有样本轮廓系数的平均值。
  轮廓系数的取值范围是[-1,1],同类别样本距离越相近不同类别样本距离越远,分数越高。
  示例代码:

>>> from sklearn import metrics
>>> from sklearn.metrics import pairwise_distances
>>> from sklearn import datasets
>>> dataset = datasets.load_iris()
>>> X = dataset.data
>>> y = dataset.target

>>> import numpy as np
>>> from sklearn.cluster import KMeans
>>> kmeans_model = KMeans(n_clusters=3, random_state=1).fit(X)
>>> labels = kmeans_model.labels_
>>> metrics.silhouette_score(X, labels, metric='euclidean')
...                                                      
0.55...
import pandas as pd import numpy as np from sklearn.cluster import KMeans from sklearn.preprocessing import LabelEncoder from sklearn.metrics import adjusted_rand_score, silhouette_score import matplotlib.pyplot as plt from io import StringIO import warnings # 忽略Matplotlib的特定警告 warnings.filterwarnings("ignore", category=UserWarning, module="matplotlib") # --------------------- 1. 数据加载与处理 --------------------- # 鸢尾花数据集 iris_data = """ Sepal.Length,Sepal.Width,Petal.Length,Petal.Width,Species 5.1,3.5,1.4,0.2,setosa 4.9,3.0,1.4,0.2,setosa 4.7,3.2,1.3,0.2,setosa 4.6,3.1,1.5,0.2,setosa 5.0,3.6,1.4,0.2,setosa 5.4,3.9,1.7,0.4,setosa 4.6,3.4,1.4,0.3,setosa 5.0,3.4,1.5,0.2,setosa 4.4,2.9,1.4,0.2,setosa 4.9,3.1,1.5,0.1,setosa 5.4,3.7,1.5,0.2,setosa 4.8,3.4,1.6,0.2,setosa 4.8,3.0,1.4,0.1,setosa 4.3,3.0,1.1,0.1,setosa 5.8,4.0,1.2,0.2,setosa 5.7,4.4,1.5,0.4,setosa 5.4,3.9,1.3,0.4,setosa 5.1,3.5,1.4,0.3,setosa 5.7,3.8,1.7,0.3,setosa 5.1,3.8,1.5,0.3,setosa 5.4,3.4,1.7,0.2,setosa 5.1,3.7,1.5,0.4,setosa 4.6,3.6,1.0,0.2,setosa 5.1,3.3,1.7,0.5,setosa 4.8,3.4,1.9,0.2,setosa 5.0,3.0,1.6,0.2,setosa 5.0,3.4,1.6,0.4,setosa 5.2,3.5,1.5,0.2,setosa 5.2,3.4,1.4,0.2,setosa 4.7,3.2,1.6,0.2,setosa 4.8,3.1,1.6,0.2,setosa 5.4,3.4,1.5,0.4,setosa 5.2,4.1,1.5,0.1,setosa 5.5,4.2,1.4,0.2,setosa 4.9,3.1,1.5,0.2,setosa 5.0,3.2,1.2,0.2,setosa 5.5,3.5,1.3,0.2,setosa 4.9,3.6,1.4,0.1,setosa 4.4,3.0,1.3,0.2,setosa 5.1,3.4,1.5,0.2,setosa 5.0,3.5,1.3,0.3,setosa 4.5,2.3,1.3,0.3,setosa 4.4,3.2,1.3,0.2,setosa 5.0,3.5,1.6,0.6,setosa 5.1,3.8,1.9,0.4,setosa 4.8,3.0,1.4,0.3,setosa 5.1,3.8,1.6,0.2,setosa 4.6,3.2,1.4,0.2,setosa 5.3,3.7,1.5,0.2,setosa 5.0,3.3,1.4,0.2,setosa 7.0,3.2,4.7,1.4,versicolor 6.4,3.2,4.5,1.5,versicolor 6.9,3.1,4.9,1.5,versicolor 5.5,2.3,4.0,1.3,versicolor 6.5,2.8,4.6,1.5,versicolor 5.7,2.8,4.5,1.3,versicolor 6.3,3.3,4.7,1.6,versicolor 4.9,2.4,3.3,1.0,versicolor 6.6,2.9,4.6,1.3,versicolor 5.2,2.7,3.9,1.4,versicolor 5.0,2.0,3.5,1.0,versicolor 5.9,3.0,4.2,1.5,versicolor 6.0,2.2,4.0,1.0,versicolor 6.1,2.9,4.7,1.4,versicolor 5.6,2.9,3.6,1.3,versicolor 6.7,3.1,4.4,1.4,versicolor 5.6,3.0,4.5,1.5,versicolor 5.8,2.7,4.1,1.0,versicolor 6.2,2.2,4.5,1.5,versicolor 5.6,2.5,3.9,1.1,versicolor 5.9,3.2,4.8,1.8,versicolor 6.1,2.8,4.0,1.3,versicolor 6.3,2.5,4.9,1.5,versicolor 6.1,2.8,4.7,1.2,versicolor 6.4,2.9,4.3,1.3,versicolor 6.6,3.0,4.4,1.4,versicolor 6.8,2.8,4.8,1.4,versicolor 6.7,3.0,5.0,1.7,versicolor 6.0,2.9,4.5,1.5,versicolor 5.7,2.6,3.5,1.0,versicolor 5.5,2.4,3.8,1.1,versicolor 5.5,2.4,3.7,1.0,versicolor 5.8,2.7,3.9,1.2,versicolor 6.0,2.7,5.1,1.6,versicolor 5.4,3.0,4.5,1.5,versicolor 6.0,3.4,4.5,1.6,versicolor 6.7,3.1,4.7,1.5,versicolor 6.3,2.3,4.4,1.3,versicolor 5.6,3.0,4.1,1.3,versicolor 5.5,2.5,4.0,1.3,versicolor 5.5,2.6,4.4,1.2,versicolor 6.1,3.0,4.6,1.4,versicolor 5.8,2.6,4.0,1.2,versicolor 5.0,2.3,3.3,1.0,versicolor 5.6,2.7,4.2,1.3,versicolor 5.7,3.0,4.2,1.2,versicolor 5.7,2.9,4.2,1.3,versicolor 6.2,2.9,4.3,1.3,versicolor 5.1,2.5,3.0,1.1,versicolor 5.7,2.8,4.1,1.3,versicolor 6.3,3.3,6.0,2.5,virginica 5.8,2.7,5.1,1.9,virginica 7.1,3.0,5.9,2.1,virginica 6.3,2.9,5.6,1.8,virginica 6.5,3.0,5.8,2.2,virginica 7.6,3.0,6.6,2.1,virginica 4.9,2.5,4.5,1.7,virginica 7.3,2.9,6.3,1.8,virginica 6.7,2.5,5.8,1.8,virginica 7.2,3.6,6.1,2.5,virginica 6.5,3.2,5.1,2.0,virginica 6.4,2.7,5.3,1.9,virginica 6.8,3.0,5.5,2.1,virginica 5.7,2.5,5.0,2.0,virginica 5.8,2.8,5.1,2.4,virginica 6.4,3.2,5.3,2.3,virginica 6.5,3.0,5.5,1.8,virginica 7.7,3.8,6.7,2.2,virginica 7.7,2.6,6.9,2.3,virginica 6.0,2.2,5.0,1.5,virginica 6.9,3.2,5.7,2.3,virginica 5.6,2.8,4.9,2.0,virginica 7.7,2.8,6.7,2.0,virginica 6.3,2.7,4.9,1.8,virginica 6.7,3.3,5.7,2.1,virginica 7.2,3.2,6.0,1.8,virginica 6.2,2.8,4.8,1.8,virginica 6.1,3.0,4.9,1.8,virginica 6.4,2.8,5.6,2.1,virginica 7.2,3.0,5.8,1.6,virginica 7.4,2.8,6.1,1.9,virginica 7.9,3.8,6.4,2.0,virginica 6.4,2.8,5.6,2.2,virginica 6.3,2.8,5.1,1.5,virginica 6.1,2.6,5.6,1.4,virginica 7.7,3.0,6.1,2.3,virginica 6.3,3.4,5.6,2.4,virginica 6.4,3.1,5.5,1.8,virginica 6.0,3.0,4.8,1.8,virginica 6.9,3.1,5.4,2.1,virginica 6.7,3.1,5.6,2.4,virginica 6.9,3.1,5.1,2.3,virginica 5.8,2.7,5.1,1.9,virginica 6.8,3.2,5.9,2.3,virginica 6.7,3.3,5.7,2.5,virginica 6.7,3.0,5.2,2.3,virginica 6.3,2.5,5.0,1.9,virginica 6.5,3.0,5.2,2.0,virginica 6.2,3.4,5.4,2.3,virginica 5.9,3.0,5.1,1.8,virginica """ # 读取数据 df = pd.read_csv(StringIO(iris_data)) print(f"数据集形状: {df.shape}") print("前5行数据:") print(df.head()) # --------------------- 2. 数据预处理 --------------------- # 提取特征 X = df[["Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"]].values # 标签编码 le = LabelEncoder() y_true = le.fit_transform(df["Species"]) species_names = le.classes_ print("\n物种编码映射:") for i, name in enumerate(species_names): print(f"{name} → {i}") # --------------------- 3. K-Means聚类 --------------------- # 创建K-Means模型 kmeans = KMeans( n_clusters=3, # 聚类数量 init='k-means++', # 智能初始化方法 n_init=10, # 多次初始化以避免局部最优 max_iter=300, # 最大迭代次数 random_state=42 # 随机种子 ) # 训练模型 kmeans.fit(X) # 获取结果 y_pred = kmeans.labels_ # 聚类标签 centroids = kmeans.cluster_centers_ # 聚类中心 inertia = kmeans.inertia_ # 样本到最近聚类中心的平方和 print("\n聚类完成!") print(f"样本到最近聚类中心的平方和: {inertia:.2f}") # --------------------- 4. 聚类结果评估 --------------------- # 评估指标 ari = adjusted_rand_score(y_true, y_pred) # 调整兰德指数 silhouette = silhouette_score(X, y_pred) # 轮廓系数 print("\n聚类评估:") print(f"调整兰德指数(ARI): {ari:.4f} (1.0表示完美匹配)") print(f"轮廓系数: {silhouette:.4f} (越高表示聚类效果越好)") # 聚类中心分析 print("\n聚类中心坐标:") for i, center in enumerate(centroids): print(f"Cluster {i}:") print(f" 萼片长度: {center[0]:.2f} cm, 萼片宽度: {center[1]:.2f} cm") print(f" 花瓣长度: {center[2]:.2f} cm, 花瓣宽度: {center[3]:.2f} cm") # 聚类与物种对应关系 cluster_species = {} print("\n聚类物种分布:") for cluster in range(3): species_in_cluster = df.loc[y_pred == cluster, 'Species'].value_counts() dominant_species = species_in_cluster.idxmax() cluster_species[cluster] = dominant_species print(f"Cluster {cluster} 主要包含: {dominant_species} ({species_in_cluster[dominant_species]}个样本)") # --------------------- 5. 可视化 --------------------- plt.figure(figsize=(15, 10)) # 1. 花瓣特征散点图 ax1 = plt.subplot(2, 2, 1) # 为每个子图创建新的轴对象 scatter1 = ax1.scatter( X[:, 2], X[:, 3], c=y_pred, cmap='viridis', s=50, alpha=0.7, edgecolor='k' ) centroids1 = ax1.scatter( centroids[:, 2], centroids[:, 3], marker='X', s=200, c='red', label='聚类中心' ) ax1.set_title('花瓣长度 vs 花瓣宽度') ax1.set_xlabel('花瓣长度 (cm)') ax1.set_ylabel('花瓣宽度 (cm)') ax1.legend() ax1.grid(alpha=0.3) # 2. 萼片特征散点图 ax2 = plt.subplot(2, 2, 2) scatter2 = ax2.scatter( X[:, 0], X[:, 1], c=y_pred, cmap='viridis', s=50, alpha=0.7, edgecolor='k' ) centroids2 = ax2.scatter( centroids[:, 0], centroids[:, 1], marker='X', s=200, c='red' ) ax2.set_title('萼片长度 vs 萼片宽度') ax2.set_xlabel('萼片长度 (cm)') ax2.set_ylabel('萼片宽度 (cm)') ax2.grid(alpha=0.3) # 3. 混合特征散点图 ax3 = plt.subplot(2, 2, 3) scatter3 = ax3.scatter( X[:, 0], X[:, 2], c=y_pred, cmap='viridis', s=50, alpha=0.7, edgecolor='k' ) centroids3 = ax3.scatter( centroids[:, 0], centroids[:, 2], marker='X', s=200, c='red' ) ax3.set_title('萼片长度 vs 花瓣长度') ax3.set_xlabel('萼片长度 (cm)') ax3.set_ylabel('花瓣长度 (cm)') ax3.grid(alpha=0.3) # 4. 聚类物种分布饼图 ax4 = plt.subplot(2, 2, 4) # 准备数据:每个聚类中每个物种的数量 cluster_counts = [] for cluster in range(3): species_in_cluster = df.loc[y_pred == cluster, 'Species'].value_counts() # 保证顺序与species_names一致 counts = [species_in_cluster.get(s, 0) for s in species_names] cluster_counts.append(counts) # 转换为数组,形状为(3,3) cluster_counts = np.array(cluster_counts) # 设置条形图位置 bar_width = 0.6 x = np.arange(3) # 绘制堆叠条形图 bottom = np.zeros(3) colors = ['#ff9999','#66b3ff','#99ff99'] for i, species in enumerate(species_names): ax4.bar( x, cluster_counts[:, i], bottom=bottom, width=bar_width, label=species, color=colors[i], alpha=0.7 ) bottom += cluster_counts[:, i] ax4.set_title('每个聚类中的物种分布') ax4.set_xlabel('聚类') ax4.set_ylabel('样本数量') ax4.set_xticks(x) ax4.set_xticklabels(['Cluster 0', 'Cluster 1', 'Cluster 2']) ax4.legend(loc='upper right') ax4.grid(axis='y', alpha=0.3) plt.tight_layout() plt.savefig('iris_clustering_results.png', dpi=300, bbox_inches='tight') plt.show() # --------------------- 6. 肘部法确定最佳K值 --------------------- plt.figure(figsize=(10, 6)) inertias = [] k_range = range(1, 10) for k in k_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) kmeans.fit(X) inertias.append(kmeans.inertia_) plt.plot(k_range, inertias, 'bo-') plt.xlabel('聚类数量 (K)') plt.ylabel('样本到最近中心的平方和') plt.title('肘部法确定最佳K值') plt.xticks(k_range) plt.grid(True) plt.savefig('elbow_method.png', dpi=300, bbox_inches='tight') plt.show() # --------------------- 7. 聚类边界可视化 --------------------- # 选择两个特征进行可视化 plt.figure(figsize=(12, 8)) # 选择花瓣长度和宽度进行可视化 x_min, x_max = X[:, 2].min() - 0.5, X[:, 2].max() + 0.5 y_min, y_max = X[:, 3].min() - 0.5, X[:, 3].max() + 0.5 # 创建网格点 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02)) # 使用花瓣特征训练新模型 X_subset = X[:, 2:4] kmeans_subset = KMeans(n_clusters=3, random_state=42, n_init=10) kmeans_subset.fit(X_subset) # 预测网格点 Z = kmeans_subset.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) # 绘制决策边界 plt.contourf(xx, yy, Z, alpha=0.4, cmap='viridis') plt.scatter( X_subset[:, 0], X_subset[:, 1], c=y_pred, cmap='viridis', s=50, alpha=0.8, edgecolor='k' ) plt.scatter( centroids[:, 2], centroids[:, 3], marker='X', s=200, c='red', label='聚类中心' ) plt.title('花瓣特征的聚类边界') plt.xlabel('花瓣长度 (cm)') plt.ylabel('花瓣宽度 (cm)') plt.legend() plt.grid(alpha=0.3) plt.savefig('cluster_boundaries.png', dpi=300, bbox_inches='tight') plt.show()表明横纵坐标
最新发布
11-26
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值