【机器学习聚类算法实战-2】| 基于sklearn.cluster工具包的Mean-Shift均值偏移聚类算法以及BIRCH聚类算法分别对商品批发商的市场细分实例分析

🎩 欢迎来到技术探索的奇幻世界👨‍💻

📜 个人主页@一伦明悦-CSDN博客

✍🏻 作者简介: C++软件开发、Python机器学习爱好者

🗣️ 互动与支持:💬评论      👍🏻点赞      📂收藏     👀关注+

如果文章有所帮助,欢迎留下您宝贵的评论,点赞加收藏支持我,点击关注,一起进步!


目录

前言       

正文

01-核密度估计       

02-Mean-Shift聚类算法     

(1)核密度估计在Mean-Shift聚类中的意义

(2)Mean-Shift 聚类过程

03-BIRCH聚类算法     

(1)BIRCH算法中的聚类特征树

04-基于两种算法对商品批发商的市场细分    

总结       

参考文献       


前言       

        本篇讲解的两种聚类算法是机器学习中的特色聚类方法,包括基于密度的Mean-Shift均值偏移聚类算法以及适合超大数据集的在线动态聚类算法BIRCH

        基于密度的聚类算法是一种无需预先指定聚类数量的方法,它通过识别数据中高密度区域来划分聚类。其基本原理是通过定义密度的概念,将数据点分为核心点、边界点和噪声点。核心点是在给定半径范围内具有足够多的邻居的数据点,边界点是在给定半径范围内靠近核心点但邻居数量不足的数据点,而噪声点则是既不是核心点也不是边界点的数据点。基于密度的聚类算法通常以一个核心点开始,然后通过迭代地找到其他密度可达的点来扩展聚类,直到所有密度可达的点都被访问为止。这种方法可以处理任意形状的聚类,并且对噪声数据相对鲁棒,因为它们通常被归类为噪声点而不是错误地被分到某个聚类中。

        适合超大数据集的在线动态聚类算法通常是基于密度的,其原理是在不断到来的数据流中,通过识别高密度区域来动态地划分聚类。这类算法与传统的批处理聚类不同,它能够实时地处理数据流,并且通常不需要存储整个数据集。基于密度的在线动态聚类算法会随着数据的到来而不断更新聚类结构,通常使用一些启发式方法来保持聚类的稳定性和准确性。这样的算法通常会对数据点的密度进行估计,并根据密度的变化来更新聚类结构,以适应数据流的动态变化。一个常见的基于密度的在线动态聚类算法是基于密度的增量聚类算法,它通过不断地添加新数据点,并根据密度的变化来调整聚类结构,从而实现动态聚类的目的。这种算法适用于处理实时产生的数据流,并且能够在保持高效性的同时保持较好的聚类质量。

正文

01-核密度估计       

        核密度估计(Kernel Density Estimation,KDE)是一种非参数的统计方法,用于估计随机变量的概率密度函数。其原理是对每个数据点放置一个核函数,然后通过对所有核函数的贡献进行叠加来估计密度函数。这样做的效果是在每个数据点周围形成一个小的概率贡献,所有这些贡献叠加起来就形成了整个数据空间的密度估计。

        核密度估计的主要思想是以每个数据点为中心,在其周围生成一个核函数,通常是正态分布的形状。这个核函数在数据点附近有一个峰值,然后随着距离的增加逐渐减小。通过对所有数据点生成的核函数进行叠加,就可以得到整个数据集的密度估计。

        核密度估计的平滑性和形状受到核函数的选择和带宽参数的影响。常见的核函数包括高斯核、Epanechnikov核和矩形核等,而带宽参数控制了核函数的宽度,从而影响了估计密度函数的平滑程度和精确度。

        核密度估计的基本设计思想与绘制关于单个变量X的概率分布图有内在联系。下方由代码生成的图像为X的带核密度估计曲线的直方图,它很好地展示了X的分布特征。图中的多个蓝色矩形条,俗称为“直方桶”记为,构成了变量X的直方图。其中每个的宽度相等(均等于h),中心点为。横坐标为变量X的取值,纵坐标为概率密度值。第k个“直方桶”的中心点的概率密度定义为,其中为落入的样本量(即频数),N为总样本量, 为频率。当的宽度h都相等时,各“直方桶”的概率密度之比等于频率之比,这也正是直方图的纵坐标通常为频率的原因。
        首先,定义一个非负的距离函数:

        其中,记为,表示样本观测点C ^ { S _ { k } }的距离。上式中,若样本观测X _ { i }落入S _ { k }中(即在C ^ { S _ { k } }的附近),则距离函数等于1。反之,若X _ { i }远离C ^ { S _ { k } },则距离函数等于0。这里,距离函数称为核函数,其中h称为核宽。然后,基于对样本量为N的数据集,计算落入S _ { k }的样本频率: 。最后,计算C ^ { S _ { k } }处的概率密度:

        这就是点^{}C ^ { S _ { k } }处的核密度估计值。将所有C ^ { S _ { k } } ( k = 1 , 2 , \cdots , K )的核密度估计值连线并平滑,便可得到如图中虚线所示的核密度曲线。
        式中的核函数等同于式的定义,称为均匀核函数。特点是:无论X _ { i }C ^ { S _ { k } }的距离具体值是大或是小,只要满足d _ { X _ { i } } = \vert \vert C ^ { S _ { k } } - X _ { i } \vert \vert \leq \frac { h } { 2 },核函数均等于1,即以核函数值等于1的“权重”落入S _ { k }中。同样,只要满足d _ { X _ { i } } = \vert \vert C ^ { S _ { k } } - X _ { i } \vert \vert > \frac { h } { 2 },核函数均等于0,即以核函数值等于0的“权重”落入S _ { k }中。可见,均匀核函数忽略了d _ { X _ { i } }值大小的信息。
        为充分利用d _ { X _ { i } }的计算结果,更细致地描述样本观测点以怎样的“权重”落入S _ { k }中,通常可选择径向基核函数:

                                 K ( \vert \vert C ^ { S _ { k } } - X _ { i } \vert \vert ) = \frac { 1 } { \sqrt { 2 \pi } h } e ^ { - \frac { \vert \vert c ^ { 5 k } - X _ { i } \vert \vert ^ { 2 } } { 2 h ^ { 2 } } }                 
        其中,h为核宽。径向基核函数是关于d _ { X _ { i } } ^ { 2 }的非线性函数。距离d _ { X _ { i } } ^ { 2 }值越小,核函数K ( \vert \vert C ^ { S _ { k } } - X _ { i } \vert \vert )值越大。反之,距离d _ { X _ { i } } ^ { 2 }值越大,核函数K( \vert \vert C ^ { S _ { k } } - X _ { i } \vert \vert )值越小。

        此时,点C ^ { t S _ { k } }的核密度估计可表述为
                                 \hat { f } _ { h , K } ( C ^ { S _ { k } } ) = \frac { c } { N h } \sum _ { i = 1 } ^ { N } K \parallel \frac { C ^ { S _ { k } } - X _ { i } } { h } \parallel                  
        总之,核密度曲线完全是“多点平滑”的结果,仅基于数据本身,并不对数据的理论分布做任何假定。此外,均匀核函数中的核宽h会影响核密度曲线的光滑程度。h越小曲线越平滑,反之,h越大曲线越不平滑。径向基核函数中的核宽h同样也会影响核函数值。h较大时,即分布的标准差较大,其分布曲线呈“平峰”分布。反之,h较小时的分布曲线呈“尖峰”分布。在给定d _ { X _ { i } } ^ { 2 }的条件下,减少Δd2时,前者的核函数值变化小于后者。可采用不同的方法确定核宽h。
        可以有很多将单变量的核密度估计推广到多变量下的方法。这里仅给出Mean-Shift聚类中,聚类变量空间S _ { k } \subsetR”的中心点C的核密度估计:
                                 \hat { f } _ { h , K } ( C ^ { S _ { k } } ) = \frac { c _ { k } } { N h ^ { p } } \sum _ { i = 1 } ^ { N } K \vert \vert \frac { C ^ { S _ { k } } - X _ { i } } { h } \vert \vert ^ { 2 }   

        下面是直方图生成的代码:

        这段代码首先设置了随机种子,以确保生成的随机数据可重现性。然后使用 norm.rvs() 函数生成两组服从正态分布的随机数据 X1 和 X2,分别代表两个不同的正态分布。接着将这两组数据合并为一组数据 X。然后创建一个图形窗口,并设置其大小为 (9, 6)。使用 plt.hist() 函数绘制数据 X 的直方图,其中 density=True 表示绘制的是概率密度直方图,alpha=0.45 设置透明度,color='blue' 设置颜色为蓝色。

        接着,使用 gaussian_kde() 函数对数据 X 进行高斯核密度估计,得到密度估计函数 probDensityFun。然后使用 plt.plot() 函数绘制核密度估计曲线,通过 np.sort(X) 对数据进行排序以确保曲线的平滑度。曲线的颜色设置为红色,线型设置为虚线,标签设置为“核密度估计曲线”。

        接下来,添加标题和轴标签,并显示图例和网格线。最后,将图像保存到内存中,并从内存中读取图像并保存为PNG格式文件。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

