DAY 20 常见聚类算法

知识点:

  1. 聚类的指标

  2. 聚类常见算法:kmeans聚类、dbscan聚类、层次聚类

  3. 三种算法对应的流程

聚类的指标

通俗易懂讲解(适合零基础笔记)

先铺垫一下:聚类就像把一堆混在一起的东西(比如苹果、香蕉、橘子)自动分堆—— 相似的放一堆,不相似的分开。聚类指标就是用来 ** 判断 “分堆分得到底好不好”** 的尺子。

一、内部指标(没有标准答案时用)

意思是:我们不知道哪些东西 “本该是一类”(比如只看到一堆水果,不知道名字),只能从 “分堆的结果本身” 判断好坏。

1. 轮廓系数(Silhouette Coefficient)

一句话理解:看每个样本 “待在自己堆里舒不舒服,去别的堆合不合适”。

  • 对每个样本算两个距离:
    • a:它和自己堆里其他样本的平均距离(“堆内亲密度”,越小越好);
    • b:它和最近的另一个堆里样本的平均距离(“堆间疏离度”,越大越好)。
  • 轮廓值 = (b - a) / max(a, b),范围是[-1, 1]

怎么看好坏

  • 越接近 1:样本在自己堆里很 “合群”,和别的堆离得远,分堆好;
  • 接近 0:样本卡在两个堆中间,分堆模糊;
  • 负数:样本分错堆了(比如苹果被分到香蕉堆里)。

生活化例子:如果一个苹果在 “苹果堆” 里,和堆里的苹果挨得近(a 小),离旁边的 “香蕉堆” 很远(b 大),它的轮廓值就高,说明分对了。

2. DB 指数(Davies-Bouldin Index)

一句话理解:看所有堆 “又紧凑又分散” 的程度。

  • 每个堆的 “紧凑度”:堆内样本的平均距离(越小越紧凑);
  • 堆之间的 “分散度”:两个堆中心的距离(越大越分散)。
  • DB 指数是 “每个堆的紧凑度 ÷ 堆间分散度” 的平均值,越小越好

生活化例子:苹果堆里的苹果都挤在一起(紧凑),香蕉堆也很紧凑,而且苹果堆和香蕉堆离得老远(分散),DB 指数就小,分堆好。

二、外部指标(有标准答案时用)

意思是:我们知道每个东西 “本该属于哪一类”(比如水果都贴了标签:苹果 / 香蕉 / 橘子),直接对比 “聚类结果” 和 “真实标签” 的匹配度。

1. 兰德指数(Rand Index, RI)

一句话理解:看聚类结果和真实标签 “想法一致的比例”。

  • 随便挑两个样本,看两种情况:
    • 情况 1:真实标签里是一类,聚类结果里也是一类(“都对”);
    • 情况 2:真实标签里不是一类,聚类结果里也不是一类(“都错但一致”);
  • 兰德指数 = (情况 1 + 情况 2)÷ 所有可能的样本对总数,范围[0, 1]越接近 1 越好

生活化例子

  • 两个苹果:真实是一类,聚类也放一类(对);
  • 一个苹果一个香蕉:真实不是一类,聚类也分开(对);
  • 一个苹果一个橘子:真实不是一类,但聚类放一起了(错)。兰德指数就是 “对的情况” 占总情况的比例。
2. 调整兰德指数(Adjusted Rand Index, ARI)

一句话理解:修正版的兰德指数(更靠谱)。

  • 兰德指数有个坑:哪怕 “随便分堆”,也可能算出不错的分数(比如 10 个样本全放一堆,兰德指数也不低);
  • ARI 修正了这个问题,范围[-1, 1]越接近 1 越好,0 附近等于 “随机分堆” 的水平。
3. 纯度(Purity)

一句话理解:看每个堆里 “最主要的那类东西占多少”。

  • 对每个堆:统计里面占比最高的真实类别数量 ÷ 这个堆的总样本数(就是这个堆的纯度);
  • 整体纯度 = 所有堆的纯度平均值,越接近 1 越好

生活化例子:一个堆里有 8 个苹果、2 个香蕉,这个堆的纯度是8/10=0.8;如果一个堆全是苹果,纯度就是 1(完美)。

三、总结

类型常用指标核心逻辑好坏判断
内部指标(无标准答案)轮廓系数堆内近、堆间远越接近 1 越好
DB 指数堆紧凑、堆分散越小越好
外部指标(有标准答案)调整兰德指数(ARI)和真实标签的匹配度越接近 1 越好
纯度每个堆的 “单一性”越接近 1 越好

零基础记法:不用记公式,记住 “内部指标看‘堆紧不紧、离得远不远’,外部指标看‘和标准答案像不像’” 就行~

如何理解聚类指标中的 ARI 和纯度?

ARI 和纯度都是有真实标签时用的外部指标,但看问题的角度完全不一样 ——ARI 看 “整体匹配度”,纯度看 “单个堆的干净度”,用生活化的例子拆解开就很好懂啦~

一、先懂纯度:看 “每个堆里最多的是啥,占多少”

纯度的核心逻辑超简单:每个堆里挑出占比最高的那类东西,算它占这个堆的比例,最后平均一下。它只关心 “单个堆干不干净”,不管 “同类有没有被拆成多个堆”。

举个栗子:水果分堆

假设真实标签是:苹果(A)、苹果(A)、香蕉(B)、香蕉(B)、橘子(C)、橘子(C)聚类结果分了 3 堆:

  • 堆 1:A、A、B → 最多的是 A(2 个),纯度 = 2/3≈0.67
  • 堆 2:B、C → 最多的是 B 或 C(1 个),纯度 = 1/2=0.5
  • 堆 3:C → 只有 C,纯度 = 1

整体纯度 =(0.67+0.5+1)/3≈0.72

纯度的特点:
  • ✅ 优点:特别好懂!老板问 “这个客户群里主要是啥人?”,直接拿纯度说 “90% 是老客户” 就行,业务解释成本低;
  • ❌ 缺点:“眼瞎”—— 哪怕把同一类拆成好几个堆,纯度也可能很高。比如把所有苹果拆成 3 个小堆,每个堆的纯度都是 1,整体纯度也高,但其实聚类把同类拆散了,效果并不好。

二、再懂 ARI(调整兰德指数):看 “和标准答案的整体想法一致吗”

ARI 的核心是对比 “聚类结果” 和 “真实标签” 对 “哪些样本该在一堆” 的判断是否一致,而且修正了 “随便分也得分高” 的坑。它看的是 “全局匹配”,不是单个堆。

还是用水果栗子(简化版):

样本:A1、A2、B1、B2(真实标签:A、A、B、B)

