程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

Python中的数据聚类及可视化分析实践

hfteth 2025-02-16 20:58:39 技术文章 13 ℃

探索如何通过聚类分析揭露糖尿病预测数据集的特征!我们将运用Python的强力工具,深入挖掘数据,以直观的可视化揭示不同特征间的关系。一同探索聚类分析在糖尿病预测中的实践!

所有这些可视化都可以通过数据操作的基本工具(pandas和numpy)以及可视化的基础知识(matplotlib和seaborn)来创建。

from matplotlib import colormaps, pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import load_diabetes
from sklearn.preprocessing import MinMaxScaler
import numpy as np
import pandas as pd
import seaborn as sns

对于这个教程,我将使用嵌入在matplotlib中的糖尿病预测数据集,拟合了一个简单的k-means模型。

diabetesData = load_diabetes(as_frame = True).data
# 中心化和规模化可聚类特征
diabetesScaler = MinMaxScaler().fit(diabetesData)
diabetesDataScaled = pd.DataFrame(diabetesScaler.transform(diabetesData)
                                  , columns = diabetesData.columns
                                  , index = diabetesData.index)  # 构建三个小的聚类模型
km3 = KMeans(n_clusters = 3).fit(diabetesDataScaled)
km4 = KMeans(n_clusters = 4).fit(diabetesDataScaled)
km10 = KMeans(n_clusters = 10).fit(diabetesDataScaled)

matplotlib包通过其colormaps注册表提供了许多内置的颜色方案。选择一个颜色图对于整个可视化来说是方便的,而且选择得当是很重要的。这可能意味着评估从数据是否可以沿着从低到高的规模解释,还是在两个极端中最相关的数据,以及是否对主题(例如地形项目的绿色和棕色)具有主题性。当数据与将以什么顺序呈现之间没有特定关系时,nipy_spectral颜色图是一个不错的选择。

nps = colormaps['nipy_spectral']
# 查看整个颜色图
nps


每个matplotlib颜色图都由一系列元组组成,每个元组以RGBA格式描述颜色(尽管组件缩放到[0,1]而不是[0,255])。可以通过整数(介于0和255之间)或浮点数(介于0和1之间)访问地图中的单个颜色。靠近0的数字对应于颜色图的较低端的颜色,而靠近255的整数和靠近1.0的浮点数对应于颜色图的较高端的颜色。直观地说,可以通过整数或表示该整数作为255的商的浮点数来描述相同的颜色。

print(nps(51))
print(nps(0.2))
#(0.0, 0.0, 0.8667, 1.0)

聚类模型的经典可视化是一系列散点图,比较了输入到聚类模型中的每对特征,用颜色表示聚类分配。有实现此目标的内置方法,但DIY方法可以更好地控制颜色方案等细节。

def plotScatters(df, model):
    """ 根据数据帧中的每对列创建散点图。
    使用颜色表示模型标签。
    """
# 创建图形和轴
    plotRows = df.shape[1]
    plotCols = df.shape[1]
    fig, axes = plt.subplots(
        # 为数据帧中的每个特征创建一行和一列
        plotRows, plotCols
        # 放大图形大小以便于查看
        , figsize = ((plotCols * 3), (plotRows * 3))
    )
    # 遍历子图以创建散点图
    pltindex = 0
    for i in np.arange(0, plotRows):
        for j in np.arange(0, plotCols):
            pltindex += 1
            # 确定当前子图
            plt.subplot(plotRows, plotCols, pltindex)
            plt.scatter(
                # 比较数据帧的第i个和第j个特征
                df.iloc[:, j], df.iloc[:, i]
                # 使用整数聚类标签和颜色图来统一颜色选择
                , c = model.labels_, cmap = nps
                # 选择较小的标记大小以减少重叠
                , s = 1)
            # 在子图的底部行上标记x轴
            if i == df.shape[1] - 1:
                plt.xlabel(df.columns[j])
            # 在子图的第一列上标记y轴
            if j == 0:
                plt.ylabel(df.columns[i])
            plt.show()

这些图表同时完成了两项任务,显示了一对特征之间的关系,以及这些特征与聚类分配之间的关系。

plotScatters(diabetesDataScaled, km3)


随着分析的进行,很容易专注于较小的特征子集。

plotScatters(diabetesDataScaled.iloc[:, 2:7], km4)

为了更好地了解每个特征在每个聚类中的分布,我们还可以查看小提琴图。如果您不熟悉小提琴图,请将它们视为经典箱线图的成年表亲。箱线图只

识别分布的一些关键描述符,而小提琴图则根据整个概率密度函数进行轮廓绘制。