np.random.seed(123)
X1 = norm.rvs(loc=-1.0, scale=1, size=500) 
X2 = norm.rvs(loc=2.0, scale=0.6, size=500)
X = np.hstack([X1, X2])
fig=plt.figure(figsize=(9,6))
plt.hist(X, density=True, alpha=0.45, color='blue')
probDensityFun = gaussian_kde(X)
plt.title("带核密度估计曲线的直方图", fontsize=15)
plt.plot(np.sort(X), probDensityFun(np.sort(X)),c='r',linestyle='--',label="核密度估计曲线")
plt.xlabel("X")
plt.ylabel("密度值")
plt.legend()
plt.grid(True,linestyle='-.')

# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=500)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')

plt.show()

         直方图如下图:

02-Mean-Shift聚类算法     

        Mean-Shift聚类是一种基于密度的聚类算法,其原理是通过在数据点密度较高的区域寻找密度梯度的方向,并将数据点沿着梯度方向移动,直到收敛到局部密度最大值的位置。具体而言,算法首先在数据集中随机选择一个点作为起始点,然后计算该点周围数据点的密度中心(即以该点为中心,一定半径内的数据点的平均位置),并将当前点移动到密度中心。重复这个过程,直到所有点都收敛到局部密度的极值点,这些极值点就是聚类的中心。与K-Means等聚类算法不同,Mean-Shift聚类无需事先指定聚类数量,而是根据数据点的密度分布自适应地确定聚类中心。

        Mean-Shift 聚类是 Dorin Comaniciu 等学者在2002年提出的一种基于密度的聚类算法。它与DBSCAN 聚类的相同点是:对小类的定义相同,即一个小类对应着样本观测点分布的一个高密度区域。不同点在于:Mean-Shift 聚类基于统计分布定义密度,即:若p维聚类变量空间中的区域S _ { k } ( k = 1 , 2 , \cdots , K ) \subset R ^ { p },对应一个小类(为一个高密度区域),那么意味着S _ { k }区域中的样本观测X _ { i } \in S _ { k } ( i = 1 , 2 , \cdots , N _ { k } ),在以区域中心点C ^ { S _ { k } } = ( C _ { 1 } ^ { S _ { k } } , C _ { 2 } ^ { S _ { k } } , \cdots , C _ { p } ^ { S _ { k } } )为均值向量、指定广义方差的多元径向基核密度估计中,均有较大的核密度估计值。此外,Mean-Shift 聚类的另一个重要特点是不需要事先指定聚类数目K。