情况 1:聚类结果完美匹配 → 堆 1:A1、A2;堆 2:B1、B2
  • 所有 “该在一堆的样本对”(A1&A2、B1&B2)都聚在一起了;
  • 所有 “不该在一堆的样本对”(A1&B1、A1&B2、A2&B1、A2&B2)都分开了;
  • ARI≈1(完美)。
情况 2:聚类乱分 → 堆 1:A1、B1;堆 2:A2、B2
  • 真实里 A1&A2 该在一堆,结果被拆了;B1&B2 该在一堆,也被拆了;
  • 这种 “想法不一致” 的情况多,ARI≈0(接近随机分堆水平)。
情况 3:聚类全放一堆 → 堆 1:A1、A2、B1、B2
  • 虽然把同类放一起了,但也把不同类混了,ARI 会比 1 小很多(因为 “不该在一堆的样本对” 都被凑一起了)。
ARI 的特点:
  • ✅ 优点:客观严谨!不管是 “把不同类混一起” 还是 “把同类拆成多堆”,都会让 ARI 变低,能全面反映整体匹配度;
  • ❌ 缺点:数值不如纯度好解释(比如 ARI=0.7,得说 “整体匹配度不错”,没法直接说 “某堆里有多少 XX”)。

三、ARI 和纯度的核心区别(一张表记清)

特点ARI(调整兰德指数)纯度
看什么全局匹配度(和真实标签的想法一致吗)单个堆的干净度(堆里最多的类占比)
优点客观全面,排除随机干扰简单易懂,业务好解释
缺点数值不好给非技术人解释不管 “同类拆堆”,容易误判
适合场景学术对比、算法选型(要严谨)业务汇报、快速看单堆质量

零基础总结记法

  • 想 “跟老板快速说清某堆里有啥”→ 用纯度;
  • 想 “客观判断聚类整体好不好”→ 用 ARI;
  • 两者结合看更全面:比如 ARI 高 + 纯度高,说明聚类既整体匹配好,每个堆又干净~

如何选择合适的聚类指标?

选聚类指标的核心逻辑很简单:先看 “有没有标准答案”,再看 “你关心什么”,最后结合数据特点挑就行~

第一步:先判断 —— 有没有 “标准答案(真实标签)”?

这是最关键的分岔路,直接决定你用 “外部指标” 还是 “内部指标”:

情况 1:有真实标签(知道 “本该怎么分”)

比如你要给客户分群,手里有 “老客户 / 新客户 / 流失客户” 的真实标签;或者给水果分类,知道每个样本是苹果 / 香蕉 / 橘子。这种情况优先选外部指标,因为能直接对比 “聚类结果” 和 “标准答案” 的匹配度:

  • ✅ 想 “严谨对比”:选调整兰德指数(ARI)理由:它修正了 “随机分堆也得分高” 的坑,比兰德指数靠谱,适合需要客观评估的场景(比如学术实验、算法对比)。例子:你测试两种聚类算法,用 ARI 看哪种结果更贴近真实客户标签。
  • ✅ 想 “简单直观”:选纯度理由:一眼能看出每个簇里 “主要是什么”,业务上更好解释(比如老板问 “这个客户群里主要是啥客户?”,纯度能直接答)。例子:一个客户簇的纯度是 0.9,你能直接说 “这个群里 90% 是老客户”,比 ARI 的数字好懂。
情况 2:没有真实标签(不知道 “本该怎么分”)

比如你拿到一堆用户行为数据,想 “无中生有” 分群,没有任何参考标签。这种情况只能用内部指标,从聚类结果本身判断好坏:

  • ✅ 想 “看每个样本分对没”:选轮廓系数理由:能算出每个样本的 “舒适度”,还能画轮廓图(虽然不用懂画图),比如发现某个簇的轮廓值都很低,就知道这个簇分错了。例子:给用户分群后,发现 “簇 A” 的轮廓值都接近 1,“簇 B” 很多负数,说明簇 B 分杂了,得调整算法。
  • ✅ 想 “看整体分堆效果”:选DB 指数理由:聚焦 “簇紧不紧、簇间远不远” 的整体情况,数值越小说明整体分堆越清晰,适合对比不同聚类数量(比如 k-means 选 k=3 还是 k=4)。例子:试 k=3 时 DB 指数是 0.5,k=4 时是 0.8,说明 k=3 的整体分堆更紧凑、分散。

第二步:再考虑 —— 数据特点和业务需求

选指标不能只看标签,还要结合你的数据和目的:

1. 数据形状 / 密度不一样?
  • 如果数据是 “抱团型”(比如正常的客户群、水果堆):轮廓系数 + DB 指数都好用;
  • 如果数据是 “不规则型”(比如有的簇密、有的簇疏,像沙滩上的小石子堆和大石头堆):DB 指数可能比轮廓系数更稳(轮廓系数对稀疏簇容易误判)。
2. 业务更关心什么?
  • 要是业务怕 “把不同的东西混在一起”:优先选轮廓系数 / DB 指数(强调簇间要远);
  • 要是业务怕 “把同类的东西拆成好几堆”:可以看轮廓系数的 “簇内平均值”(簇内值越高,说明没拆散);
  • 要是需要给非技术人员解释:优先纯度(有标签)DB 指数(无标签),比轮廓系数的 “正负值” 好懂。

第三步:最后小技巧 —— 别只靠一个指标!

就像判断水果甜不甜,不能只看颜色,还要尝味道。聚类指标也一样:

  • 有标签时:ARI + 纯度一起看(比如 ARI 高但纯度低,说明整体匹配好但个别簇杂,可能是业务能接受的);
  • 无标签时:轮廓系数 + DB 指数一起看(比如轮廓系数高且 DB 指数低,说明分堆又准又清晰)。

总结:一张表搞定选择

场景优先选的指标为啥选?
有真实标签,要严谨对比调整兰德指数(ARI)客观,排除随机干扰
有真实标签,要简单好解释纯度业务上一眼能懂
无真实标签,看整体分堆效果DB 指数越小越清晰,适合选聚类数
无真实标签,看单个样本分对没轮廓系数能揪出分错的样本 / 簇
想全面评估组合用(比如 ARI + 纯度)避免单一指标的片面性

零基础记法:有标签选 ARI 或纯度,没标签选轮廓系数或 DB 指数,结合业务好不好懂、数据长啥样就行~

聚类常见算法:kmeans聚类、dbscan聚类、层次聚类

三种算法就像 “不同的分堆方法”—— 有的靠 “找中心点” 分,有的靠 “看密度抱团” 分,有的靠 “一层层合并 / 拆分” 分,用生活化的例子拆解开就很好懂啦~

一、kmeans 聚类:“找中心点,围着中心点分堆”

核心逻辑

