聚类分析基础

sklearn.cluster.KMeans

  • class sklearn.cluster.KMeans (n_clusters=8, init=’k-means++’, n_init=10, max_iter=300, tol=0.0001,precompute_distances=’auto’, verbose=0, random_state=None, copy_x=True, n_jobs=None, algorithm=’auto’)

  • n_clusters是KMeans中的k,表示着要分几类。这是KMeans当中唯一一个必填的参数,默认为8类,但通常聚类结果会是一个小于8的结果。通常,在开始聚类之前,并不知道n_clusters究竟是多少,因此要对它进行探索

  • 当拿到一个数据集,如果可能的话,能够通过绘图先观察一下这个数据集的数据分布,以此来为聚类时输入的n_clusters做一个参考

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
                                # 设置为两个特征
x,y = make_blobs(n_samples=500,n_features=2,centers=4,random_state=1)  
# x是特征矩阵,x.shape=(500,2)  y为标签(500,)

fig,ax1 = plt.subplots(1)   # 生成一个子图 fig画布
ax1.scatter(x[:,0],x[:,1]
           ,marker='o'  # 点的形状
           ,s=8    # 点的大小
           )
plt.show()

在这里插入图片描述

# 给不同分布的点着色
color = ["red","pink","orange","gray"]
fig,ax1 = plt.subplots(1)

for i in range(4):
    ax1.scatter(x[y==i,0],x[y==i,1]   # bool索引
               ,marker='o'
               ,c=color[i]
               )
plt.show()

在这里插入图片描述

一、假定不知道上述数据的分布做聚类

from sklearn.cluster import KMeans
n_clusters = 3

cluster = KMeans(n_clusters=n_clusters,random_state=0).fit(x)

1.1、重要属性

  • .labels_:查看聚好的类别,每个样本所对应的类
    • KMeans因为不需要建立模型或者预测结果,因此只需要fit就能够得到聚类的结果
    • KMeans也有接口predict和fit_predict,表示学习数据x并对x的类别进行预测
    • 但所得到的结果和不调用predict,直接fit后用属性labels一样
  • .cluster_centers_:查看质心
  • .inertia_:查看总距离平方和
y_pred = cluster.labels_
y_pred

在这里插入图片描述

# 假设数据样本非常大,用前面一部分数据来做训练计算出质心,后面的用predict()来预测

cluster_samllsub = KMeans(n_clusters=n_clusters,random_state=0).fit(x[:200])
y_pred_ = cluster_samllsub.predict(x)
y_pred_

在这里插入图片描述

# 查看用少量数据计算质心与用全部数据计算质心的区别

y_pred == y_pred_

在这里插入图片描述

centroid = cluster.cluster_centers_
centroid   # 查看质心

在这里插入图片描述

inertia = cluster.inertia_
inertia   # 查看总距离平方和

在这里插入图片描述

  • inertia:(整体平方和)越小代表分簇的效果越好,但是n_clusters越大,inertia必定会越小
color = ["red","pink","orange","gray"]
fig,ax1 = plt.subplots(1)
# 画簇
for i in range(n_clusters):
    ax1.scatter(x[y_pred==i,0],x[y_pred==i,1]
               ,marker='o'
               ,s=8
               ,c=color[i]
               )
# 画质心
ax1.scatter(centroid[:,0],centroid[:,1]
           ,marker="x"
           ,s=15
           ,c="black"
           )
plt.show()

在这里插入图片描述

# 对比上面的n_clusters=3要好
n_clusters =4 
cluster_ = KMeans(n_clusters=n_clusters,random_state=0).fit(x)
inertia_ = cluster_.inertia_
inertia_

在这里插入图片描述

n_clusters =5 
cluster_ = KMeans(n_clusters=n_clusters,random_state=0).fit(x)
inertia_ = cluster_.inertia_
inertia_

在这里插入图片描述