(1)核密度估计在Mean-Shift聚类中的意义

        利用核密度估计可以方便地找到聚类变量空间中的高密度区域。式中的\hat { f } _ { h , K } ( C ^ { S _ { k } } )越大,表明以C ^ { S _ { k } }为中心的S _ { k }空间内聚集了较多的样本观测点。显然,C ^ { S _ { k } }在式\hat { f } _ { h , K } ( C ^ { S _ { k } } )的导数等于0,即\nabla \hat { f } _ { h , K } ( C ^ { S _ { k } } ) = 0处取最大值。
        核密度估计在Mean-Shift 聚类中的意义在于:Mean-Shift聚类通过\nabla \hat { f } _ { h , K } ( C ^ { S _ { k } } )= 0找到高密度区域的中心点C ^ { S _ { k } },即小类S _ { k }的中心点C ^ { S _ { k } }
        在Mean-Shift 聚类的初始阶段,任意样本观测点X,都可作为初始的C ^ { S _ { k } } ( t = 0 )。为快速找到合理的C ^ { S _ { k } },计算核密度的梯估计(Density Gradient Estimation)。对式求导:
                                \hat { Y } f _ { h , K } ( C ^ { S } _ { k } ) = \frac { 2 c _ { k } \cdot } { N h ^ { p + 2 } } \sum _ { i = 1 } ^ { N } ( C ^ { S _ { k } } - X _ { i } ) K ^ { \prime } \vert \vert \frac { C ^ { S _ { k } } - X _ { i } } { h }      
其中, C ^ { S _ { k } }为当前小类中心C ^ { S _ { k } } ( t )。为便于后续讨论,设:
                                                 g ( X ) = - K ^ { \prime } ( X )                        
并定义核函数: G ( X ) = c _ { g } g ( \parallel X \parallel ^ { 2 } )


\hat { f } _ { h , G } ( C ^ { S _ { k } } ) = \frac { c _ { g } } { N h ^ { p } } [ \sum _ { i = 1 } ^ { N } g ( \vert \frac { C ^ { S _ { k } } - X _ { j } } { h } \vert ) ^ { 2 } ]
 

        称为均值偏移(Mean-Shift),Mean-Shift 聚类也因此得名。m _ { h , G } ( C ^ { S _ { k } } )是一个p维向量也称为均值偏移向量,给出了每个维上的偏移。从这个角度看, \sum _ { i = 1 } ^ { N } X _ { i } g \vert \vert \frac { e ^ { \xi _ { k } } - X _ { i } } { h } \vert \vert ^ { 2 } } { \sum _ { i = 1 } ^ { N } g ( \vert \frac { e ^ { \xi _ { k } } - X _ { i } } { h } \vert ^ { 2 } ) } 就是p维聚类空间中