先指定要分k 个堆,然后选 k 个 “中心点”,让所有样本往最近的中心点靠,再调整中心点位置,重复直到分堆稳定。

通俗步骤(比如分水果:苹果、香蕉、橘子,指定 k=3)
  1. 瞎选 3 个中心点:比如随便挑 1 个苹果、1 个香蕉、1 个橘子当 “老大”;
  2. 第一次分堆:把每个水果分到离自己最近的 “老大” 身边(比如苹果靠近苹果老大,香蕉靠近香蕉老大);
  3. 更新中心点:每个堆里找 “位置最中间” 的水果当新老大(比如苹果堆的中心苹果);
  4. 重复 2-3 步:直到中心点不再变,分堆就定了。
优缺点
  • ✅ 优点:简单、跑得快!大数据也能处理,结果好解释;
  • ❌ 缺点:必须先定 k(比如分 3 堆还是 4 堆),对 “球形簇”(比如圆乎乎的堆)友好,对长条、环形等奇怪形状的簇不行,还怕异常值(比如混个榴莲,可能带偏中心点)。
适合场景

数据是 “抱团成圆形 / 球形” 的(比如客户消费金额分群)、数据量较大、能大致判断分堆数量的情况。

用 “分水果堆” 讲懂 kmeans 聚类算法(超通俗版)

kmeans 就像老师帮小朋友分水果堆,核心就是 “先定分几堆→选堆头→凑堆→换堆头→直到分好”,一步一步拆开来超简单~

场景设定

假设你是老师,手里有一堆水果:苹果、香蕉、橘子混在一起,想让小朋友们帮忙分成 3 堆(苹果一堆、香蕉一堆、橘子一堆)—— 这就是 kmeans 要做的事,k=3(3 堆)。

kmeans 的 5 个步骤(对应分水果)
第一步:定好 “分几堆”(k 值)

先说好要分 3 堆(k=3),这是 kmeans 的 “前提”—— 得先告诉它分多少堆才行。

第二步:随便选 3 个 “堆头”(初始中心点)

从水果堆里随便抓 3 个水果当 “堆头”:比如抓 1 个苹果、1 个香蕉、1 个橘子,放在桌子上当 3 个堆的 “老大”。

第三步:所有水果 “找最近的堆头凑堆”

让每个水果都 “跑” 到离自己最近的堆头身边:

  • 苹果们觉得和 “苹果老大” 最像,都凑到苹果堆;
  • 香蕉们凑到香蕉堆;
  • 橘子们凑到橘子堆。这时候可能有点乱:比如有个香蕉不小心凑到苹果堆了(因为离得近),没关系,下一步调整。
第四步:更新 “堆头”(重新选中心点)

每个堆里选一个 “最中间” 的水果当新堆头:

  • 苹果堆里挑一个位置最居中的苹果当新老大;
  • 香蕉堆、橘子堆也一样换最中间的当老大。(比如刚才凑错堆的香蕉,现在新香蕉老大离它更近,它就会跑到香蕉堆了)
第五步:重复 “凑堆→换堆头”,直到堆头不变

来回重复第三步和第四步:

  • 第一次凑堆后换堆头,第二次凑堆更整齐,再换堆头…… 直到某一次换完堆头,所有水果的堆都不再变了 —— 分堆就完成啦!
再举个 “教室分小组” 的例子(更贴近生活)

比如老师想把全班 20 个同学按 “身高 + 体重” 分成 4 组(k=4):

  1. 先定 k=4,随便挑 4 个同学当 “组长”(初始中心点);
  2. 每个同学根据自己的身高体重,选离自己 “最像” 的组长组队;
  3. 每组里选一个 “身高体重最中间” 的同学当新组长;
  4. 重复组队、换组长,直到组长不再变,4 个小组就分好啦!
kmeans 的 “小脾气”(特点)
  • ✅ 优点:简单!分堆快,小朋友都能听懂的逻辑,大数据也能分;
  • ❌ 缺点 1:必须先定 k(比如分 3 堆还是 4 堆,定错了分不好);
  • ❌ 缺点 2:只认 “圆乎乎的堆”(比如苹果堆凑成圆形没问题,要是想分成长条的堆就不行);
  • ❌ 缺点 3:怕 “捣蛋鬼”(比如混个榴莲在水果堆里,榴莲离所有水果都远,会带偏堆头,分堆就乱了)。
一句话总结 kmeans

“先说好分几堆,选几个头头,大家凑头头,换更合适的头头,直到分好不动啦~”

二、DBSCAN 聚类:“看密度,抱团的算一类,孤零零的是路人”

核心逻辑

不指定分几堆,而是看 “样本挤不挤”—— 挤在一起的(密度高)算一类,孤零零的(密度低)算 “噪声”(不管)。

关键概念(用 “人群” 举例)
  • ε 邻域:你伸手能碰到的范围(比如半径 1 米);
  • 核心点:你身边 1 米内≥5 个人(密度够),你就是 “核心大哥”;
  • 边界点:你身边 1 米内 < 5 人,但挨着核心大哥,算大哥的小弟;
  • 噪声点:你身边 1 米内没人,就是路人。
通俗步骤(比如公园里分人群)
  1. 随便找个人,看他 1 米内≥5 人(核心点),就把他和身边的人归为一群;
  2. 从这群里的人继续往外找,只要挨着核心点的都拉进来(比如大哥的小弟旁边还有小弟,都算一群);
  3. 找不到新的人了,就换个没分群的人重复;
  4. 最后孤零零的人不管(噪声)。
优缺点
  • ✅ 优点:不用定 k!能分任意形状的簇(比如长条、环形),还能自动识别异常值;
  • ❌ 缺点:对参数敏感(比如 “1 米”“5 个人” 定不好就分错),高维数据(比如很多特征)里 “密度” 不好算,密度不均匀的簇分不好。
适合场景

数据形状不规则(比如地图上的商圈人群)、有异常值、不知道该分几堆的情况。

三、层次聚类:“要么从小堆合并,要么从大堆拆分”

核心逻辑

像 “搭积木” 或 “拆积木”:要么凝聚式(从每个样本单独一堆开始,每次把最像的两堆合并,直到成一个大堆),要么分裂式(从一个大堆开始,每次拆最不像的部分,直到每个样本一堆)—— 常用凝聚式。

通俗步骤(比如分一堆零食:薯片、饼干、糖果)
  1. 初始:薯片 1、薯片 2、饼干 1、饼干 2、糖果 1 各算一堆(共 5 堆);
  2. 第一次合并:薯片 1 和薯片 2 最像,合并成 “薯片堆”(剩 4 堆);
  3. 第二次合并:饼干 1 和饼干 2 最像,合并成 “饼干堆”(剩 3 堆);
  4. 第三次合并:薯片堆和饼干堆有点像(都是膨化 / 烘焙),合并成 “零食堆”(剩 2 堆);
  5. 最后合并:零食堆和糖果堆合并成 “所有零食堆”(剩 1 堆);
  6. 画 “树状图”:从下往上看,想分几堆就切一刀(比如切在 3 堆位置,就是薯片、饼干、糖果)。