n_clusters =6
cluster_ = KMeans(n_clusters=n_clusters,random_state=0).fit(x)
inertia_ = cluster_.inertia_
inertia_

在这里插入图片描述

1.2、聚类算法模型评估指标

1.2.1、当真实标签已知时
  • 如果拥有真实标签,更倾向于使用分类算法。
  • 但依然可能使用聚类算法。如果有样本真实聚类情况的数据,可以对于聚类算法的结果和真实结果来衡量聚类的效果。常用的有以下三种方法:
    在这里插入图片描述
1.2.2、当真实标签未知时:轮廓系数
  • 在99%的情况下,是对没有真实标签的数据进行探索,也就是对不知道真正答案的数据进行聚类。

  • 这样的聚类,是完全依赖于评价簇内的稠密程度(簇内差异小)和簇间的离散程度(簇外差异大)来评估聚类的效果。

  • 其中轮廓系数是最常用的聚类算法的评价指标。它是对每个样本来定义的,它能够同时衡量:

    • 1)样本与其自身所在的簇中的其他样本的相似度a,等于样本与同一簇中所有其他点之间的平均距离
    • 2)样本与其他簇中的样本的相似度b,等于样本与下一个最近的簇中的所有点之间的平均距离
  • 根据聚类的要求”簇内差异小,簇外差异大“,我们希望b永远大于a,并且大得越多越好。
    单个样本的轮廓系数计算为:
    在这里插入图片描述

  • 轮廓系数范围是(-1,1),其中值越接近1表示样本与自己所在的簇中的样本很相似,并且与其他簇中的样本不相似,当样本点与簇外的样本更相似的时候,轮廓系数就为负。

  • 当轮廓系数为0时,则代表两个簇中的样本相似度一致,两个簇本应该是一个簇。

  • 可以总结为轮廓系数越接近于1越好,负数则表示聚类效果非常差

  • 如果一个簇中的大多数样本具有比较高的轮廓系数,则簇会有较高的总轮廓系数,则整个数据集的平均轮廓系数越高,则聚类是合适的。

  • 如果许多样本点具有低轮廓系数甚至负值,则聚类是不合适的,聚类的超参数K可能设定得太大或者太小

  • 使用模块metrics中的类silhouette_score来计算轮廓系数,它返回的是一个数据集中,所有样本的轮廓系数的均值。

  • 同在metrics模块中的silhouette_samples,它的参数与轮廓系数一致,但返回的是数据集中每个样本自己的轮廓系数

from sklearn.metrics import silhouette_score
from sklearn.metrics import silhouette_samples

# 分三簇时
silhouette_score(x,y_pred)  # 数据 预测标签

在这里插入图片描述

# 分四簇时
silhouette_score(x,cluster_.labels_)

在这里插入图片描述

# 分5簇时
silhouette_score(x,cluster_.labels_)

在这里插入图片描述

# 每一个样本所对应的轮廓系数
silhouette_samples(x,y_pred)

在这里插入图片描述

1.2.3、当真实标签未知时:Calinski-Harabaz Index (卡林斯基-哈拉巴斯指数:简称CHI,也被称为方差比标准)

在这里插入图片描述

  • Calinski-Harabaz指数越高越好。对于有k个簇的聚类而言,Calinski-Harabaz指数s(k)写作如下公式:
    在这里插入图片描述
  • 其中N为数据集中的样本量,k为簇的个数(即类别的个数), B k B_k Bk是组间离散矩阵,即不同簇之间的协方差矩阵, W k W_k Wk是簇内离散矩阵,即一个簇内数据的协方差矩阵。
  • 数据之间的离散程度越高,协方差矩阵的迹就会越大。组内离散程度低,协方差的迹就会越小
  • 因此Calinski-harabaz指数越高越好
from sklearn.metrics import calinski_harabaz_score
calinski_harabaz_score(x,y_pred)