的一个点,将成为t+1时刻的小类中心点C ^ { S _ { k } } ( t + 1 )
t+1时刻的小类中心点C ^ { S _ { k } } ( t + 1)的位置坐标由N个样本观测X _ { i } ( i = 1 , 2 , \cdots , N )的加权平均值决定。

        事实上,可将\frac { g ( \vert \vert \frac { c _ { k } ^ { \xi _ { k } } - X _ { i } } { h } \vert \vert ^ { 2 } ) } { \sum _ { i = 1 } ^ { N } g ( \vert \frac { c ^ { s _ { k } } - X _ { i } \vert ^ { 2 } ) } { h }视为样本观测点X 的权重。距当前小类中心C ^ { S _ { k } } ( t )越近的样本观测点,核密度值g ( \vert \vert \frac { C ^ { S _ { k } } - X _ { i } } { h } \vert \vert ^ { 2 } )越大,权重也就越大。所以,小类中心点C ^ { S _ { k } } ( t + 1 )是所有X _ { i } ( i = 1 , 2 , \cdots , N )的加权平均值对应的点。于是,有
                                       \hat { Y } f _ { h , K } ( C ^ { S _ { k } } ) = \hat { f } _ { h , G } ( C ^ { S _ { k } } ) \frac { 2 c _ { k } } { c _ { g } h ^ { 2 } } m _ { h , G } ( C ^ { S _ { k } } )

                                           m _ { h , G } ( C ^ { S _ { k } } ) = \frac { 1 } { 2 } h ^ { 2 } c \frac { \hat { V } f _ { h , K } ( C ^ { S _ { k } } ) } { \hat { f } _ { h , G } ( C ^ { S _ { k } } ) }               
其中, c = \frac { c _ { g } } { c _ { k } }。所以,均值偏移向量取决当前小类中心C ^ { S _ { k } } ( t )的核密度的梯估计(以K()为核函.数)与核密度估计(以G()为核函数)之比,且总是向着当前密度增加最大的方向偏移。
m _ { h , G } ( C ^ { S _ { k } } ) \approx 0,极端情况下: m _ { h , G } ( C ^ { S _ { k } } ) = C ^ { S _ { k } } ( t + 1 ) - C ^ { S _ { k } } ( t ) = C ^ { S _ { k } } ( t ) - C ^ { S _ { k } } ( t ) = 0 ^ { \ominus },即零梯度时,表示找到了高密度区域的一个稳定的中心点C ^ { S _ { k } } ( t )

(2)Mean-Shift 聚类过程

        Shift 聚类采用不断迭代的方式确定K个小类的类中心C ^ { S _ { k } }。首先,指定核宽h,然后进行N次迭代。对于第i次迭代,执行以下两步。
第一步,令样本观测点X _ { i }作为小类初始类中心C ^ { S _ { k } } ( 0 ) = X _ { i },然后进行如下次数的迭代。对第t次迭代:
①计算m _ { h , G } ( C ^ { S _ { k } } ) = \frac { 1 } { 2 } h ^ { 2 } c \frac { \hat { V } f _ { h , K } ( C ^ { S _ { k } } ( t ) ) } { \hat { f } _ { h , G } ( C ^ { S _ { k } } ( t ) ) }
②若,将小类心移至C ^ { S _ { k } } ( t + 1 )处。
        重复①、②,直到m _ { h , G } ( C ^ { S _ { k } } ) \leq \epsilon迭代结束,从而确定出一个高密度区域的稳定的小类中心点T。
        第二步,判断本次小类中心点T是否与已有小类中心点C ^ { S _ { j } }重合。若不重合则得到一个新的小类中心点C ^ { S _ { k } } = T
        经过上述N次迭代,将找到K个不重合的小类中心点C ^ { s _ { k } } , k = 1 , 2 , \cdots , K。可通过减少迭代次数的方式提高算法效率。例如,将样本数据分箱为M < N个组后再迭代(小类初始中心为组的中心点),此时只需迭代M次。
        当K个小类的类中心C ^ { S _ { k } }确定后,样本观测点X _ { i }所属的小类应为C _ { i } = \arg \max ( \vert \vert X _ { i } - C ^ { S _ { k } } \vert \vert ),即属于距X,最近的小类中心所在的小类。
        需要说明的是,Mean-Shift聚类的不必事先指定聚类数目K,但K与核宽h有关。通常核宽h越小,聚类数目K越大。

        下面给出具体代码分析应用过程:这段代码首先使用make_moons函数生成了一个具有噪声的月亮形状的数据集,其中包含300个样本点。然后,通过plt.scatter函数将生成的数据集在二维平面上可视化。接着,使用plt.savefig将图像保存到内存中,并使用Image.open将其读取并保存到相册中。该代码的作用是生成一个带有月亮形状数据点的散点图,并将图像保存到相册中。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io


X, ymoon = make_moons(300, noise=0.05, random_state=0)
fig=plt.figure(figsize=(10,4))
plt.figure(figsize=(6,4))

plt.scatter(X[:, 0], X[:, 1])

# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=500)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')

        代码运行结果如下图所示:对于生成的图像进行分析,可以看出数据点大致呈现出两个月亮形状的聚类,这与使用make_moons函数生成的数据集特征相符合。由于设置了噪声参数为0.05,因此在月亮形状的数据点周围存在一些离群点或噪声点,但整体上数据点还是比较集中在两个月亮形状的区域内。 

        下面代码用于分析生成的数据集进行Mean-Shift聚类的操作:这段代码是对生成的数据集进行Mean-Shift聚类的操作,并将不同带宽参数下的聚类结果可视化展示出来。首先,通过循环遍历不同的带宽参数值,创建Mean-Shift聚类器,并对数据集进行拟合。然后,提取聚类中心和标签信息,并绘制每个聚类的数据点以及聚类中心点。最后,将每个带宽参数值对应的聚类结果图像以子图形式展示在一个大图中,并在每个子图标题中显示估计的聚类数目和核宽度。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

fig=plt.figure(figsize=(18,10))
bandwidths=np.linspace(0.2,1.2,6)
colors = cycle('bgrcmyk')
i=0
for bandwidth in bandwidths:
    MS = MeanShift(bandwidth=bandwidth, bin_seeding=True)
    MS.fit(X)
    cluster_centers = MS.cluster_centers_
    labels = np.unique(MS.labels_)
    n_clusters_ = len(labels)
    i+=1
    plt.subplot(2,3,i)
    for k, col in zip(range(n_clusters_), colors):
        cluster_center = cluster_centers[k]
        plt.plot(X[MS.labels_==k, 0], X[MS.labels_==k, 1], col + '.')
        plt.plot(cluster_center[0], cluster_center[1], 'o', markerfacecolor=col,
                 markeredgecolor='k', markersize=14)
    plt.title('估计的聚类数目: %d\n(核宽:%.2f)' % (n_clusters_,bandwidth))
    
# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=500)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')
plt.show()

        运行结果如下图所示:对于生成的图像进行分析,可以看到不同带宽参数值下的聚类结果。随着带宽参数的增加,聚类中心的数量逐渐减少,即聚类数目减少。带宽参数值较小时,聚类中心较多,数据点被细分成多个小的簇;而当带宽参数值较大时,部分簇可能被合并成一个大的簇。因此,通过调节带宽参数,可以在一定程度上控制聚类的精细程度。

03-BIRCH聚类算法     

        BIRCH(Balanced Iterative Reducing and Clustering using Hierarchies)聚类算法是一种适用于大规模数据集的层次聚类算法。其主要特点是通过构建一个树形结构来表示数据的层次聚类,从而实现对数据进行高效的聚类分析。

        具体而言,BIRCH算法的工作原理如下:

        特征空间的划分: 首先,BIRCH算法将数据集表示为一个树形结构,这棵树被称为CF树(Clustering Feature tree)。CF树通过一个或多个内部节点来描述数据的分层聚类结构,每个内部节点都包含了一些子簇(称为CF项)的紧凑表示。

        聚类特征的紧凑表示: 每个CF项包含了子簇的聚类特征,如子簇的质心、数据点数量等。这些信息使得BIRCH算法能够在树的内部节点上进行紧凑的聚类特征计算,而不必维护所有数据点的详细信息,从而节省了内存和计算资源。

        迭代聚类和合并: BIRCH算法采用迭代的方式不断将数据点插入CF树中,并在插入后对树的结构进行调整和合并操作,以保持树的平衡性和紧凑性。这样可以动态地适应数据的变化和增长。

        最终聚类结果的提取: 当所有数据点都插入到CF树中后,可以通过遍历树的叶子节点和内部节点来提取最终的聚类结果。每个叶子节点代表一个最终的聚类簇,而每个内部节点代表了其子树的聚类特征。

        总体而言,BIRCH算法适合处理大规模数据集,并且能够在处理过程中动态地调整和优化聚类结构,以适应数据的特点和变化。

(1)BIRCH算法中的聚类特征树

        BIRCH 算法解决大数据集聚类的主要策略是引入了基于聚类特征(ClusteringFeature,CF)的聚类特征树(CF 树)。
        聚类特征树:
        聚类特征树是关于聚类特征的一棵倒置的树,以便直观展示聚类过程和聚类解,如图所示。这里首先对树结构做简要说明。首先,尽管从外观上看,图中的树形结构与系统聚类中的类似,但最大不同在于图中的每个叶节点(A1、A2、B1、B2、C1、C2)均代表一个子类,而不是代表系统聚类树中的一个样本观测。图中的数据集被聚成了6个小类。

        其次,具有同一父节点的若干小类可合并成一个中类,以树的中间节点(A、B、C)表示

        A1与A2为兄弟节点,它们有共同的父节点A,是A的子类。B1与B2为兄弟节点,它们有共同的父节点B,是B的子类等。若干中类还可继续合并成更大的中类(这里略去了),直到包含根节点在内的所有数据形成一个大类。
        此外,聚类特征树的本质是以树的形式,实现对数据集的高度压缩表示。这一点主要体现在:每个节点中并不会存储属于该小类(或中类或大类)的样本观测的全部信息,而是相应小类的聚类特征。
        聚类特征:
        第k个小类(树节点)的聚类特征一般由三组数值组成:第一,节点k的样本量N _ { k };第二,p维数值向量,它存储着节点k的p个聚类变量的线性和;第三,p维数值向量,它存储着节点k的p个聚类变量的平方和。表示为

例如,图13.5中,叶节点A1中包含5个样本观测\{ ( X _ { 1 } = 3 , X _ { 2 } = 4 ) , ( X _ { 1 } = 2 , X _ { 2 } = 6 ) ,( X _ { 1 } = 4 ,X _ { 2 } = 5 ) , ( X _ { 1 } = 4 , X _ { 2 } = 7 ) , ( X _ { 1 } = 3 , X _ { 2 } = 8 ) \},聚类变量X _ { 1 }的线性和为16, X _ { 2 }的线性和为30。聚类变量X _ { 1 }的平方和为54, X _ { 2 }的平方和为190。所以, C F _ { A 1 } = ( 5 , ( 1 6 , 3 0 ) ^ { T } , ( 5 4 , 1 9 0 ) ^ { T } )
对多个不相交的小类k和小类j,聚类特征具有可加性,即:

这种可加性意味着,可基于子节点k与j的聚类特征,直接计算得到其父节点的聚类特征。
引入聚类特征的原因是:一方面,只需根据聚类特征,便可方便地得到诸如小类质心、小类
内样本观测的离散程度等信息。例如,第k个小类的质心为( \frac { \sum _ { i = 1 } ^ { N _ { k } } X _ { i 1 } } { N _ { k } } , \frac { \sum _ { i = 1 } ^ { N _ { k } } X _ { i 2 } } { N _ { k } } , \cdots , \frac { \sum _ { i = 1 } ^ { N _ { k } } X _ { i p } } { N _ { k } } )。第k个小类在各维上的方差为另一方面,只需依据聚类特征,便可方便地计算出样本观测点X _ { i }与小类(如A与A1)间的距离(或类内离差平方)并快速完成小类的指派。
BIRCH算法中的基于聚类特征的聚类特征树,是实现大数据集在线动态聚类的基础。
        决定聚类特征树规模的参数:
        聚类特征树的规模,即树的层数和叶节点的个数,取决于两个参数:分支因子B和阈值T,它们是后续BIRCH聚类的两个非常关键的参数。可从图中直观理解。

        图中的聚类特征树是对的细化。其中,每个节点中均存储着相应小类的聚类特征。例如,图中第三行左侧的叶节点存储:C F _ { A 1 } = ( 5 , ( 1 6 , 3 0 ) ^ { T } , ( 5 4 , 1 9 0 ) ^ { T } ),第二行左侧的中间节点存储其子节点的聚类特征: C F _ { A 1 } + C F _ { A 2 } + C F _ { A 3 }
        进一步,图中的实线框代表一个节点(包括中间节点和叶节点),对应一个小类的聚类特征。虚线框表示节点空间,节点空间仅能容纳指定个数的节点。若将单个虚线框类比成计算机中的一个数组,实线框则对应数组中的一个数组单元。通常,数组大小(数组单元数)是需预先定义的,数组单元数不能大于预设值。此外,还额外需要内存单元(这里为指针单元)存储各类间的从属关系。如图中树的上下层关系、叶节点的左右邻居关系等。
        因每个节点和指针均对应一个数据存储单元,所以从计算机内存角度来讲,聚类特征树的规模,即树的层数、中间节点、叶节点的个数以及表示类间从属关系的指针个数等,均与设定的可用内存容量P有直接关系。具体讲,将涉及以下两个重要参数。
(1)非叶节点的子类个数
        每个非叶节点包含的子类个数不能大于指定的参数:分支因子B(可对应于前述的数组单元个数)。
        首先,根节点包含的子类个数不大于B个。如图13.6中第二层中间节点空间所包含的实线框个数不大于B个(这里B = 3 );每个中间节点包含的子类个数均不大于B个。如图13.6 第三层每个节点空间包含的实线框个数不大于B个(这里B=3)。BIRCH 算法规定,若某非叶节点的子类成员个数大于B,需将其“一分为二”以减少子类个数。
        总之,BIRCH算法称B为分支因子。显然,较大的分支因子B意味着允许存在较多的小类。较小的分支因子B表示不能将数据聚成较多的小类,此时聚类特征树的层数较大。分支因子B的大小取决于内存使用的容量限制P。
(2)叶节点允许的最大“半径”
        通常,将叶节点中样本观测点两两间距离的平均值,定义为叶节点的“半径”,它是判断样本观测点X,能否并入叶节点(小类)的依据:X,并入后,叶节点的“半径”不应大于一个指定的阈值T。原因是,若此时叶节点的“半径”较大且大于阈值T,意味着样本观测点的并入将导致叶节点(小类)内的差异性过大,并入是不恰当的。显然,指定的阈值T越小,小类内的差异
性越低,聚类数目K会越大。反之,指定的阈值T越大,小类内的差异性越高,聚类数目K会越小。应在聚类数目K和小类内的差异性之间取得平衡。
        综上所述,聚类特征树的各个节点并不直接存储数据本身,仅存储聚类特征,因而要求的存储空间远远小于存储原始数据集的情况。聚类特征实现了对数据的高度压缩存储,旨在确保有限内存的同时保证大数据集聚类的可实施性,以及计算的高效性。尽管BIRCH 聚类中的P是一个客观存在的可调参数,决定着分支因子B且间接影响阈值T。但从操作性上讲,通常会直接设定分支因子B和阈值T。
        分支因子B和阈值T具有内在联系。若阈值T较小会导致聚类数目K较大。当聚类数目K 大于分支因子B时,会导致聚类特征树结构的调整并对聚类解产生影响。若阈值T较大,则聚类数目K较少,但会出现小类内差异过大的问题。
        BIRCH聚类的核心步骤:
        算法提出者 Zhang 等学者指出,BIRCH聚类过程包含4大阶段。这里仅介绍其中的核心步骤。在给定分支因子B和阈值T的前提下,BIRCH算法采用随机逐个抽取和处理样本观测数据的方式,建立聚类特征树。
        首先,初始化聚类特征树。从数据集中随机抽取若干(小于B)个样本观测点作为各子类的初始类中心。然后,依次对每个样本观测X _ { i }做如下判断处理。
        第一步,沿“最近路径”找到与样本观测点X,距离最近的叶节点,记为Cmin。
        第二步,判断样本观测点X1 并入Cmin后,叶节点的半径d是否仍小于阈值T。
(1)当d<T时
        当d<T时,样本观测点 被“吸入” Cmin,如图所示。

        图中,对样本观测点b,它与a _ { 1 }的距离最近,且因进入a _ { 1 }所在小类后,小类“半径”d小于阈值T,而被  “吸入”;对样本观测点c,它与a _ { 1 }的距离最近,但因进入a _ { 1 }所在小类后,小类“半径”d大于阈值T,而不能被a1“吸入”。
(2)当d > T时
        当d>T时:样本观测点X _ { i }不能被“吸入”C _ { \min } ,此时需判断样本观测点X _ { i }能否自成一类“开辟”出一个新的叶节点。
        1)若Cmin所在的节点空间中的节点个数小于B,即C _ { \min }的兄弟节点个数小于B,且其父节点包含的子类个数也小于B,则样本观测点X _ { i }可以自成一类,成为C _ { \min }n的兄弟节点。节点空间中的子类个数增加一个,其父节点包含的子类个数增加1。如图13.7所示,第二层中间节点空间中的节点个数仍小于B,样本观测点c自成一类。
        2)若C _ { \min }的兄弟节点个数已等于B,也就是说其父节点包含的子类个数已等于B,需暂将样本观测点X _ { i }并入Cmin的父节点中。然后,再将C _ { \min }n的父节点一分为二“分裂”成两个子节点。如图13.7所示,样本观测点d距d _ { 1 }最近,但d > T。同时,  的父节点D已包含d _ { 1 } , d _ { 2 } , \cdots , d _ { B }个子类。此时,需暂将d并入D,然后再将D一分为二“分裂”成两个半圆所示的两个小类D _ { 1 }与D _ { 2 }。
        “分裂”时以相距最远的两个样本观测点为类质心,根据距离最近原则,分配Cmin的父节点中的各观测点到两个新的叶节点中。如图13.7中两个小类D _ { 1 } 、D _ { 2 }下的叶节点。特征树的层数将会增加。
        第三步,重新计算每个节点的聚类特征CF,为处理下一个样本观测点X _ { j }做好准备。