优缺点
  • ✅ 优点:不用定 k!树状图超直观,能看到层级关系(比如薯片和饼干先合,再和糖果合);
  • ❌ 缺点:!数据量大了跑不动,对异常值敏感(比如混个辣条,可能早早就合并错)。
适合场景

数据量小(比如几十 / 几百个样本)、需要看层级关系(比如物种分类、客户细分的层级)的情况。

四、三种算法对比总结(笔记版)

算法核心逻辑要不要定 k?适合簇形状速度适合场景
kmeans找中心点,按距离分堆球形 / 圆形大数据、簇形状规则、已知 k
DBSCAN按密度抱团,识噪声不要任意形状(长条 / 环)形状不规则、有异常值
层次聚类逐层合并 / 拆分,画树状图不要任意形状小数据、需层级关系
零基础记法
  • 想快、数据抱团圆、知道分几堆 → kmeans;
  • 不知道分几堆、形状怪、有异常值 → DBSCAN;
  • 数据少、想看清谁先合谁后合 → 层次聚类~

如何选合适的聚类算法?

选聚类算法就像 “选工具干活”—— 先看 “活儿的特点”(数据情况、需求),再挑 “趁手的工具”(kmeans/DBSCAN/ 层次聚类),一步一步对号入座就行~

第一步:先问自己 3 个关键问题(定方向)

问题 1:数据量多不多?
  • 多(几千 / 几万甚至更多,比如客户行为数据):优先选kmeansDBSCAN(层次聚类跑不动);
  • 少(几十 / 几百个,比如物种样本、小团队客户):可以选层次聚类(能看层级关系,结果直观)。
问题 2:知道要分几堆(k 值)吗?
  • 知道(比如要分 “高 / 中 / 低”3 类客户):选kmeans(必须指定 k,刚好匹配);
  • 不知道(比如 “看看数据自然能分几堆”):选DBSCAN层次聚类(不用定 k)。
问题 3:数据的 “堆形状” 规则吗?
  • 规则(圆乎乎 / 抱团成球形,比如消费金额分群):选kmeans(它就擅长分这种堆);
  • 不规则(长条、环形、零散形状,比如地图上的商圈人群):选DBSCAN(层次聚类也能凑活,但不如 DBSCAN)。

第二步:按场景对号入座(选具体算法)

场景 1:大数据 + 知道分几堆 + 簇形状规则

比如:给 10 万用户按 “消费额 + 购买频率” 分 3 类(高价值 / 中价值 / 低价值)。

✅ 选kmeans

理由:跑得快、简单,专门对付 “圆簇 + 大数据”,指定 k=3 就行,结果也好解释。

场景 2:不知道分几堆 + 簇形状不规则 + 有异常值

比如:分析城市地图上的商圈人群(有的商圈是长条街,有的是零散小店,还有孤立的便利店)。

✅ 选DBSCAN

理由:不用定 k,能分任意形状的簇,还能自动把孤立的便利店当 “噪声” 去掉,不怕异常值。

场景 3:小数据 + 需要看层级关系

比如:给 20 种动植物样本按特征分类(想知道 “哪些物种先归为一类,再和其他类合并”)。✅ 选层次聚类

理由:数据少跑得动,还能画出 “树状图”,清楚看到谁先合、谁后合的层级关系,学术分析 / 小样本分类超合适。

场景 4:数据有异常值(比如混了极端值)

比如:客户数据里有几个 “一年消费百万” 的土豪(和普通客户差太远)。

✅ 选DBSCAN(能把土豪当噪声排除);

❌ 别选 kmeans(土豪会带偏中心点,分堆就乱了)。

按场景对号入座(一看就会)

具体场景选啥算法?为啥?
10 万用户分 3 类(高 / 中 / 低价值)kmeans数据多、知道 k=3、簇形状规则,跑得快
城市商圈人群分堆(形状不规则、有孤立小店)DBSCAN不知道 k、形状怪、能去掉孤立小店(噪声)
20 种动植物样本分类(想看谁先归一类)层次聚类数据少、要层级关系,树状图一目了然
客户数据里有极端土豪,想分自然堆DBSCAN能排除土豪这个异常值,分堆更准

第三步:避坑提醒(别选错)

  • 别用 kmeans 分 “长条 / 环形簇”:比如想分地图上的沿河商圈(长条),kmeans 会把两头的人硬拆成两堆,结果错得离谱;
  • 别用层次聚类处理大数据:比如给 10 万用户分群,层次聚类可能跑几小时还出不来结果;
  • 别用 DBSCAN 处理 “密度不均匀” 的数据:比如有的簇挤成一团,有的簇稀稀拉拉,DBSCAN 会把稀的簇当成噪声,分不好。

总结:一张表搞定选择

场景特点优先选的算法为啥选?别选啥?
大数据、知道 k、簇形状规则kmeans快、简单、适配规则簇层次聚类、DBSCAN
不知道 k、簇形状不规则、有异常值DBSCAN不用定 k、分任意形状、去噪声kmeans
小数据、需要层级关系(谁先合谁后合)层次聚类树状图直观、看层级kmeans(浪费层级信息)

零基础记法

大数据 + 知 k + 圆簇→kmeans;

不知 k + 怪形状→DBSCAN;

小数据 + 要层级→层次聚类~

三种聚类算法的具体流程

就像不同的 “分堆游戏规则”,kmeans、DBSCAN、层次聚类的流程各有套路,用生活化的例子拆成 “一步步操作”,记起来超简单~

一、kmeans 聚类:“定堆数→选老大→凑堆→换老大→直到不乱”

核心:围绕 “中心点” 反复调整分堆
通俗流程(以 “分水果堆,k=3” 为例):
  • 定好要分几堆(k 值)

先明确分 3 堆(苹果、香蕉、橘子各一堆),这是 kmeans 的 “前提条件”。

  • 随机选 k 个 “初始中心点”(堆老大)

从水果里随便抓 3 个:1 个苹果、1 个香蕉、1 个橘子,放在桌子上当 3 个堆的 “老大”。

  • 所有样本 “找最近的老大凑堆”

每个水果看离哪个老大最近:苹果靠苹果老大,香蕉靠香蕉老大,哪怕个别香蕉凑错到苹果堆也没关系。

  • 更新中心点(选新老大)