在这里插入图片描述

  • calinski-Harabaz指数没有界,在凸型的数据上的聚类也会和轮廓系数一样表现虚高。
  • 但是比起轮廓系数,它有一个巨大的优点,就是计算非常快速。
  • 魔法命令%%timeit来计算一个命令的运算时间(至少7次),另一种方法:时间戳计算运行时间。
from time import time

t0 = time()
calinski_harabaz_score(x,y_pred)
print(time() - t0)

t1 = time()
silhouette_score(x,y_pred)
print(time()-t1)

# 时间戳通过datetime中的fromtimestamp转换成真正的时间
import datetime
datetime.datetime.fromtimestamp(t1).strftime("%Y-%M-%D %H:%M:%S")

在这里插入图片描述

二、案例:基于轮廓系数来选择n_clusters

  • 通常会绘制轮廓系数分布图和聚类后的数据分布图来选择最佳的:n_clusters
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples,silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm     # colormap
import numpy as np
import pandas as pd
  • 基于轮廓系数来选择最佳的:n-clusters
  • 要知道每个聚出来的类的轮廓系数是多少,并且要一个各个类之间轮廓系数之间的对比
  • 要知道聚类完之后的图像的分布样子
# 假定要分的簇的数量为4 对上述内容初步探索
n_clusters = 4

# 创建画布,画布为一行两列两个子图
fig,(ax1,ax2) = plt.subplots(1,2)

# 画布尺寸
fig.set_size_inches(18,7)

# 轮廓系数图像画横向条形图,横坐标为轮廓系数值,纵坐标为每个样本
# 轮廓系数取值在[-1,1],但希望轮廓系数至少大于0
# 设定x轴的取值为[-0.1,1]
ax1.set_xlim([-0.1,1])

# 纵坐标从0开始,最大值取到x.shape[0]
# 每个簇能够排列到一起,不同簇之间有一定的空隙
# 图像展示的为不同的条形图聚合成的块,对应一个簇
# 在设定纵坐标的取值范围时,在x.shape[0]上加一个距离,留作间隔使用
ax1.set_ylim([0,x.shape[0] + (n_clusters + 1) * 10])


############   开始建模,调用聚类好的标签   ##############
clusterer = KMeans(n_clusters=n_clusters,random_state=10).fit(x)
cluster_labels = clusterer.labels_

# 调用轮廓系数的分数
# silhouette_score生成的是所有样本点的轮廓系数均值
# 需要输入的参数为:特征矩阵x和聚类完毕后的标签
silhouette_avg = silhouette_score(x,cluster_labels)

# 查看当前簇下整体的轮廓系数
print("For n_clusters = ",n_clusters,
     "The average silhouette_score is:",silhouette_avg)

# 调用silhouette_samples,返回每个样本点的轮廓系数,即横坐标
sample_silhouette_values = silhouette_samples(x,cluster_labels)


# 设定y轴初始值,与x轴有一定距离
y_lower = 10

# 对每一个簇进行循环
for i in range(n_clusters):
    # 从返回的每个样本点的轮廓系数中取出第i簇的轮廓系数,并对其进行排序
    ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]   # bool索引
    
    # .sort()命令会直接改掉原数据的顺序
    ith_cluster_silhouette_values.sort()
    
    # 查看簇中有多少样本
    size_cluster_i = ith_cluster_silhouette_values.shape[0]
    
    # 某一簇在y轴上的取值,应该由初始值(y_lower)开始,到初始值+簇中样本数量(y_upper)
    y_upper = y_lower + size_cluster_i
    
    # colormap库中,使用小数来调用颜色的函数
    # 在nipy_spectral([输入任意小数代表一个颜色])
    # 让每个簇的颜色不同,保证每次生成的小数不同即可
    color = cm.nipy_spectral(float(i)/n_clusters)
    
    ########################     开始填充子图1中的内容   ###########################
    # fill_between : 让一个范围中的柱状图都统一颜色的函数
    # fill_betweenx : x方向填充,填充对象为选定的y
    # fill_betweeny : y方向填充,填充对象为选定的x
    # fill_betweenx(纵坐标的下限,纵坐标的上限,x轴上的取值,柱状图的颜色)
    ax1.fill_betweenx(np.arange(y_lower,y_upper)
                     ,ith_cluster_silhouette_values
                     ,facecolor=color
                     ,alpha=0.7      # 透明度
                     )
    
    # 为每个簇的轮廓系数图加上簇的编号,让编号显示在簇的中间位置
    # text(要显示编号位置的横坐标,要显示编号位置的纵坐标,要显示编号的内容)
    ax1.text(-0.05
            ,y_lower + 0.5 * size_cluster_i
            ,str(i)
            )
    
    # 为下一个簇计算新的y轴上的初值,每一次迭代后,y的上限+10保持簇之间的距离
    y_lower = y_upper + 10