重复上述步骤,逐个对每个样本观测做一次且仅一次的判断处理。其间,聚类数目将不断增加,聚类特征树也将愈发茂盛。

        下面给出代码分析应用过程:这段代码首先生成了两个具有不同分布的样本观测点集合,分别存储在set1set2中,并将它们合并成一个数据集X。然后,使用BIRCH聚类算法对这个数据集进行聚类操作,通过调节分支因子和阈值参数,得到不同的聚类解,并将结果可视化展示出来。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

fig=plt.figure(figsize=(15,20))
np.random.seed(12345)
N1,N2=1000,1000
mu1,cov1=[0,0],[[1,0],[0,2]]
set1= np.random.multivariate_normal(mu1,cov1,N1)  #set1 = multivariate_normal(mean=mu1, cov=cov1,N1)
mu2,cov2=[10,10],[[1,0.9],[0.9,1]]
set2=np.random.multivariate_normal(mu2,cov2,N2)  #set2 = multivariate_normal(mean=mu2, cov=cov2,N2)

X=np.vstack([set1,set2])
ax=plt.subplot(421)
ax.scatter(X[:,0],X[:,1],s=40)
ax.set_title("%d个样本观测点的分布"%(N1+N2))
ax.set_xlabel("X1")
ax.set_ylabel("X2")