def plotViolins(df, model, plotCols = 5):
    """ 创建数据帧中每个特征的小提琴图
    使用模型标签进行分组。
    """
# 计算绘图网格所需的行数
    plotRows = df.shape[1] // plotCols
    while plotRows * plotCols < df.shape[1]:
        plotRows += 1
    # 创建图形和轴
    fig, axes = plt.subplots(plotRows, plotCols
                             # 放大图形大小以便于查看
                             , figsize = ((plotCols * 3), (plotRows * 3))
                            )
    # 从模型中识别唯一的聚类标签
    uniqueLabels = sorted(np.unique(model.labels_))
    # 从唯一标签中创建自定义子调色板
    # 这将返回
    npsTemp = nps([x / max(uniqueLabels) for x in uniqueLabels])
    # 向输入数据帧添加整数聚类标签
    df2 = df.assign(cluster = model.labels_)
    # 遍历子图以创建小提琴图
    pltindex = 0
    for col in df.columns:
        pltindex += 1
        plt.subplot(plotRows, plotCols, pltindex)
        sns.violinplot(
            data = df2
            # 使用聚类标签作为x分组器
            , x = 'cluster'
            # 使用当前特征作为y值
            , y = col
            # 使用聚类标签和自定义调色板来统一颜色选择
            , hue = model.labels_
            , palette = npsTemp
        ).legend_.remove()
        # 用特征名标记y轴
        plt.ylabel(col)
        plt.show()
plotViolins(diabetesDataScaled, km3, plotCols = 5)

小提琴图显示了每个聚类中每个特征的分布,但是查看每个聚类在每个特征的更广泛分布中的表示也是有帮助的。修改后的直方图可以很好地说明这一点。

def histogramByCluster(df, labels, plotCols = 5, nbins = 30, legend = False, vlines = False):
    """ 创建每个特征的直方图。
    使用模型标签进行颜色编码。
    """
    plotRows = df.shape[1] // plotCols
    while plotRows * plotCols < df.shape[1]:
        plotRows += 1
# 识别唯一的聚类标签
    uniqueLabels = sorted(np.unique(labels))

    # 创建图形和轴
    fig, axes = plt.subplots(plotRows, plotCols
                             # 放大图形大小以便于查看
                             , figsize = ((plotCols * 3), (plotRows * 3))
                            )
    pltindex = 0
    # 遍历输入数据中的特征
    for col in df.columns:
        # 将特征离散化到指定数量的箱中
        tempBins = np.trunc(nbins * df[col]) / nbins
        # 将离散化的特征与聚类标签交叉
        tempComb = pd.crosstab(tempBins, labels)
        # 创建与交叉表相同大小的索引
        # 这将有助于对齐
        ind = np.arange(tempComb.shape[0])

        # 确定相关的子图
        pltindex += 1
        plt.subplot(plotRows, plotCols, pltindex)
        # 创建分组直方图数据
        histPrep = {}
        # 一次处理一个聚类
        for lbl in uniqueLabels:
            histPrep.update(
                {
                    # 关联聚类标签...
                    lbl:
                    # ...与柱状图
                    plt.bar(
                        # 使用特征特定的索引设置x

位置
                        x = ind
                        # 使用与此聚类关联的计数作为柱高
                        , height = tempComb[lbl]
                        # 将此柱堆叠在先前聚类柱的顶部
                        , bottom = tempComb[[x for x in uniqueLabels if x < lbl]].sum(axis = 1)
                        # 消除柱之间的间隙
                        , width = 1
                        , color = nps(lbl / max(uniqueLabels))
                    )
                }
            )

        # 使用特征名标记每个图的x轴
        plt.xlabel(col)

        # 在第一列的图中标记y轴
        if pltindex % plotCols == 1:
            plt.ylabel('Frequency')
        plt.xticks(ind[0::5], np.round(tempComb.index[0::5], 2))

        # 如果需要,覆盖垂直线
        if vlines:
            for vline in vlines:
                plt.axvline(x = vline * ind[-1], lw = 0.5, color = 'red')

        if legend:
        leg1 = []; leg2 = []
        for key in histPrep:
            leg1 += [histPrep[key]]
            leg2 += [str(key)]
        plt.legend(leg1, leg2)

    plt.show()
histogramByCluster(diabetesDataScaled, km4.labels_)

当需要更多聚类类别时,这个过程很容易扩展。

histogramByCluster(diabetesDataScaled, km10.labels_)

这些可视化将为评估聚类模型提供强有力的基础。

我是superpenglife,一个拥有十年经验的Python、机器学习、深度学习、数据分析和大模型工程师。我的目标是为你提供关于各种清晰且实用的指南。

你的关注、点赞是对我最大的支持。

Tags:

最近发表
标签列表