ML-For-Beginners 聚类可视化实战指南:用 Pandas 与 Seaborn 诊断尼日利亚音乐数据集,为 K-Means 聚类做准备
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
本文基于 ML-For-Beginners 课程第 5 周(Clustering)的第一课 "Visualize",系统讲解无监督学习中的聚类(clustering):从定义、Scikit-learn 十种聚类方法的选型对比、五大算法家族的划分,到一条完整的实操链路——加载并体检一份包含 530 首尼日利亚歌曲的 Spotify 音乐数据集,用柱状图、相关系数热图、KDE 联合图与散点分布图逐层诊断数据形态,最终回答"这份数据适合用哪种聚类算法"这一前置问题。读完后,你将掌握一套可复用的"聚类前数据诊断"方法论。
一、什么是聚类:无监督学习的一种
聚类是无监督学习(Unsupervised Learning)的一个分支:它假设数据集是未标注的,或者说输入并不与任何预先定义的输出配对。聚类算法通过挖掘数据中隐含的模式,把无标签的样本自动划分为若干组(簇)。
与分类、回归等监督任务不同,聚类在建模前"不知道答案",这决定了它的一个典型定位——数据探索。原始课程文档给出的现实类比很直观:聚类就像把一堆洗衣物按家庭成员分拣;在数据科学中,聚类用于分析用户偏好、刻画任意无标签数据集的特征结构,"帮助理解混乱,就像一个袜子抽屉"。
1.1 专业场景中的聚类应用
在职业场景中,聚类的典型用途包括:
- 市场细分(market segmentation):例如确定哪些年龄段的群体购买哪些商品;
- 异常检测(anomaly detection):例如从信用卡交易数据集中发现欺诈行为;
- 医学影像分析:例如在一批医学扫描图中定位肿瘤;
- 搜索结果分组:按购物链接、图片或评论对搜索结果聚合;
- 隐私保护:数据被组织成簇之后可以分配一个簇 ID(cluster id),此后用簇 ID 指代数据点,而不是用更具身份暴露性的原始字段。
值得注意的是,文档指出聚类分析最早起源于 20 世纪 30 年代的人类学(Anthropology)与心理学领域,后来才进入统计学与机器学习。当你有一份想要"降维"后再做更细粒度分析的大数据集时,聚类常被用作在其他模型之前"先了解数据"的技术手段。
二、Scikit-learn 聚类方法选型:10 种方法与使用场景
Scikit-learn 提供了覆盖面很大的聚类方法族,选型完全取决于你的用例。原始课程文档基于 Scikit-learn 官方文档整理了一张简化的选型表,这里完整保留:
| 方法名 | 适用场景(Use case) |
|---|---|
| K-Means | 通用、归纳式(inductive) |
| Affinity Propagation(亲和传播) | 簇数多、簇大小不均、归纳式 |
| Mean-shift(均值漂移) | 簇数多、簇大小不均、归纳式 |
| Spectral Clustering(谱聚类) | 簇数少、簇大小均匀、迁移式(transductive) |
| Ward Hierarchical(Ward 层次聚类) | 簇数多、带约束的簇、迁移式 |
| Agglomerative Clustering(凝聚聚类) | 簇数多、带约束、非欧氏距离、迁移式 |
| DBSCAN | 非平坦几何、簇大小不均、迁移式 |
| OPTICS | 非平坦几何、密度可变的簇大小不均、迁移式 |
| Gaussian Mixtures(高斯混合) | 平坦几何、归纳式 |
| BIRCH | 含离群值的大数据集、归纳式 |
2.1 选型表背后的术语体系
这张表中的几个形容词——"归纳式/迁移式""平坦/非平坦""距离""约束""密度"——是理解聚类选型的关键,原始文档用五个名词解释块逐一展开:
(1)"迁移式(transductive)" vs "归纳式(inductive)"
- 迁移式推理:由观察到的训练样例直接映射到特定的测试样例;
- 归纳式推理:由训练样例归纳出一般规则,再把规则应用到测试样例。
文档给出的例子:假设一份数据只被部分标注,一些条目是"唱片(records)"、一些是"CD"、一些是空白,你的任务是为空白项打标签。走归纳式路线,就是训练一个模型去识别"唱片"和"CD"再把标签应用到无标签数据——这种路线对实际是"磁带(cassettes)"的条目会束手无策。而走迁移式路线,算法先把相似条目聚在一起,再对整组打标签,此时簇可能反映"圆形的音乐介质"和"方形的音乐介质",对未见过的类别更鲁棒。
(2)"非平坦(non-flat)" vs "平坦(flat)"几何
这一对术语源自数学:指两点间距离用"平坦"(欧氏,Euclidean)还是"非平坦"(非欧氏,non-Euclidean)几何方式度量。欧氏距离是两点间线段的长度;非欧氏距离则沿曲线度量。如果你的数据可视化后"似乎不存在一个平面上",就需要选用能处理非平坦几何的专门算法(如表中的 DBSCAN、OPTICS)。
(3)"距离(Distances)"
簇由其距离矩阵(点与点之间的距离)定义。距离的度量方式有多种:
- 欧氏簇由点值的平均定义,含一个"质心(centroid)"或中心点,距离即到该质心的距离;
- 非欧氏距离指向clustroid(类簇中心点)——离其他点最近的那个点,clustroid 本身也有多种定义方式。
(4)"约束(Constrained)"
约束聚类把"半监督"学习引入这种无监督方法:点与点之间的关系被标记为cannot-link(不可链接)或must-link(必须链接),相当于给数据集强加了一些规则。例如算法自由运行在一批无标签数据上时,产出的簇质量可能很差(把"圆形音乐物""方形音乐物""三角形物体""曲奇"混在一起分组);若给定约束("该物品必须由塑料制成""该物品必须能发出音乐"),算法就被"约束"着做出更好的选择。
(5)"密度(Density)"
含"噪声"的数据被认为具有"密度"特征。考察各簇内部点间距离,可能发现簇有疏有密("拥挤"程度不同),因此需要用恰当的聚类方法(如密度型算法)来分析。文档还特别提到,密度型方法(DBSCAN、Mean-shift、OPTICS)与 K-Means 在处理"簇密度不均的噪声数据集"上表现差异显著,这是选型时的重要判断依据。
三、五大聚类算法家族
聚类算法总数超过 100 种,具体用哪一种取决于手头数据的性质。原始文档归纳了五个主要家族:
- 层次聚类(Hierarchical clustering):对象按它与"近邻对象"(而非远处对象)的接近程度分类,簇的成员彼此间基于与其他对象的距离来形成。Scikit-learn 的凝聚聚类(agglomerative clustering)就属于层次聚类。
- 质心聚类(Centroid clustering):需要预先指定
k(要形成的簇数),然后算法确定每个簇的中心点并把数据向该中心点聚集。K-Means 是质心聚类的著名版本——中心由"最近均值"确定,故名 K-means;算法最小化数据点到簇的平方距离。
- 分布型聚类(Distribution-based clustering):基于统计建模,核心是计算一个数据点属于某个簇的概率,再据此指派。高斯混合(Gaussian mixtures)方法属于此类型。
- 密度型聚类(Density-based clustering):按点的密度(彼此聚集的程度)把数据点分入簇;远离群体的点被当作离群值或噪声。DBSCAN、Mean-shift、OPTICS 属于这一类型。
- 网格型聚类(Grid-based clustering):面向多维数据集,先建立一张网格,再把数据分配到网格的各个单元格中,由此形成簇。
这五大分类与第二节的选型表互相印证:选型表回答"什么数据形态配什么算法",算法家族则回答"算法的内在机制是什么"。
四、动手实操:可视化尼日利亚音乐数据
练习文件:notebook.ipynb(本课配套 notebook),数据文件:nigerian-songs.csv。
聚类作为一种技术,高度依赖恰当的可视化来辅助判断,因此课程从可视化音乐数据开始——这一步的目的是帮助我们决定:对于这种数据形态,哪种聚类方法最有效。
从源码结构看,仓库中该 notebook 的初始状态只含一个标题单元格("Nigerian Music scraped from Spotify - an analysis")和一个空代码单元格,即文档中的代码步骤需要你在 notebook 中逐步填写完成。
4.1 准备环境并加载数据
先安装可视化库,再读取数据:
!pip install seabornimport matplotlib.pyplot as plt import pandas as pd df = pd.read_csv("../data/nigerian-songs.csv") df.head()前几行数据长这样(该 CSV 实际为 530 行歌曲记录、16 列):
| name | album | artist | artist_top_genre | release_date | length | popularity | danceability | acousticness | energy | instrumentalness | liveness | loudness | speechiness | tempo | time_signature | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Sparky | Mandy & The Jungle | Cruel Santino | alternative r&b | 2019 | 144000 | 48 | 0.666 | 0.851 | 0.42 | 0.534 | 0.11 | -6.699 | 0.0829 | 133.015 | 5 |
| 1 | shuga rush | EVERYTHING YOU HEARD IS TRUE | Odunsi (The Engine) | afropop | 2020 | 89488 | 30 | 0.71 | 0.0822 | 0.683 | 0.000169 | 0.101 | -5.64 | 0.36 | 129.993 | 3 |
| 2 | LITT! | LITT! | AYLØ | indie r&b | 2018 | 207758 | 40 | 0.836 | 0.272 | 0.564 | 0.000537 | 0.11 | -7.127 | 0.0424 | 130.005 | 4 |
| 3 | Confident / Feeling Cool | Enjoy Your Life | Lady Donli | nigerian pop | 2019 | 175135 | 14 | 0.894 | 0.798 | 0.611 | 0.000187 | 0.0964 | -4.961 | 0.113 | 111.087 | 4 |
| 4 | wanted you | rare. | Odunsi (The Engine) | afropop | 2018 | 152049 | 25 | 0.702 | 0.116 | 0.833 | 0.91 | 0.348 | -6.044 | 0.0447 | 105.115 | 4 |
特征的含义可以理解为:popularity(流行度)、danceability(可舞性)、energy(能量)、loudness(响度)、speechiness(说话感)、tempo(节奏)等 Spotify 音频特征,外加专辑、艺人、流派、发行年份、时长、拍号等元数据。
4.2 数据体检:info() / isnull() / describe()
第一步,调用info()获取 DataFrame 的结构信息:
df.info()期望输出:
<class 'pandas.core.frame.DataFrame'> RangeIndex: 530 entries, 0 to 529 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 530 non-null object 1 album 530 non-null object 2 artist 530 non-null object 3 artist_top_genre 530 non-null object 4 release_date 530 non-null int64 5 length 530 non-null int64 6 popularity 530 non-null int64 7 danceability 530 non-null float64 8 acousticness 530 non-null float64 9 energy 530 non-null float64 10 instrumentalness 530 non-null float64 11 liveness 530 non-null float64 12 loudness 530 non-null float64 13 speechiness 530 non-null float64 14 tempo 530 non-null float64 15 time_signature 530 non-null int64 dtypes: float64(8), int64(4), object(4) memory usage: 66.4+ KB这与数据文件本身核对一致:530 个条目、8 个 float64 列、4 个 int64 列、4 个 object(字符串)列。
第二步,用isnull()复核缺失值,确认各列求和为 0:
df.isnull().sum()name 0 album 0 artist 0 artist_top_genre 0 release_date 0 length 0 popularity 0 danceability 0 acousticness 0 energy 0 instrumentalness 0 liveness 0 loudness 0 speechiness 0 tempo 0 time_signature 0 dtype: int64各列均为 0,数据干净,无需填补缺失值。
第三步,调用describe()查看数值分布:
df.describe()| release_date | length | popularity | danceability | acousticness | energy | instrumentalness | liveness | loudness | speechiness | tempo | time_signature | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 530 | 530 | 530 | 530 | 530 | 530 | 530 | 530 | 530 | 530 | 530 | 530 |
| mean | 2015.390566 | 222298.1698 | 17.507547 | 0.741619 | 0.265412 | 0.760623 | 0.016305 | 0.147308 | -4.953011 | 0.130748 | 116.487864 | 3.986792 |
| std | 3.131688 | 39696.82226 | 18.992212 | 0.117522 | 0.208342 | 0.148533 | 0.090321 | 0.123588 | 2.464186 | 0.092939 | 23.518601 | 0.333701 |
| min | 1998 | 89488 | 0 | 0.255 | 0.000665 | 0.111 | 0 | 0.0283 | -19.362 | 0.0278 | 61.695 | 3 |
| 25% | 2014 | 199305 | 0 | 0.681 | 0.089525 | 0.669 | 0 | 0.07565 | -6.29875 | 0.0591 | 102.96125 | 4 |
| 50% | 2016 | 218509 | 13 | 0.761 | 0.2205 | 0.7845 | 0.000004 | 0.1035 | -4.5585 | 0.09795 | 112.7145 | 4 |
| 75% | 2017 | 242098.5 | 31 | 0.8295 | 0.403 | 0.87575 | 0.000234 | 0.164 | -3.331 | 0.177 | 125.03925 | 4 |
| max | 2020 | 511738 | 73 | 0.966 | 0.954 | 0.995 | 0.91 | 0.811 | 0.582 | 0.514 | 206.007 | 5 |
这里文档提出一个值得深思的问题:既然聚类是无监督方法、不需要标签,为什么还要展示这些带标签的数据?答案是在数据探索阶段,这些标签(尤其artist_top_genre)很有用,但聚类算法本身并不依赖它们——你完全可以删掉列名、只按列号引用数据。这也是后面用流派字段做"分组对照"而非"目标变量"的原因。
另外注意popularity可以取 0(25 分位数就是 0),代表这些歌曲没有被评出流行度,稍后会过滤掉。
4.3 柱状图:找出最主流的流派
import seaborn as sns top = df['artist_top_genre'].value_counts() plt.figure(figsize=(10,7)) sns.barplot(x=top[:5].index,y=top[:5].values) plt.xticks(rotation=45) plt.title('Top genres',color = 'blue')小贴士:如果想看更多头部取值,把切片[:5]改大或直接去掉即可。注意当某个流派被标记为 "Missing" 时,意味着 Spotify 没有对该艺人做流派归类,应将其剔除。
4.4 数据清洗:去掉 Missing 流派与零流行度噪声
剔除流派为 "Missing" 的行,并重绘全流派分布:
df = df[df['artist_top_genre'] != 'Missing'] top = df['artist_top_genre'].value_counts() plt.figure(figsize=(10,7)) sns.barplot(x=top.index,y=top.values) plt.xticks(rotation=45) plt.title('Top genres',color = 'blue')聚焦前三大流派并过滤噪声:afro dancehall、afropop、nigerian pop明显主导了这份数据集;同时把popularity == 0的行也去掉——这些行没有流行度评级,对本练习而言可视为噪声:
df = df[(df['artist_top_genre'] == 'afro dancehall') | (df['artist_top_genre'] == 'afropop') | (df['artist_top_genre'] == 'nigerian pop')] df = df[(df['popularity'] > 0)] top = df['artist_top_genre'].value_counts() plt.figure(figsize=(10,7)) sns.barplot(x=top.index,y=top.values) plt.xticks(rotation=45) plt.title('Top genres',color = 'blue')4.5 相关系数热图:数据之间强相关吗?
快速测试数据中是否存在特别强的相关性:
corrmat = df.corr(numeric_only=True) f, ax = plt.subplots(figsize=(12, 9)) sns.heatmap(corrmat, vmax=.8, square=True)结论:唯一强的相关出现在energy与loudness之间——响亮的音乐通常能量也高,这并不意外;除此之外各列之间的相关性都相对较弱。这一点恰恰是有价值的:如果特征之间高度相关,某些聚类方法会受到冗余维度影响;而弱相关意味着每个特征都携带独立信息,"看看聚类算法能把这份数据做出什么花样"正是下一课的看点。
注意:相关不等于因果!我们只有相关的证据,没有因果的证据。
4.6 分布形态:KDE 联合图与 FacetGrid 散点图
练习——数据分布:这三个流派在"流行度决定可舞性感知"这件事上是否有显著差异?
第一步,用 KDE 联合图观察 popularity 与 danceability 的联合分布(按流派着色):
sns.set_theme(style="ticks") g = sns.jointplot( data=df, x="popularity", y="danceability", hue="artist_top_genre", kind="kde", )你会看到围绕一个总体收敛点的同心等高线,展示各流派点的分布。这里用的是 KDE(Kernel Density Estimate,核密度估计)图,用连续的概率密度曲线表示数据,便于在多个分布并存时做解读。总体来看,三个流派在流行度与可舞性上只是松散地对齐——要在这样松散分布的数据中确定簇,本身就是一项挑战。
第二步,创建 FacetGrid 散点图:
sns.FacetGrid(df, hue="artist_top_genre", height=5) \ .map(plt.scatter, "popularity", "danceability") \ .add_legend()同一坐标轴上的散点图呈现出与 KDE 图相似的分簇模式。文档在此留了一个开放问题:这个数据集在"歌曲流行度感知"与"可舞性"附近是否存在收敛?FacetGrid 显示存在对齐的同心圆——无论流派如何,尼日利亚听众的口味是否在这个流派中收敛到某个可舞性水平?你可以换不同的数据点(energy、loudness、speechiness)和更多或不同的流派来尝试,df.describe()表能帮你把握各数据点的大致分布范围。
从方法论上总结:聚类工作中,散点图是最常用的"簇探测器",因此掌握这类可视化非常实用。本课结束时,我们已经完成了三件事——确认数据干净、聚焦到三个主流派、并观察到特征间相关性弱但二维分布存在松散聚类倾向;下一课(K-Means)将直接基于这份过滤后的数据,用 K-Means 算法发现其中看似以有趣方式重叠的分组。
五、挑战与延伸阅读
挑战:为下一课做准备,绘制一张关于"生产环境中可能发现并使用的各类聚类算法"的图表——每种聚类算法试图解决什么问题?(可直接对照第二节的选型表与第三节的五大算法家族来组织。)
自测与延伸阅读:在套用聚类算法之前,理解你的数据集性质是明智的。原始课程文档还推荐了两篇外部资料:一篇讨论"如何为你的数据形态选择正确的聚类算法",另一篇带你走查不同数据形态下各聚类算法的行为差异(可在仓库原文档中查看出处)。
课后作业:研究聚类的其他可视化方式。要求见 assignment.md——用散点图(可结合不同绘图库)绘制并解释你的发现,评分标准是"呈现一个包含五个文档完善的散点图的 notebook"。
六、本课要点回顾
- 聚类是无监督学习,核心价值在于建模前的数据探索;选型取决于数据规模、簇的均匀性、几何性质(平坦/非平坦)与是否需要约束。
- Scikit-learn 的十种主流方法各有适用场景,K-Means 通用,DBSCAN/OPTICS 应对非平坦几何与密度不均,BIRCH 面向含离群值的大数据,Gaussian Mixtures 立足概率建模。
- 五大算法家族(层次、质心、分布、密度、网格)从机制层面解释了选型表的由来。
- 实操链路上,本课用 530 首尼日利亚歌曲的数据演示了完整的诊断流程:
info()看结构 →isnull()查缺失 →describe()看分布 → 柱状图定流派 → 过滤 "Missing" 与零流行度噪声 → 相关热图判断特征冗余 → KDE 与散点图观察分布形态。 - 诊断结论:数据无缺失、特征间相关弱、三维流派在 popularity×danceability 平面上松散聚集——为下一课 K-Means 聚类 提供了明确的数据基础。
【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考