B=[50,100,100]
T=[0.5,1,1.5]
i=1
colors=cycle('bgrcmyk')
for b,tau in zip(B,T):
    Bi=Birch(n_clusters=None,threshold=tau,branching_factor=b)
    Bi.fit(X)
    labels=np.unique(Bi.labels_)
    print(len(labels))
    i+=1
    ax=plt.subplot(4,2,i)
    for color,k in zip(colors,labels):
        ax.scatter(X[Bi.labels_==k,0],X[Bi.labels_==k,1],c=color,s=30,alpha=0.5) 
    ax.set_title("%d个样本观测点的BIRCH聚类解(分支因子=%d,阈值T=%.2f)"%(N1+N2,b,tau))
    ax.set_xlabel("X1")
    ax.set_ylabel("X2")
# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=200)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')

        实例运行结果如下图所示:对于生成的图像进行分析,可以看到不同分支因子和阈值参数下的BIRCH聚类结果。随着分支因子和阈值参数的变化,聚类结果的形态和数量也会发生变化。较小的分支因子和阈值参数会导致更细粒度的聚类结果,而较大的参数则会产生更少但更大的聚类簇。这种参数调节能够影响聚类结果的密度和紧凑度,从而适应不同数据分布和聚类需求。

        下面代码用于,在这部分代码中,首先新增了两组具有不同分布的样本观测点集合NewX,分别存储在mu3cov3N3mu4cov4N4变量中。然后,将这两组新的数据点合并到原有数据集X中,形成新的数据集D。接着,使用BIRCH聚类算法对合并后的数据集X进行聚类,并通过动态和离线方式更新聚类结果,并将最终的聚类结果可视化展示出来。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

np.random.seed(12345)   #新增数据
mu3,cov3,N3=[7,7],[[1,0.9],[0.9,1]],10
mu4,cov4,N4=[10,0],[[1,0],[0,1]],10
NewX=np.random.multivariate_normal(mu3,cov3,N3)
NewX=np.vstack((NewX,np.random.multivariate_normal(mu4,cov4,N4)))
fig=plt.figure(figsize=(15,4))
ax=plt.subplot(131)
ax.scatter(X[:,0],X[:,1],s=40)
ax.scatter(NewX[:,0],NewX[:,1],marker='*',s=50)
ax.set_title("%d个样本观测点的分布"%(N1+N2+N3+N4))
ax.set_xlabel("X1")
ax.set_ylabel("X2")