每个堆里选 “位置最中间” 的水果当新老大:比如苹果堆里挑最居中的苹果,香蕉堆换最居中的香蕉。

  • 重复 3-4 步,直到中心点不变

再让水果重新凑新老大的堆,再换更合适的老大…… 直到某一轮后,所有水果的堆不再变,中心点也固定了,分堆完成!

一句话记流程:定 k→选老大→凑堆→换老大→稳定收尾

二、DBSCAN 聚类:“定规则→找大哥→拉小弟→标路人”

核心:按 “密度” 抱团,不用定堆数
先明确两个 “游戏规则”(参数):
  • ε 邻域:比如 “伸手能碰到的范围(半径 1 米)”;
  • 最小点数:比如 “身边 1 米内至少 5 个人才算大哥”。
通俗流程(以 “公园分人群” 为例):
  • 随机选一个没标记的人,查他的 “ε 圈”

比如先找穿红衣服的人,看他 1 米内有几个人。

  • 判断是不是 “核心点(大哥)”

如果他 1 米内≥5 人→是大哥,标记他为 “核心点”,并把他和圈里的人归为 “第一群”。

  • 从群里的人继续 “扩圈”

比如第一群里的绿衣服小哥,他的 1 米内也有≥5 人(也是大哥),就把绿衣服小哥圈里的人也拉进第一群;哪怕绿衣服小哥圈里有人是 “小弟”(1 米内 < 5 人),也一起拉进来。

  • 直到扩不动,换新人重复

第一群再也拉不到新人了,就找下一个没标记的人,重复 1-3 步,分第二群、第三群……

  • 标记 “噪声点(路人)”

最后剩下那些 1 米内没人,也挨不着任何大哥的人,就是 “路人”,不算任何群。

一句话记流程:定规则→找大哥→扩小弟→标路人→分完所有群

三、层次聚类(凝聚式):“单样本成堆→合并最像的堆→直到成一堆→切分树状图”

核心:从 “各自成堆” 到 “合并成一堆”,最后按需切分
通俗流程(以 “分零食堆:薯片、饼干、糖果” 为例):
  • 初始化:每个样本单独成一堆

薯片 1、薯片 2、饼干 1、饼干 2、糖果 1,各算一个堆(共 5 堆)。

  • 找 “最像的两堆” 合并

对比所有堆的相似度:薯片 1 和薯片 2 最像→合并成 “薯片堆”(剩 4 堆);接着饼干 1 和饼干 2 最像→合并成 “饼干堆”(剩 3 堆)。

  • 重复合并,直到只剩一个大堆

再对比:薯片堆和饼干堆有点像(都是膨化 / 烘焙类)→合并成 “零食堆”(剩 2 堆);最后零食堆和糖果堆合并成 “所有零食堆”(只剩 1 堆)。

  • 画 “树状图”,按需切分堆数

把合并过程画成 “树”(从下往上是合并顺序):想分 3 堆就从 “薯片堆、饼干堆、糖果堆” 的位置切一刀;想分 2 堆就从 “零食堆、糖果堆” 切一刀。

一句话记流程:单堆→合最像→成一堆→切树图定堆数

三种算法流程对比表(笔记版)

算法流程关键步骤核心动作要不要定参数?
kmeans定 k→选初始中心点→分堆→更心中心点→重复稳定围绕中心点调整要定 k(堆数)
DBSCAN定 ε 和最小点数→找核心点→扩簇→标噪声按密度扩群要定 ε(范围)和最小点数
层次聚类单样本成堆→合并最像堆→成一堆→切树图逐层合并不用定堆数(最后切分)

零基础记法:kmeans 记 “定 k 换老大”,DBSCAN 记 “找大哥拉小弟”,层次聚类记 “合并到一堆再切”~

聚类的通用流程

聚类就像 “给一堆东西分堆” 的完整工作 —— 从 “准备东西” 到 “分好堆再检查”,一步一步来,哪怕零基础也能跟着走~

第一步:先想清楚 “为啥要分堆?”(明确目标)

先搞明白聚类的目的,不然分了也白分:

  • 比如:“给客户分群,好针对性发优惠券”“给商品分类,方便上架”“给城市商圈分堆,规划店铺选址”。
  • 例子:你想给水果摊的水果分堆,目标是 “让顾客拿的时候更方便”,这就定好方向了。

第二步:准备 “要分的东西”(数据准备)

就像分水果前要先把烂果子挑掉、洗干净,数据也得 “收拾干净”:

  1. 选数据:挑和目标相关的数据(比如客户聚类就选 “消费额、购买频率、买过的商品类型”,别选 “客户名字、手机号” 这种没用的);
  2. 洗数据:去掉错数据(比如消费额写 “1000000” 明显是输错了)、补全缺数据(比如有的客户没填购买频率,就用平均值补上);
  3. 预处理:把数据 “调成一样的标准”(比如消费额是 “元”,购买频率是 “次 / 月”,单位不一样,要换成 0-1 之间的数,不然消费额的大数会 “欺负” 小数)。

例子:分水果时,先挑掉烂苹果,把大小不一的水果都按 “直径”“重量” 来描述(统一标准)。

第三步:选 “分堆的依据”(特征选择 / 处理)

选哪些 “特点” 来判断 “像不像”?比如分水果可以看 “颜色、形状、味道”,分客户看 “消费能力、购物偏好”:

  • 别选太多没用的特征(比如分水果看 “贴纸颜色” 就没意义);
  • 要是特征太多(比如客户数据有 10 个指标),可以先 “精简”(比如把 “每次消费额” 和 “每月消费次数” 合并成 “月消费总额”)。

例子:决定用 “甜度、脆度” 给苹果分堆,而不是看 “有没有叶子”。

第四步:挑 “分堆的方法”(选算法 + 调参数)

根据之前讲的选算法的技巧,挑合适的算法,再定参数:

  • 比如:数据多、知道分 3 堆、堆是圆的→选 kmeans,定 k=3;
  • 比如:不知道分几堆、堆形状怪→选 DBSCAN,定 “ε=1 米、最少 5 个算一堆”。

例子:分水果选 “按大小分堆” 的方法(算法),定 “大中小 3 堆”(参数)。

第五步:动手 “分堆”(执行聚类)

把收拾好的数据放进选好的算法里,让电脑自动分堆:

  • kmeans 会跑几遍,直到堆稳定;
  • DBSCAN 会找 “挤在一起的” 当一堆,孤立的当路人;
  • 层次聚类会一层层合并,最后出树状图。

例子:按大小把苹果放进三个筐里,大的装一个筐,中的一个,小的一个。

第六步:看看 “分堆分好不好?”(结果评估)

用之前学的指标检查分堆效果:

  • 没有标准答案→看轮廓系数(堆内近、堆间远?)、DB 指数(堆紧不紧?);
  • 有标准答案→看 ARI(和真实标签像不像?)、纯度(堆里干不干净?)。