# 给图1加上标题、横坐标轴、纵坐标轴的名字
ax1.set_title("The silhouette plot for the various clusters")
ax1.set_xlabel("The silhouette coefficient values")
ax1.set_ylabel("Cluster label")

# 将整个数据集上的轮廓系数的均值以虚线的形式加入图片
ax1.axvline(x=silhouette_avg,color="red",linestyle="--")

# 让y轴不显示任何刻度
ax1.set_yticks([])

# 让x轴上的刻度显示为规定的列表
ax1.set_xticks([-0.1,0,0.2,0.4,0.6,0.8,1])


#################### 对第二个图进行处理   ##################################
# 获取新的颜色,一次生成多个小数来获取多个颜色
colors = cm.nipy_spectral(cluster_labels.astype(float)/n_clusters)
             # 只接受浮点数       # 标签矩阵取浮点数,这里只返回了四个颜色值
ax2.scatter(x[:,0],x[:,1]
           ,marker='o'
           ,s=8
           ,c=colors
           )

# 将质心放到图上
centers = clusterer.cluster_centers_
ax2.scatter(centers[:,0],centers[:,1]
           ,marker='x'
           ,c='red'
           ,alpha=1
           ,s=200
           )

# 为图2设置标题
ax2.set_title("The visualization of the clustered data")
ax2.set_xlabel("Feature space for the 1st feature")
ax2.set_ylabel("Feature space for the 2nd feature")

# 为整个图设置标题
plt.suptitle(("Silhouette analysis for KMeans clustering on sample data" " "
              "with n_clusters = %d" % n_clusters)
            ,fontsize=14
            ,fontweight='bold'
            )
plt.show()

在这里插入图片描述

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