b=100 
tau=1.5
Bi=Birch(n_clusters=None,threshold=tau,branching_factor=b)
ylabel=Bi.fit_predict(X)
labels=np.unique(Bi.labels_)
ax=plt.subplot(132)
colors ='bgrmyck'
for color,k in zip(colors,labels):
        ax.scatter(X[Bi.labels_==k,0],X[Bi.labels_==k,1],c=color,s=30,alpha=0.5)  
Bi.partial_fit(NewX)
for i,k in enumerate(Bi.labels_):
    ax.plot(NewX[Bi.labels_==k,0],NewX[Bi.labels_==k,1], '*', markerfacecolor=colors[k],
            markeredgecolor='k', markersize=10)
ax.set_title("%d个样本观测点的动态BIRCH聚类解"%(N1+N2+N3+N4))
ax.set_xlabel("X1")
ax.set_ylabel("X2")

D=np.vstack((X,NewX))  #离线更新
Bi.fit(D)
ax=plt.subplot(133)
for color,k in zip(colors,labels):
        ax.scatter(D[Bi.labels_==k,0],D[Bi.labels_==k,1],c=color,s=30,alpha=0.5)  
ax.set_title("%d个样本观测点的离线BIRCH聚类解"%(N1+N2+N3+N4))
ax.set_xlabel("X1")
ax.set_ylabel("X2")

# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=200)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')

        运行结果如下图所示:对于生成的图像进行分析,第一幅子图展示了所有样本观测点的分布,包括原有数据集X和新增的数据点NewX。第二幅子图展示了动态更新的BIRCH聚类结果,其中利用BIRCH算法对原有数据集进行聚类,然后通过partial_fit方法动态地更新聚类结果并展示新增数据点的聚类情况。最后一幅子图展示了离线更新的BIRCH聚类结果,即将原有数据集和新增数据集合并后重新进行聚类,展示了完整数据集的离线聚类结果。通过动态和离线更新,在线更新数据点后,BIRCH算法可以快速适应新数据并更新聚类结果,保持聚类准确性和效率。

        下面这部分代码首先使用了BIRCH算法进行系统聚类,聚成了3类,并对完整的数据集D进行聚类操作,将结果可视化展示在第一张子图中。接着,利用K均值(K-Means)算法确定了5个聚类中心,并将BIRCH算法应用于完整数据集D进行了优化聚类,将结果展示在第二张子图中。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

Bi=Birch(n_clusters=3)  #默认采用系统聚类且默认聚成3类
Bi.fit(D)
labels=np.unique(Bi.labels_)
colors ='bgrmyck'
fig=plt.figure(figsize=(10,5))
ax=plt.subplot(121)
for color,k in zip(colors,labels):
        ax.scatter(D[Bi.labels_==k,0],D[Bi.labels_==k,1],c=color,s=30,alpha=0.5)
ax.set_title("%d个样本观测点的基于系统聚类的优化解"%(N1+N2+N3+N4))
ax.set_xlabel("X1")
ax.set_ylabel("X2")

KM=KMeans(random_state=1,n_clusters=5)
Bi=Birch(n_clusters=KM)
Bi.fit(D)
labels=np.unique(Bi.labels_)
ax=plt.subplot(122)
for color,k in zip(colors,labels):
        ax.scatter(D[Bi.labels_==k,0],D[Bi.labels_==k,1],c=color,s=30,alpha=0.5)
ax.set_title("%d个样本观测点的基于K-均值聚类的优化解"%(N1+N2+N3+N4))
ax.set_xlabel("X1")
ax.set_ylabel("X2")

# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=200)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')

        实例运行结果如下图所示:对于生成的图像进行分析,第一张子图展示了基于系统聚类的BIRCH算法聚类结果,其中每个颜色代表一种聚类。第二张子图展示了基于K均值聚类的BIRCH算法优化聚类结果,同样每种颜色代表一种聚类。可以观察到,基于K均值聚类的优化解可能更符合某些数据集的内在结构,从而提供了更好的聚类效果。

04-基于两种算法对商品批发商的市场细分    

        本节将通过采用聚类分析对商品批发商进行市场细分的Pvthon实践案例,说明聚类方法的特点,体现聚类方法的实际应用价值。采用聚类分析对商品批发商进行市场细分。数据(来自UCI的 Wholesale 数据集)是关于荷兰商品批发市场中的 440个批发商,某年在各类商品的年批发销售额。商品类别包括生鲜(Fresh)、奶制品(Milk)、杂货类(Grocery)、冷冻食品(Frozen)、洗涤用品和纸类(Detergents_paper)、熟食类(Delicatessen)。这些批发商分别位于三个不同的区域(Region)且其销售渠道(Channel)主要包括餐饮类(包括酒店、餐厅和咖啡店等)和零售店两类。
        现希望采用 Mean-Shi 聚类和 BIRCH 聚类对批发商进行市场细分。一方面,由于各类批发商的主要特征是销售渠道不同,因此以销售渠道作为市场细分的评价依据9,对比两种算法。

以下是数据集中的部分数据情况:

        以下将首先对聚类变量进行对数变换,然后进行主成分分析,并绘制两个主成分的散点图,且以不同颜色和符号标记各批发商的销售渠道和区域,旨在观察批发商的销售渠道和区域分布特
点。代码及结果如下所示。

        Mean-Shif聚类通常采用具有对称性的径向基核函数,因此首先对数据进行对数变换以更好地满足算法要求。
        对数据进行主成分分析,提取两个主成分并输出主成分系数。结果表明,第1主成分主要受杂货类、洗涤用品和纸类销售额的影响。生鲜、冷冻食品和熟食类的销售额是
        绘制两个主成分的散点图,不同颜色和形状表示不同的销售渠道,如影响第2主成分的重要因素。

        绘制两个主成分的散点图,不同颜色和形状表示不同的区域,如右图所示。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn import decomposition
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

data=pd.read_csv("批发商数据.csv")
data


logX=data.iloc[:,range(2,8)].apply(lambda x:np.log(x))
pca =decomposition.PCA(n_components=2)
pca.fit(logX)
print('主成分系数',pca.components_)
Xtmp = pca.fit_transform(logX)
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(12,5))
channels=np.unique(data['Channel'])
regions=np.unique(data['Region'])
markers=['+','*','.']
colors=['r','g','b','y']
for channel in channels:
    axes[0].plot(Xtmp[data['Channel']==channel,0],Xtmp[data['Channel']==channel,1],colors[channel-1]+markers[channel-1],
                 label='渠道'+str(channel))