例子:检查苹果堆 —— 大筐里是不是都是大苹果(纯度高)?大筐和中筐的苹果差别明显吗(堆间远)?

第七步:用起来!(结果解读 + 应用)

最后分析每个堆的特点,用到实际里:

  • 比如客户分堆后,“高价值堆” 的特点是 “每月消费超 500 元、买高端商品”,就给他们发高端优惠券;
  • 比如水果分堆后,大苹果卖 10 元 / 斤,小苹果卖 5 元 / 斤。

例子:把大苹果摆显眼位置高价卖,小苹果做促销,中苹果正常卖。

总结:聚类流程一句话记

“定目标→整数据→选特点→挑方法→分堆→检查→用起来”,就像包饺子:先想包啥馅(目标),再和面买菜(整数据),选馅和皮(特点),挑包法(算法),包好(分堆),煮了尝尝好不好(评估),最后吃(应用)~

小提醒

要是分堆效果不好,就回头调整:比如换算法、改参数、重新选特征,直到分满意为止~

作业:对心脏病数据集进行聚类。

按照之前讲解的聚类通用流程(明确目标→数据准备→特征处理→算法选择→执行聚类→结果评估→结果解读),已完成对心脏病数据集的完整聚类分析,以下是详细内容和成果。

一、聚类分析完整流程

1. 明确目标

  • 核心目标:基于 13 个健康指标,识别不同风险层次的心脏病患者群体
  • 业务价值:为医疗风险评估和干预提供数据支持

2. 数据准备

  • 数据概况:303 个患者样本,13 个健康指标 + 1 个目标变量(0 = 无风险,1 = 有风险)
  • 数据预处理
    • 标准化处理:消除量纲影响(如血压和心率的单位差异)
    • 缺失值处理:无缺失值,数据完整性 100%
    • PCA 降维:13 维→2 维,用于聚类结果可视化

3. 特征处理

  • 相关性分析:无高度相关特征(| 相关系数 |≤0.7),保留所有 13 个特征
  • 关键特征:年龄、血压、胆固醇、最大心率、ST 段压低、血管数量等心脏病核心指标

4. 算法选择与执行

测试三种主流聚类算法,通过轮廓系数DB 指数评估效果:

算法聚类数轮廓系数DB 指数表现评价
KMeans20.1682.187✅ 最优
层次聚类20.1332.564⚠️ 良好
DBSCAN0--❌ 不适用(全为噪声)

5. 结果评估与解读

最优算法(KMeans,k=2)的聚类结果:
  • 簇 0:低风险人群(105 人,占 34.7%)

    • 特征:年龄较大(58.5 岁)、血压正常、心肌缺血轻、血管病变少
    • 健康比例:88.6% 为真实健康人群(target=0)
    • 建议:定期监测,适当运动
  • 簇 1:高风险人群(198 人,占 65.3%)

    • 特征:年龄较轻(52.2 岁)、心率功能弱、心肌缺血严重、血管病变多
    • 患病比例:77.3% 为真实患病人群(target=1)
    • 建议:立即检查,严格控制风险因素

二、核心结论

  1. 算法选择:KMeans 在本数据集上表现最优,适合心脏病风险分层
  2. 风险识别:成功区分低风险(34.7%)和高风险(65.3%)人群,与真实患病情况匹配度高
  3. 业务价值:可直接用于医疗风险筛查,优先关注高风险人群
  4. 学习价值:完整的聚类流程代码,适合零基础学习聚类分析

心脏病数据集聚类簇(KMeans,2 簇)详细特征对比表

基于最优聚类算法(KMeans)的结果,将样本分为 “低风险簇” 和 “高风险簇”,以下是两簇在 13 个核心健康指标及样本分布上的详细对比,数据来源heart_with_kmeans_cluster.csv中各簇特征的均值统计(保留 2 位小数)。

特征类别特征名称单位簇 0(低风险)簇 1(高风险)差异分析
基础信息样本数量105198高风险簇样本量是低风险簇的 1.9 倍,占总样本的 65.3%
平均年龄58.5252.21低风险簇年龄更高(约大 6.3 岁),说明年轻群体心脏病风险需重点关注
心血管指标静息血压(trestbps)mmHg129.33134.57高风险簇血压更高(约高 5.2mmHg),血压异常与心脏负荷增加相关
血清胆固醇(chol)mg/dl242.68251.44高风险簇胆固醇略高(约高 8.8mg/dl),虽未达 “高胆固醇” 标准(≥240mg/dl),但仍体现风险差异
空腹血糖(fbs)(0 = 正常,1=≥120mg/dl)0.140.16两簇血糖异常比例接近,均低于 20%,血糖对本次聚类风险分层影响较小
心脏功能指标静息心电图结果(restecg)(0 = 正常,1 = 异常)0.510.58高风险簇心电图异常比例略高(7 个百分点),反映心脏电活动稳定性稍差
最大心率(thalach)次 / 分142.31133.05低风险簇最大心率更高(约高 9.3 次 / 分),说明心脏储备功能更强
运动诱发心绞痛(exang)(0 = 无,1 = 有)0.160.52高风险簇心绞痛比例是低风险簇的 3.25 倍,运动时心肌缺血风险显著更高
ST 段压低(oldpeak)mV0.541.28高风险簇 ST 段压低更明显(约高 1.4 倍),ST 段异常是心肌缺血的直接表现
ST 段斜率(slope)(1 = 上坡,2 = 平坦,3 = 下坡)1.591.89高风险簇 ST 段斜率更接近 “平坦 / 下坡”(正常为 “上坡”),心脏舒张期供血能力较弱
病变相关指标荧光检查血管数(ca)条(0-3)0.381.16高风险簇病变血管数是低风险簇的 3.1 倍,血管病变数量越多,心脏供血越差
地中海贫血(thal)(0 = 正常,1 = 固定缺陷,2 = 可逆缺陷)0.571.32高风险簇 “可逆缺陷” 比例更高,反映心肌损伤的可逆性风险(需及时干预)
风险验证真实患病比例(target)%11.4377.27高风险簇真实患病比例是低风险簇的 6.8 倍,聚类结果与真实标签匹配度极高

表格说明

  1. 簇风险定位依据:以 “真实患病比例” 为核心验证指标,簇 0 患病比例仅 11.43%,定义为 “低风险簇”;簇 1 患病比例 77.27%,定义为 “高风险簇”。
  2. 关键风险特征:对两簇差异影响最大的指标依次为 “运动诱发心绞痛(exang)”“荧光检查血管数(ca)”“ST 段压低(oldpeak)”,均为临床诊断心脏病的核心依据。
  3. 特殊发现:低风险簇虽年龄更高,但心血管功能(如最大心率、血压)更稳定,说明 “年龄” 并非心脏病的唯一风险因素,需结合多指标综合判断。
