资讯动态

K-Modes聚类入门:分类数据聚类的原理、实战与避坑指南

发布时间:2026/10/22 6:57:38 来源:尧图企业网站定制
简介kmodes 是一个针对分类数据聚类的 Python 库实现了 k-Modes 和 k-Prototypes 两种算法。它主要面向需要处理非数值型或混合型数据的开发者、数据分析师以及 scikit-learn 用户弥补传统 k-means 难以直接处理分类变量的不足。库内部使用汉明距离衡量分类属性差异并在 k-prototypes 中结合欧氏距离处理数值属性适用于市场细分、客户分群、文本聚类、生物信息学等场景。压缩包共 30 个文件以 20 个 Python 源码文件为主体涵盖 kmodes 与 kprototypes 核心实现、测试用例和性能基准示例另有 README、配置文件及示例数据集便于快速理解和使用。资源包整体仅 36KB结构清晰适合直接阅读源码学习聚类细节。目前已有 2008 人学习下载。通过此包可以掌握算法完整实现参考测试与持续集成配置深入理解参数初始化、距离计算及聚类中心更新等关键环节进而迁移到自己的项目中应用。1. 先搞懂一件事聚类是什么为什么一到分类数据就卡壳当你第一次听到“聚类”这个词可能和我当年一样以为把一堆数据自动分成几组是很简单的事。一上手就翻车想给用户分群手头数据却是“学历本科/硕士”“地区北京/上海”“购买渠道线上/线下”这类满屏文字最常见的k-means直接罢工——它要算点与点之间的距离可“本科”和“硕士”之间的距离该怎么量“红色”和“蓝色”求个平均能得到“紫色”吗k-modes就是专治这个问题的聚类算法。标题里的k模式处理“全是分类数据”的情况k原型处理“分类数值混合数据”的情况。这篇入门文会从零带你跑通第一个例子读完你就能拿自己的数据试一把。2. 从k-means到k-modes换一种“距离”一切就通了2.1 先吐槽k-means为什么分类数据会让它崩溃k-means的思路很直白把每个样本看成坐标系里的一个点。比如身高165厘米、体重55公斤就是一个坐标为(165, 55)的点。聚类时算法不断计算“样本点到中心点的距离”把点分给最近的簇。这里用的欧氏距离天然要求每个维度上的值可以加减乘除是有“数量含义”的连续数值。但分类数据没有这种含义。以“城市”为例北京、上海、广州之间的“距离”是多少你可能说地理上有距离但那不是“类别”含义上的距离。再看“颜色偏好红、蓝、绿”你能说红和蓝相差1、蓝和绿相差2吗这些数没法比较。更麻烦的是就算你把文字硬编码成0、1、2再丢给k-means这个问题也并没有消失——编码成1和编码成2之间的人为差距会被当成真实的几何距离参与计算聚类结果自然随缘。很多新手卡在这一步还以为是自己的数据不够“干净”。我当年也一样试过把分类变量直接转成数字再套k-means出来的分组怎么看都不对劲。后来才明白不是算法不行是算法和你手里数据的类型根本不匹配。分类数据需要的是“是否相同”的比较而不是“相差多少”的比较。2.2 k-modes的核心把“距离”换成“不匹配次数”k-modes的想法非常朴素朴素到像作弊它完全不计算数值距离。两个样本之间的距离等于它们在所有属性上“不一致”的个数。举个例子。样本1颜色偏好红所在地区城市购买渠道线上样本2颜色偏好红所在地区城市购买渠道线下对比一下前两个属性一致第三个不一致那么这两个样本之间的距离就是1。如果三个属性全部不同距离就是3。这种距离叫“简单匹配距离”本质上是数一数两组属性有多少个地方对不上。它天然适合分类数据因为只判断“相等”还是“不相等”不需要比较谁大谁小。那簇中心怎么定义k-means的簇中心是“均值”取所有样本坐标的平均值。k-modes没有“平均值”它取的是“模式”也就是每个属性上出现次数最多的那个值。比如一个簇里有5个人其中4个人的地区都是“城市”1个人是“乡镇”那这个簇在“地区”这个属性上的模式就是“城市”。这就是为什么算法叫k-modesmode的英文意思就是“众数、出现最多的值”。算法的迭代流程和k-means很像循环做两件事第一步把每个样本分给“模式距离最近”的簇第二步重新计算每个簇的模式。重复到模式不再变化或者达到最大迭代次数就收敛了。它要优化的目标也被换成“代价”cost所有样本到各自簇中心模式的不匹配次数总和。代价越小说明分组越“整齐”。2.3 k-prototypes一半数值一半分类怎么办现实中的数据很少是“纯文本”或者“纯数字”更常见的是混合体。举个例子一个用户画像表里有“年龄”数值、有“职业类型”分类、有“收入等级”分类。这种情况k-modes处理不了年龄因为它不想把年龄离散化k-means处理不了职业因为职业没法算距离。k-prototypes就是为这种场景设计的它把两种距离放在一起算数值部分用欧氏距离分类部分用不匹配次数然后把两部分加权求和。总距离 数值距离 gamma × 分类不匹配数这里的gamma是一个权重参数。gamma越大分类属性对聚类结果的影响力就越大gamma越小数值属性的主导作用越强。实际使用中如果你发现分类属性不重要可以调小gamma如果分类属性是核心业务字段就调大。这个值没有万能默认需要你用业务经验和多次实验去感受。动手时记住一条数值部分的列最好先做标准化处理。因为欧氏距离对量级敏感如果“年龄”是0到100“月薪”是5000到30000月薪会直接压倒年龄。用标准化把数值列缩放到相近范围k-prototypes的结果才合理。3. 跑通第一个例子用kmodes给你的分类数据分个组3.1 安装一行命令搞定打开终端或命令行执行pip install kmodes这条命令会安装kmodes库和它依赖的几个底层包。装完后建议立刻用一行命令确认安装成功python -c from kmodes.kmodes import KModes; print(ok)如果输出ok说明安装没问题。这里有个新手容易踩的坑如果你电脑上有多个Python环境直接敲pip可能装到别处去。入门阶段我建议用下面这条更稳的命令python -m pip install kmodes这样pip会明确装到当前python所在的环境里避免装完了却import不到。3.2 第一个例子给一份小型“用户画像”数据聚类我们构造一份非常小的数据只有10条记录、3个分类属性颜色偏好、所在地区、购买渠道。这份数据小到你可以手算验证结果跑通了再换自己的数据。from kmodes.kmodes import KModes # 构造10条样本每条有3个分类属性 data [ [红, 城市, 线上], [蓝, 乡镇, 线下], [绿, 城市, 线上], [红, 城市, 线上], [蓝, 乡镇, 线下], [绿, 乡镇, 线上], [红, 城市, 线下], [蓝, 城市, 线上], [绿, 乡镇, 线下], [红, 乡镇, 线上], ] # 创建K-Modes聚类器分成2个簇 km KModes(n_clusters2, initCao, n_init5, random_state0) # 训练并直接得到每个样本的簇标签 clusters km.fit_predict(data) print(每个样本的簇标签:, clusters)运行后你会看到类似这样的输出每个样本的簇标签: [0 0 1 0 0 1 0 1 1 0]我来解释这段代码在做什么。KModes(n_clusters2)表示我们希望算法把这10个人分成2组initCao是初始化方法比完全随机初始化更稳定也是新手最省心的选择n_init5的意思是算法会从5个不同的初始点开始跑最后选代价最小的那一次结果用来避免运气不好陷入很差的解random_state0则是固定随机种子让你这次跑和下次跑的结果完全一致。fit_predict是一个很常用的方法它一次完成两件事先训练模型再返回每个样本的簇标签。返回结果里标签0和1只是编号没有大小含义。比如上面结果里第一行数据的标签是0意思是样本0被分到了第0个簇。3.3 看结果簇中心长什么样拿到标签只是第一步更关键的是看看每个簇的“画像”是什么样的。kmodes把每个簇的模式存在cluster_centroids_属性里你可以在上面的代码后面追加两行print(每个簇的模式中心:, km.cluster_centroids_) print(聚类代价不匹配总数:, km.cost_)输出大概是这样每个簇的模式中心: [[红, 城市, 线上], [蓝, 乡镇, 线下]] 聚类代价不匹配总数: 5这个输出非常直观第0个簇最典型的人是“红、城市、线上”第1个簇最典型的人是“蓝、乡镇、线下”。你可以把这个描述直接写进业务报告里比如“第一组用户偏好红色、来自城市、习惯线上购买”。这就是聚类落地的基本方式。代价cost_是5意思是全部10条样本和各自簇中心模式加起来的“不匹配次数”总共有5次。这个数字单独看没有绝对意义但它非常有用可以用来比较不同簇数的好坏。那么n_clusters到底怎么选新手最常用的办法是看代价曲线从2跑到6画出k值和cost_的对应关系找一个“下降到拐点不再明显变快”的位置。代码可以这样写for k in range(2, 7): km KModes(n_clustersk, initCao, n_init5, random_state0) km.fit_predict(data) print(k, km.cost_)cost_会随着k增大而下降因为簇越多每个簇内部越“整齐”。簇数从2涨到3时如果cost_下降非常猛从3到4时下降很少那3可能就是合理的簇数。这外号叫“肘部法”因为它看的是曲线形状。我把K-Modes最常用的几个参数整理成了一个表方便你对照调参参数作用入门建议n_clusters你想分成几组从2开始试用肘部法选init初始化方式Cao/Huang/random优先用Cao最稳定n_init从几个初始点跑取最优5到10都够用max_iter最大迭代轮数默认即可一般不用调random_state固定随机种子固定一个数方便复现4. 新手避坑五个最容易翻车的细节4.1 坑一拿着连续数值硬跑K-Modes现象把“年龄”“月薪”“产品价格”这种连续数值原封不动喂给KModes结果聚类结果非常不稳定换一次随机种子分组就变样。原因K-Modes只看属性“是否相等”。连续数值几乎每个样本都不一样不匹配次数会很高导致所有样本看起来都很“远”簇的结构无法形成。它本来就不擅长处理这类数据。解决连续数值要么先离散化比如把年龄切成“青年、中年、老年”要么改用K-Prototypes。K-Prototypes能同时处理数值列和分类列比硬编码分类列再丢给K-Modes靠谱得多。4.2 坑二数据里有缺失值直接报错现象运行fit_predict时报ValueError提示数据里有NaN或者直接返回一个包含NaN的糟糕结果。原因分类数据里的缺失值没法判断“是否相等”。NaN和NaN不应该算相等和任何值不匹配算法内部会卡住。解决训练之前先检查缺失值。入门阶段最简单的做法是删除缺失样本或者用该列出现次数最多的值去填充。先让自己跑通再去认真考虑填充策略。4.3 坑三n_clusters拍脑袋乱定现象随手写n_clusters5看完结果觉得“这也太散了”但又说不出哪里不对。原因簇数需要结合业务和数据分布来定拍脑袋定的簇数往往会切出一些语义不清的簇。解决先跑几个不同的k值打印cost_看趋势。如果业务上有明确分组需求比如分高、中、低三档就按业务来没有的话用肘部法找一个“性价比最高”的值。4.4 坑四不固定随机种子复现不了结果现象代码没变只是重新运行了一次聚类结果和上次完全不一样比如样本归属变了连簇的画像都换了。原因K-Modes的初始化带随机性初始化点不同最终结果可能不同这很常见尤其是在数据本身可分性不明显的时候。解决创建模型时固定random_state。我在前面代码里写random_state0就是为了让结果可复现。排查问题、写文档、跟同事讨论时这一步能帮你省掉大量“为什么我不一样”的困惑。4.5 坑五把cluster_centroids_当成了平均值现象打印出cluster_centroids_看到里面是“红”“城市”“线上”这种字符串以为算错了。原因K-Modes的簇中心不是平均值而是众数组合。它表示的是这个簇里最具代表性的属性值组合字符串本来就是正常的。解决读懂簇中心时别把它当作“中间值”要把它当作“这个簇里占比最高的特征组合”。你完全可以把它转成可读的画像描述。这也是k-modes相对k-means的优势聚类结果天然可以当作文本报告去解释不用再想办法解释抽象的数字中心点。5. 下一步从跑通到会用建议你这样补到这里你已经能跑通K-Modes也能用K-Prototypes处理混合数据了。下一步我建议你按这个顺序继续补每一步都紧接着上一步大约一个周末就能走完。第一补聚类结果的评估方法。K-Modes没有sklearn那种“轮廓系数”可以直接用但你可以用cost_曲线对比不同k值也可以跑多次看标签稳定性同一份数据不同随机种子下分组越稳定说明聚类越可靠。第二做一个真实的模拟项目X。找一份带混合字段的表格数据比如模拟的会员信息表里面同时有年龄、城市、消费等级、注册渠道等。先用K-Modes对纯分类部分聚类再用K-Prototypes对所有列聚类对比两种做法哪边分组更符合业务直觉。这一步会让你真正理解两种算法的适用边界。第三养成一个固定习惯。我自己的习惯是每次拿到数据先看列类型分类列和数值列分开放训练前打印一遍缺失值数量建模时固定随机种子跑完聚类立刻把cluster_centroids_转回原始列名的表格逐簇读一遍画像。结尾想分享一个当年的教训。我第一次用K-Modes时忘记固定随机种子连续跑三次得到三个不同结果还以为是数据有问题急得满屋子找原因。从那以后我每次跑聚类都会强制在模型前面写一行random_state0也养成了先看数据和类型、再选算法的习惯。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