# 嵌套循环来调整n_clusters 并得到一个较好的n_clusters
for n_clusters in [2,3,4,5,6,7]:
    n_clusters = n_clusters
    
    fig, (ax1, ax2) = plt.subplots(1, 2)
    fig.set_size_inches(18, 7)
    ax1.set_xlim([-0.1, 1])
    ax1.set_ylim([0, x.shape[0] + (n_clusters + 1) * 10])
    
    clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(x)
    cluster_labels = clusterer.labels_
    silhouette_avg = silhouette_score(x, cluster_labels)
    
    print("For n_clusters =", n_clusters,
    "The average silhouette_score is :", silhouette_avg)
    
    sample_silhouette_values = silhouette_samples(x, cluster_labels)
    y_lower = 10
    
    for i in range(n_clusters):
        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
        ith_cluster_silhouette_values.sort()
        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i
        
        color = cm.nipy_spectral(float(i)/n_clusters)
        ax1.fill_betweenx(np.arange(y_lower, y_upper)
                        ,ith_cluster_silhouette_values
                        ,facecolor=color
                        ,alpha=0.7
                        )
        ax1.text(-0.05
                , y_lower + 0.5 * size_cluster_i
                , str(i)
                )
        y_lower = y_upper + 10
        
    ax1.set_title("The silhouette plot for the various clusters.")
    ax1.set_xlabel("The silhouette coefficient values")
    ax1.set_ylabel("Cluster label")
    ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
    ax1.set_yticks([])
    ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
    
    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
    ax2.scatter(x[:, 0], x[:, 1]
                ,marker='o'
                ,s=8
                ,c=colors
                )
    centers = clusterer.cluster_centers_
    # Draw white circles at cluster centers
    ax2.scatter(centers[:, 0], centers[:, 1], marker='x',
                            c="red", alpha=1, s=200)
    
    ax2.set_title("The visualization of the clustered data.")
    ax2.set_xlabel("Feature space for the 1st feature")
    ax2.set_ylabel("Feature space for the 2nd feature")
    plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
                  "with n_clusters = %d" % n_clusters)
                 ,fontsize=14
                 , fontweight='bold'
                )
    plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、重要参数init & random_state & n_init:初始质心怎么放好?

  • 放置初始质心。如果有足够的时间,K-means一定会收敛,但Inertia可能收敛到局部最小值。是否能够收敛到真正的最小值很大程度上取决于质心的初始化。- - init就是用来帮助决定初始化方式的参数。

  • 可以使用random_state参数来控制每次生成的初始质心都在相同位置,甚至可以画学习曲线来确定最优的random_state是哪个整数。

  • 可以使用参数n_init来选择,每个随机数种子下运行的次数。这个参数不常用到,默认10次,如果希望运行的结果更加精确,可以增加这个参数n_init的值来增加每个随机数种子下运行的次数。然而这种方法依然是基于随机性的

  • init:可输入"k-means++“,“random"或者一个n维数组。这是初始化质心的方法,默认"k-means++”。输入"kmeans++”:一种为K均值聚类选择初始聚类中心的聪明的办法,以加速收敛。如果输入了n维数组,数组的形状应该是(n_clusters,n_features)并给出初始质心。

  • random_state:控制每次质心随机初始化的随机数种子

  • n_init:整数,默认10,使用不同的质心随机初始化的种子来运行k-means算法的次数。最终结果会是基于Inertia来计算的n_init次连续运行后的最佳输出

plus = KMeans(n_clusters = 10).fit(x)
plus.n_iter_    # 返回迭代次数

在这里插入图片描述

random = KMeans(n_clusters = 10,init="random",random_state=13).fit(x)
random.n_iter_

在这里插入图片描述

四、重要参数max_iter & tol:让迭代停下来

  • 当质心不再移动,Kmeans算法就会停下来。
  • 在完全收敛之前,可以使用max_iter,最大迭代次数,或者tol,两次迭代间Inertia下降的量,这两个参数来让迭代提前停下来。
  • max_iter:整数,默认300,单次运行的k-means算法的最大迭代次数
  • tol:浮点数,默认1e-4,两次迭代间Inertia下降的量,如果两次迭代之间Inertia下降的值小于tol所设定的值,迭代就会停下
random = KMeans(n_clusters = 10,init="random",max_iter=3,random_state=25).fit(x)
y_pred_max10 = random.labels_
silhouette_score(x,y_pred_max10)

在这里插入图片描述

random = KMeans(n_clusters = 10,init="random",max_iter=10,random_state=20).fit(x)
y_pred_max20 = random.labels_
silhouette_score(x,y_pred_max20)

在这里插入图片描述

五、函数cluster.k_means

  • sklearn.cluster.k_means (X, n_clusters, sample_weight=None, init=’k-means++’, precompute_distances=’auto’,n_init=10, max_iter=300,verbose=False, tol=0.0001, random_state=None, copy_x=True, n_jobs=None,algorithm=’auto’, return_n_iter=False)
  • 函数k_means的用法其实和类非常相似,不过函数是输入一系列值,而直接返回结果。一次性地,函数k_means会依次返回质心,每个样本对应的簇的标签,inertia以及最佳迭代次数
    在这里插入图片描述
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值