# 1. 导入依赖库(修复后无多余导入)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.metrics import silhouette_score, davies_bouldin_score, adjusted_rand_score

# 2. 加载数据集(Mac路径容错处理)
try:
    # 优先用相对路径(确保heart.csv和main.py在同一文件夹)
    df = pd.read_csv('heart.csv')
    print("✅ 数据集加载成功!")
except FileNotFoundError:
    # 若相对路径失败,提示手动修改绝对路径(示例:Mac桌面路径)
    print("❌ 错误:未找到heart.csv文件!")
    print("   请将代码中pd.read_csv('heart.csv')的路径改为绝对路径,例如:")
    print("   pd.read_csv('/Users/你的用户名/Desktop/heart_cluster_project/heart.csv')")
    exit()

# 3. 数据探索(快速了解数据)
print("\n=== 数据集基本信息 ===")
print(f"样本数:{df.shape[0]} | 特征数(含target):{df.shape[1]}")
print("\n前5行数据预览:")
print(df.head())
print("\n缺失值统计(全为0表示无缺失):")
print(df.isnull().sum())

# 4. 数据预处理(标准化+降维)
# 分离特征(X)和真实标签(y_true,仅用于评估)
X = df.drop('target', axis=1)
y_true = df['target']

# 标准化:消除量纲影响(如血压和心率的单位差异)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print(f"\n✅ 数据标准化完成:{X_scaled.shape}")

# PCA降维:13维→2维(仅用于可视化,不影响聚类计算)
pca = PCA(n_components=2, random_state=42)
X_pca = pca.fit_transform(X_scaled)
print(f"✅ PCA降维完成:{X_pca.shape}")

# 5. 特征相关性分析(排除高冗余特征)
corr_matrix = X.corr()
high_corr = []
for i in range(len(corr_matrix.columns)):
    for j in range(i+1, len(corr_matrix.columns)):
        if abs(corr_matrix.iloc[i, j]) > 0.7:
            high_corr.append(f"{corr_matrix.columns[i]} ↔ {corr_matrix.columns[j]}(相关系数:{corr_matrix.iloc[i, j]:.2f})")

print("\n=== 高相关特征检测 ===")
if high_corr:
    for pair in high_corr:
        print(f"⚠️ {pair}(建议删除其中一个特征)")
else:
    print("✅ 无高相关特征,保留所有13个特征用于聚类")

# 6. 执行3种聚类算法(核心步骤)
cluster_results = {}  # 存储各算法结果

# 算法1:KMeans(心脏病数据最优,k=2对应低/高风险)
kmeans = KMeans(n_clusters=2, random_state=42, n_init=10)  # n_init=10避免随机误差
y_kmeans = kmeans.fit_predict(X_scaled)
cluster_results['KMeans'] = y_kmeans
print(f"\n✅ KMeans:{len(set(y_kmeans))}个簇,样本分布:{np.bincount(y_kmeans)}")

# 算法2:DBSCAN(自动识别簇和噪声,无需指定k)
dbscan = DBSCAN(eps=1.2, min_samples=6)  # 调整参数减少簇数量,降低复杂度
y_dbscan = dbscan.fit_predict(X_scaled)
cluster_results['DBSCAN'] = y_dbscan
# 统计有效簇(排除噪声点-1)
dbscan_clusters = [c for c in set(y_dbscan) if c != -1]
print(f"✅ DBSCAN:{len(dbscan_clusters)}个有效簇,噪声点:{list(y_dbscan).count(-1)}个")

# 算法3:层次聚类(凝聚式,适合小数据)
hierarchical = AgglomerativeClustering(n_clusters=2, linkage='ward')
y_hierarchical = hierarchical.fit_predict(X_scaled)
cluster_results['Hierarchical'] = y_hierarchical
print(f"✅ 层次聚类:{len(set(y_hierarchical))}个簇,样本分布:{np.bincount(y_hierarchical)}")

# 7. 聚类结果评估(内部+外部指标)
# 自定义纯度计算函数(替代sklearn缺失的purity_score)
def calculate_purity(y_true, y_cluster):
    contingency = pd.crosstab(y_true, y_cluster)  # 混淆矩阵
    return np.sum(np.max(contingency, axis=0)) / len(y_true)  # 纯度公式

# 批量评估所有算法
def evaluate_algos(X, cluster_dict, y_true):
    eval_df = pd.DataFrame(columns=['轮廓系数', 'DB指数', 'ARI', '纯度'])
    for name, y_cluster in cluster_dict.items():
        # 跳过无效簇(仅1个簇或全是噪声)
        if len(set(y_cluster)) <= 1:
            eval_df.loc[name] = [-1, float('inf'), adjusted_rand_score(y_true, y_cluster), calculate_purity(y_true, y_cluster)]
        else:
            eval_df.loc[name] = [
                round(silhouette_score(X, y_cluster), 3),
                round(davies_bouldin_score(X, y_cluster), 3),
                round(adjusted_rand_score(y_true, y_cluster), 3),
                round(calculate_purity(y_true, y_cluster), 3)
            ]
    return eval_df

# 打印评估结果
print("\n=== 3种算法评估结果对比 ===")
eval_df = evaluate_algos(X_scaled, cluster_results, y_true)
print(eval_df)

# 选择最优算法(按轮廓系数最高、DB指数最低)
valid_algos = eval_df[eval_df['轮廓系数'] != -1]
best_algo = valid_algos['轮廓系数'].idxmax() if not valid_algos.empty else 'KMeans'
best_y = cluster_results[best_algo]
print(f"\n✅ 最优算法:{best_algo}")

# 8. 结果可视化(核心修复:动态颜色生成,解决索引错误)
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei']  # Mac中文适配
plt.rcParams['axes.unicode_minus'] = False  # 负号显示修复

# 创建2x2子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('心脏病数据集聚类结果对比(PCA降维)', fontsize=14, y=0.98)

# 子图1:真实标签分布
axes[0, 0].scatter(X_pca[y_true==0, 0], X_pca[y_true==0, 1], c='lightblue', label='无心脏病(0)', s=50, alpha=0.7)
axes[0, 0].scatter(X_pca[y_true==1, 0], X_pca[y_true==1, 1], c='lightcoral', label='有心脏病(1)', s=50, alpha=0.7)
axes[0, 0].set_title('真实标签分布', fontsize=12)
axes[0, 0].set_xlabel('PCA维度1')
axes[0, 0].set_ylabel('PCA维度2')
axes[0, 0].legend()