axes[0].set_title("主成分散点图(渠道)")
axes[0].set_xlabel("主成分1")
axes[0].set_ylabel("主成分2")
axes[0].legend(loc='lower right')
for region in regions:
    axes[1].plot(Xtmp[data['Region']==region,0],Xtmp[data['Region']==region,1],colors[region-1]+markers[region-1],
                 label='区域'+str(region))
axes[1].set_title("主成分散点图(区域)")
axes[1].set_xlabel("主成分1")
axes[1].set_ylabel("主成分2")
axes[1].legend(loc='lower right')
# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=200)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')


        主成分系数[[0.17371704-0.3944630,454363640,17219603-0.74551495-0.1494356]
[-0.68513571-0.16239926-0.06937908-0.487691-0.04191162 -0.50970874]

        左图显示,批发商在销售渠道上具有明显的聚集性。通过渠道1销售的批发商其在杂货类、洗涤用品和纸类上的销售额大多高于渠道2的。生鲜和熟食类销售额的大小在两个销售渠道上未表现出差异。右图显示各类商品的销售额在区域分布上并没有明显的不同。

        接下来采用 Mean-Shi 和 BIRCH 算法分别对数据进行聚类,对比两种算法的执行效率。

        引用time 模块用于统计函数的 CPU 运行时间。
        采用Mean-Shif 算法,指定核宽等于2进行聚类。保存CPU运行的起始和终止时间。保存聚类解。
        采用 BIRCH 算法聚类,并采用系统聚类对 BIRCH 聚类解进行优化,得到聚成两类时的聚类解。保存 CPU运行的起始和终止时间。
        接下来,绘制上述两种聚类算法聚类解的可视化图形,并计算两种算法的CPU运行时间。可视化 Mean-Shif 聚类解。不同颜色和形状表示不同的小类,如左图所示。可视化 BIRCH 聚类解。不同颜色和形状表示不同的小类,如右图所示。

#本章需导入的模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from itertools import cycle
import warnings
warnings.filterwarnings(action = 'ignore')
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False
from scipy.stats import gaussian_kde,multivariate_normal
from scipy.stats import norm
from sklearn.datasets import make_moons
from sklearn import decomposition
from sklearn.cluster import DBSCAN,Birch,KMeans,estimate_bandwidth,MeanShift
from PIL import Image
import io

data=pd.read_csv("批发商数据.csv")
data



import time
start1 = time.perf_counter()
#bandwidth = estimate_bandwidth(logX,quantile=0.1,random_state=1)
MS = MeanShift(bandwidth=2, bin_seeding=False) 
MS.fit(logX)
end1 = time.perf_counter()
labels1=np.unique(MS.labels_)

start2=time.perf_counter()
Bi=Birch(n_clusters=2)  #默认采用系统聚类
logX = np.ascontiguousarray(logX)
Bi.fit(logX)
end2 = time.perf_counter()
labels2=np.unique(Bi.labels_)

fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(12,5))
markers=['*','+','.','<','>','v','s']
colors=['g','r','b','y','c','m','k']
for k, col,m in zip(range(len(labels1)), colors,markers):
    axes[0].scatter(Xtmp[MS.labels_ == k, 0], Xtmp[MS.labels_ == k,1],c=col,marker=m)
axes[0].set_title("Mean-Shift聚类解(运行时间:%.3f秒)"%(end1-start1))
axes[0].set_xlabel("主成分1")
axes[0].set_ylabel("主成分2")

for k, col,m in zip(range(len(labels2)), colors,markers):
    axes[1].scatter(Xtmp[Bi.labels_ == k, 0], Xtmp[Bi.labels_ == k,1],c=col,marker=m)
axes[1].set_title("BIRCH聚类解(运行时间:%.3f秒)"%(end2-start2))
axes[1].set_xlabel("主成分1")
axes[1].set_ylabel("主成分2")
# 将图像保存到内存中
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=200)
buf.seek(0)

# 从内存中读取图像并保存到相册
image = Image.open(buf)
image.save('/4.png')

        左图显示,Mean-Shit聚类将数据聚成了多个小类,两个类成员较多的小类表现出类似的情况,即在杂货类、洗涤用品和纸类上的销售额较高和较低的批发商分别构成两个小类。此外,Mean-Shif聚类还依生鲜和熟食类销售额的大小,对批发商进行了更细致的市场细分。右图显示,BIRCH类形成的两个小类也表现出类似的情况,且算法的计算效率较高。

总结       

        Mean-Shift均值偏移聚类算法是一种基于密度的非参数化聚类方法,它不需要指定聚类的数量,而是通过在数据空间中寻找概率密度的高点(即密度估计的局部最大值)来确定聚类中心。算法通过迭代地移动数据点向概率密度较高的区域靠拢,直到达到密度的局部最大值为止。优点是能够自动发现不规则形状的聚类簇,不需要事先指定聚类数量,但缺点是计算复杂度较高,对数据量和维度敏感。

        BIRCH算法(Balanced Iterative Reducing and Clustering using Hierarchies)是一种适合大规模数据集的在线动态聚类算法。它采用层次化的聚类方法,先将数据集划分成多个子簇(子簇通常是一个密度较高的区域),然后通过合并相似的子簇来构建全局的聚类结构。BIRCH算法通过使用CF树(Clustering Feature tree)来高效地存储和更新聚类信息,从而在处理大规模数据时具有较高的效率。优点是能够在流式数据场景下进行在线聚类,并且对数据量的增长具有较好的可扩展性,但对参数的选择较为敏感,需要合适地调节分支因子和阈值参数来获得较好的聚类效果。

参考文献       

        [1] BAUDRY J P, Cardoso M, CELEUX G, et al. Enhancing the selection of a model-based clustering with external qualitativevariables[J. Advances in Data Analysis and Classification.2012,9(02):177--196.

  • 22
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一伦明悦

感谢,您的支持是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值