# 子图2:最优算法结果(KMeans)
axes[0, 1].scatter(X_pca[best_y==0, 0], X_pca[best_y==0, 1], c='orange', label='簇0(低风险)', s=50, alpha=0.7)
axes[0, 1].scatter(X_pca[best_y==1, 0], X_pca[best_y==1, 1], c='green', label='簇1(高风险)', s=50, alpha=0.7)
axes[0, 1].set_title(f'{best_algo}结果(最优)', fontsize=12)
axes[0, 1].set_xlabel('PCA维度1')
axes[0, 1].set_ylabel('PCA维度2')
axes[0, 1].legend()

# 子图3:DBSCAN结果(核心修复:动态颜色)
if len(dbscan_clusters) > 0:
    # 用colormap动态生成颜色(适配任意数量的簇)
    cmap = plt.cm.get_cmap('tab10', len(dbscan_clusters))  # tab10是matplotlib内置颜色表,支持10种颜色
    for i, cluster_id in enumerate(dbscan_clusters):
        color = cmap(i)  # 动态获取第i个颜色
        axes[1, 0].scatter(
            X_pca[y_dbscan==cluster_id, 0], 
            X_pca[y_dbscan==cluster_id, 1], 
            c=[color],  # 注意:需用列表包裹,否则会报错
            label=f'簇{cluster_id}', 
            s=50, 
            alpha=0.7
        )
# 绘制噪声点(固定黑色×)
axes[1, 0].scatter(
    X_pca[y_dbscan==-1, 0], 
    X_pca[y_dbscan==-1, 1], 
    c='black', 
    label='噪声点', 
    s=30, 
    alpha=0.5, 
    marker='x'
)
axes[1, 0].set_title('DBSCAN结果', fontsize=12)
axes[1, 0].set_xlabel('PCA维度1')
axes[1, 0].set_ylabel('PCA维度2')
axes[1, 0].legend()

# 子图4:层次聚类结果
axes[1, 1].scatter(X_pca[y_hierarchical==0, 0], X_pca[y_hierarchical==0, 1], c='brown', label='簇0', s=50, alpha=0.7)
axes[1, 1].scatter(X_pca[y_hierarchical==1, 0], X_pca[y_hierarchical==1, 1], c='pink', label='簇1', s=50, alpha=0.7)
axes[1, 1].set_title('层次聚类结果', fontsize=12)
axes[1, 1].set_xlabel('PCA维度1')
axes[1, 1].set_ylabel('PCA维度2')
axes[1, 1].legend()

# 保存图表(Mac路径:项目文件夹中)
plt.tight_layout()
plt.savefig('heart_cluster_visualization.png', dpi=300, bbox_inches='tight')
print("\n✅ 可视化图表已保存:heart_cluster_visualization.png")

# 9. 结果解读与数据保存
# 添加聚类标签到原始数据
df_with_cluster = df.copy()
df_with_cluster['KMeans_cluster'] = best_y  # 只保留最优算法的标签,避免混乱

# 分析簇的核心特征(心脏病关键指标)
key_features = ['age', 'trestbps', 'chol', 'thalach', 'exang', 'ca', 'target']
cluster_stats = df_with_cluster.groupby('KMeans_cluster')[key_features].mean().round(2)

print("\n=== 最优聚类(KMeans)结果解读 ===")
print("簇关键指标均值对比:")
print(cluster_stats)

# 判断风险等级(按真实患病比例)
cluster0_ratio = cluster_stats.loc[0, 'target']
cluster1_ratio = cluster_stats.loc[1, 'target']
cluster0_count = len(best_y[best_y==0])
cluster1_count = len(best_y[best_y==1])

if cluster0_ratio < cluster1_ratio:
    print(f"\n🔵 簇0({cluster0_count}人):低风险人群")
    print(f"   - 患病比例:{cluster0_ratio*100:.1f}% | 平均年龄:{cluster_stats.loc[0, 'age']}岁")
    print(f"   - 特点:血压{cluster_stats.loc[0, 'trestbps']}mmHg,胆固醇{cluster_stats.loc[0, 'chol']}mg/dl,运动心绞痛少")
    print(f"   - 建议:每半年体检1次,控制体重")
    
    print(f"\n🔴 簇1({cluster1_count}人):高风险人群")
    print(f"   - 患病比例:{cluster1_ratio*100:.1f}% | 平均年龄:{cluster_stats.loc[1, 'age']}岁")
    print(f"   - 特点:最大心率{cluster_stats.loc[1, 'thalach']}次/分,血管病变{cluster_stats.loc[1, 'ca']}条")
    print(f"   - 建议:立即做心脏专项检查,减少剧烈运动")
else:
    print(f"\n🔴 簇0({cluster0_count}人):高风险人群")
    print(f"\n🔵 簇1({cluster1_count}人):低风险人群")

# 保存带标签的数据(方便后续分析)
df_with_cluster.to_csv('heart_with_kmeans_cluster.csv', index=False, encoding='utf-8')
print(f"\n✅ 带聚类标签的数据已保存:heart_with_kmeans_cluster.csv")
print("\n🎉 心脏病数据集聚类项目完成!所有文件已保存到项目文件夹。")

这是心脏病数据集聚类结果的 PCA 降维可视化对比图,通过 2 维空间展示了不同聚类算法的效果,核心信息如下:

1. 各子图解读

  • 真实标签分布

    • 区分 “无心脏病(浅蓝色)” 和 “有心脏病(粉红色)” 两类样本;
    • 两类样本存在一定重叠,但整体有弱聚集趋势,说明数据本身具备可聚类性。
  • KMeans 结果(最优)

    • 分为 “簇 0(低风险,黄色)” 和 “簇 1(高风险,绿色)”;
    • 两类簇在 2 维空间中区分度较高,与真实标签的分布匹配度较好,体现了 KMeans 在该数据集上的有效性。
  • DBSCAN 结果

    • 仅识别出 1 个有效簇(紫色),其余样本均被判定为 “噪声点(黑色 ×)”;
    • 说明 DBSCAN 对该数据集的参数(ε、最小点数)敏感,无法有效区分样本簇,聚类效果较差。
  • 层次聚类结果

    • 分为 “簇 0(深红色)” 和 “簇 1(浅粉色)”;
    • 两类簇在 2 维空间中重叠较多,区分度远低于 KMeans,聚类边界模糊。

2. 整体结论

该图的核心作用是对比不同聚类算法的效果:

  • 最优算法:KMeans 的聚类结果与真实标签的匹配度最高,区分度最好;
  • 较差算法:DBSCAN 因参数敏感性导致大量样本被判定为噪声,层次聚类的区分度不足;
  • 用途:辅助选择适用于心脏病数据集的聚类算法,为后续风险分层、医疗分析提供依据。

浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值