资讯动态

kmodes实战:用Python搞定分类数据聚类

发布时间:2026/10/20 2:04:07 来源:尧图企业网站定制
简介kmodes开源库的完整Python实现提供k-Modes与k-Prototypes两种聚类算法专用于处理纯分类变量及数值/分类混合数据。面向数据分析、机器学习开发者解决传统k-means对非数值特征无法直接聚类的痛点。压缩包共30个文件包含20个Python源码、2个Markdown说明、2个CSV示例数据及测试、配置等文件整体仅36KB轻量易部署。源码中不仅封装了KModes类和fit_predict/fit接口还附带iris、soybean、stocks等多个示例脚本并内置单元测试目录便于读者理解算法细节并验证效果同时提供README、持续集成等元文件方便二次开发与学习。已有2008人学习下载适合希望掌握分类数据聚类原理并在实际项目中快速调用的中高级Python工程师无论是学术研究还是业务分群都能直接参考实现细节。1. 入门先搞懂一件事聚类分类数据到底难在哪提到聚类很多人的第一反应是 k-means——给一堆数字找中心、算距离、分堆。可一旦遇到只有“颜色”“产地”“口感”这种分类数据的数据集k-means 就彻底没了用武之地。kmodes 就是专门为这类场景设计的 Python 库里面实现了两个算法k 模式和 k 原型前者只处理分类数据后者能同时处理数值和分类混合的数据。对于刚接触聚类的你来说这个库最大的价值是不用自己写算法只要把数据整理好几行代码就能把样本分成有意义的几堆。这篇文章会带你搞懂它到底是什么、为什么值得学并跑通第一个能运行的例子。2. 先理解两个核心概念众数和汉明距离怎么撑起 k 模式2.1 k-means 的困局为什么“距离”和“均值”会失效k-means 做数值聚类的基础是“距离”。欧式距离需要两两坐标相减、平方、再开方所以它要求变量必须有大小。年龄 25 和 30 能比大小收入 8000 和 9000 能相减但“红”和“黄”谁大没法比“脆”和“软”差多少也没法算。于是 k-means 的整套逻辑放到分类数据上第一步就算不出距离。你可能会说那把颜色编码成 1、2、3 不就行了我第一次学的时候也这么想。问题在于一旦红1、黄2、绿3算法就会默认“黄”是“红”和“绿”的中间值仿佛颜色之间存在等差关系。这个“仿佛”是你人为造出来的数据本身没有这个性质。更麻烦的是换一种编码顺序比如红3、黄1、绿2聚类结果就会跟着变说明这个编码方式引入了假信息。分类数据的特征就是没有大小、没有差值、不能求平均。你不能说“三种颜色一平均得到黄”。所以处理分类数据需要另一套逻辑先定义什么是“相似”再定义什么是“中心”。这两件事正是 k-modes 的核心工作。2.2 k-modes 的思路用众数代替均值用汉明距离代替欧式距离k-modes 名字里的“modes”就是统计学里的“众数”——一组数据里出现次数最多的那个值。既然分类数据不能算平均那每个簇的“中心”不叫均值而叫“众数中心”。比如某个簇里有 5 个水果颜色分别是红、红、红、黄、绿那这个簇中心的颜色就是红因为红的票数最多。那么“距离”怎么算k-modes 用的是汉明距离比较两个对象在每一个属性上是否相同相同记 0不同记 1把所有属性上的结果相加。举个具体例子样本 A 是红北区脆样本 B 是红南区软两者颜色相同、产地不同、口感不同汉明距离就是 2。汉明距离天然适合分类数据因为它不需要比大小只需要判断“等不等于”。把这两件事合起来k-modes 的迭代过程就很清楚了先按一定规则挑 k 个初始中心然后把每个样本分到距离最近的中心那一簇再重新计算每一簇里的众数作为新的中心重复直到中心不再变化。整个过程和 k-means 很像只是把“求均值”换成了“求众数”把“欧式距离”换成了“汉明距离”。2.3 k-prototypes当数据里既有数字又有标签怎么办现实里的数据很少是纯数值或纯分类。用户表可能长这样年龄数值、城市分类、会员等级分类。这时候只用 k-means城市和等级的信息被丢掉只用 k-modes年龄会被拆成一堆离散的“年龄段”白白浪费它的大小关系。k-prototypes 就是来解决这个组合问题的。它的做法是“两条腿走路”数值列继续用 k-means 那套均值加欧式距离分类列用 k-modes 那套众数加汉明距离然后把两部分代价加起来。两类距离的量纲不同所以算法会给分类部分乘以一个权重 gamma让两边的数值大致可比。新手阶段你不用纠结这个权重怎么精调库会先给你一个自动估计值。一句话记住三者的关系k-means 管纯数值k-modes 管纯分类k-prototypes 管混合。数据里只要有分类列k-means 基本就不适用数据里只要有数值列k-modes 也要谨慎两边都有直接上 k-prototypes。2.4 新手可以先记住的结论什么时候用哪个算法数据类型推荐算法原因全是分类列颜色、城市、血型KModes汉明距离衡量相异众数计算中心全是数值列不推荐 kmodes考虑 k-meanskmodes 专为分类设计数值会被离散化数值列 分类列混合KPrototypes同时保留两类信息各自用合适的距离所以入门的时候先想清楚数据集里有没有数值列再决定 import 哪一行。这个判断比调参数更重要因为算法选错了后面再怎么调参都救不回来。3. 跑第一个例子用 k 模式给分类数据分堆3.1 安装 kmodes一条命令搞定先用终端执行这一条命令pip install kmodeskmodes 会自动带上 numpy、scipy、scikit-learn 这些依赖一般不需要你手动装。Windows 用户如果提示 pip 不是内部命令说明 Python 安装时没有勾选“Add Python to PATH”重新安装 Python 时勾上就行。装好后在 Python 里验证一下import kmodes print(kmodes.__version__)能打印出版本号就说明环境没问题。后面所有代码都建议写在同一个 Jupyter Notebook 或 .py 文件里方便边跑边看结果。3.2 构造一份能看懂的小数据第一个例子我故意做得小一点让每一步都能看懂。我们用 15 个水果样本每个样本有 3 个属性颜色、产地、口感。注意这三列全是字符串不是数字。import pandas as pd colors [红] * 5 [黄] * 5 [绿] * 5 origins [北区, 南区, 北区, 南区, 北区, 南区, 西区, 南区, 西区, 南区, 西区, 北区, 西区, 南区, 西区] tastes [脆, 脆, 软, 脆, 软, 软, 绵, 软, 绵, 绵, 脆, 绵, 脆, 绵, 绵] fruits pd.DataFrame({color: colors, origin: origins, taste: tastes}) print(fruits)这份数据里颜色是最干净的区分维度前 5 行全是红中间 5 行全是黄后面 5 行全是绿。产区和口感是“干扰项”有点乱。这样设计的目的是让你看到聚类结果时能直观判断算法是不是抓住了“颜色”这个主要规律。这里有个新手容易忽略的点分类列不要用数字编码。如果产地写成 1、2、3聚类中心输出“产地2”你根本不知道 2 代表哪里。字符串直接显示成“北区”“南区”结果一眼可读。3.3 训练并解读聚类结果现在用 KModes 把 15 个水果分成 3 簇from kmodes.kmodes import KModes km KModes(n_clusters3, initCao, random_state42) labels km.fit_predict(fruits) fruits[cluster] labels print(fruits.sort_values(cluster))n_clusters3表示分成 3 簇initCao是一种初始化方式比随机初始化更稳定新手可以直接照抄random_state42是随机种子固定它才能让结果可复现。fit_predict做两件事训练模型同时返回每个样本分到的簇标签。运行后你会看到15 个样本基本按颜色分成三组红色水果在一簇黄色在一簇绿色在一簇。虽然产区和口感有噪声但算法还是优先用了“颜色”这个区分度最高的属性这正是聚类的意义把相似的样本聚到一起。接下来看每个簇的“中心”长什么样print(km.cluster_centroids_)cluster_centroids_返回一个 DataFrame每一行代表一个簇的中心。中心里的值不是平均值而是这个簇里出现次数最多的属性也就是众数。比如某个簇中心是红北区脆意思就是这簇里最典型的样本是红、来自北区、口感脆。还可以看一眼km.cost_它表示所有样本到各自簇中心的总距离。距离越小说明簇内越一致。后面选 k 值时这个数值会派上大用场。3.4 如果你把参数换一换会发生什么把n_clusters改成 2结果会明显变粗红色和某个颜色可能会被合并成一簇因为簇数不够了。把init改成random再跑几次你会发现结果可能轻微变化因为初始中心是随机选的不同起点可能落到不同的“局部最优”。第一次学的时候你不需要理解这些参数背后的数学细节只需要记住一个动作复现结果时一定固定random_state。我在初学阶段就吃过这个亏同一份代码跑两次标签顺序完全不同还以为是自己写错了。还有一个参数叫n_init表示重复运行多少次、最后取代价最小的结果。默认是 1想更稳可以设成 5。数据量小的时候几乎不增加耗时但对结果稳定性帮助明显。4. 升级到 k 原型混合数据聚类怎么跑4.1 为什么需要 k-prototypes纯分类数据的场景其实不多更常见的是“一半数字、一半标签”。比如你做用户分层手里可能有年龄、月消费金额这样的数值列也有城市、会员等级这样的分类列。如果你只用 k-means分类列的信息会被丢掉只用 k-modes年龄这种连续数值会被拆成一个个离散值等于放弃了“年龄越大消费越高”这种大小关系。k-prototypes 就是为这个场景设计的。它内部像是一个“双引擎”模型数值列走 k-means 的逻辑分类列走 k-modes 的逻辑最后把两部分代价合并成一个总代价再迭代优化。你只需要告诉它“哪些列是分类列”剩下的交给库去处理。4.2 完整例子年龄、城市和会员等级一起聚类我构造一份 10 行的客户数据包含 1 个数值列年龄和 2 个分类列城市、会员等级import pandas as pd from kmodes.kprototypes import KPrototypes customers pd.DataFrame({ age: [23, 45, 31, 52, 28, 47, 36, 41, 38, 29], city: [北区, 南区, 北区, 西区, 南区, 西区, 北区, 南区, 西区, 北区], member_level: [普通, 黄金, 普通, 钻石, 黄金, 普通, 钻石, 普通, 黄金, 普通], }) kp KPrototypes(n_clusters2, initCao, random_state42) labels kp.fit_predict(customers, categorical[1, 2]) customers[cluster] labels print(customers.sort_values(cluster))注意第 3 行代码categorical[1, 2]表示“第 1 列和第 2 列是分类列”。这里的索引从 0 开始数第 0 列是 age第 1 列是 city第 2 列是 member_level。所以categorical[1, 2]就告诉算法city 和 member_level 走 k-modes 的逻辑age 走 k-means 的逻辑。运行后你会看到客户大致分成两组一组偏年轻、会员等级以普通为主另一组偏年长、会员等级以黄金和钻石为主。这就是混合数据聚类最常见的产出——把客户分成几个有业务含义的群体。再看一眼簇中心注意它的数据类型是混合的print(kp.cluster_centroids_)数值列的位置显示的是这一簇的均值比如“平均年龄 30.8”分类列的位置显示的是这一簇的众数比如“最常见的城市是北区”。一个对象里同时出现均值与众数这正是 k-prototypes 的特点。4.3 核心参数一览新手常用这几个就够参数所属类作用新手建议n_clustersKModes / KPrototypes最终分成几簇先用肘部法则试探initKModes / KPrototypes初始化方式Cao、Huang、random先用 Caon_initKModes / KPrototypes随机初始化运行次数取代价最小的结果默认 1对比效果时设 5random_stateKModes / KPrototypes随机种子固定一个整数如 42categoricalKPrototypes 的 fit/fit_predict指定哪些列是分类列传列索引列表gammaKPrototypes分类部分的权重新手先不传用自动估计注意categorical是传给fit_predict或fit的不是在KPrototypes()构造函数里传。这个细节我见过不少人踩坑如果你传错位置算法会把所有列都当数值列处理聚类结果会变得很奇怪。gamma这个参数你可能在文档里看到过它控制分类部分在总代价里的权重。默认会自动估计新手阶段不用动它。等你理解了每个参数的意义再回头调整 gamma 也不迟。5. 新手避坑指南五个最容易翻车的雷区5.1 分类列没有转成字符串聚类中心变得没法看现象聚类结果能跑出来但打印簇中心时看到的是“城市12”“等级3”这种数字完全不知道代表什么。原因分类列用了数字编码算法只认数字不会自动帮你还原成标签。解决在建模前统一把分类列转成字符串用astype(str)处理customers[city] customers[city].astype(str) customers[member_level] customers[member_level].astype(str)这样聚类中心直接输出“北区”“钻石”人一眼就能看懂。这个习惯我从第一次跑 kmodes 后一直保持到现在。5.2 用 KPrototypes 时忘了传 categorical 索引现象KPrototypes 跑出来所有样本几乎都被分到同一个簇其他簇只有零星几个样本。原因你没有传categorical参数算法把城市、会员等级这些分类列当成了数值列用欧式距离去算“北区和西区的距离”整个距离计算失去了意义。解决调fit_predict时明确传入分类列的索引列表labels kp.fit_predict(customers, categorical[1, 2])如果数据里有列名想确认索引对应关系先打印customers.columns数一遍位置。这是 KPrototypes 最高频的翻车点我见过太多人在这一步卡住。5.3 没固定 random_state结果每次都不一样现象同一份代码连续跑两次得到两组完全不同的标签顺序。原因聚类算法有随机初始化步骤初始中心不同最终结果就可能不同。解决在创建模型时固定随机种子km KModes(n_clusters3, initCao, random_state42)如果你要对比不同 k 值的效果务必保证只有 k 在变其他条件都相同。否则你很难判断结果差异到底是 k 引起的还是随机性引起的。5.4 先把分类数据做独热编码再交给 k-means现象分类列被拆成几十个 0/1 列维度瞬间爆炸k-means 跑出来的结果看不出任何业务含义。原因独热编码只是把分类标签变成稀疏的 0/1 向量并没有真正解决“分类数据没有距离”的问题而且类别越多维度越高距离计算被稀释得越厉害。解决分类数据聚类直接用 k-modes 或 k-prototypes不要绕路做独热编码再喂给 k-means。除非你用的是那种只能吃数值矩阵的算法才考虑编码但那是另一个话题。5.5 不知道 k 值取多少用代价函数画拐点现象数据没有真实标签不知道分成几类合适。原因聚类本身就是无监督问题没有标准答案。解决用肘部法则画出不同 k 值对应的代价曲线找“拐点”。代价存模型的cost_属性里import matplotlib.pyplot as plt costs [] for k in range(2, 7): km KModes(n_clustersk, initCao, random_state42) km.fit_predict(fruits) costs.append(km.cost_) plt.plot(range(2, 7), costs, markero) plt.xlabel(k) plt.ylabel(cost) plt.show()曲线由陡变缓的位置就是相对合理的 k 值。注意 k 越大代价越小是必然的所以不要找最小值要找“拐点”。画图需要 matplotlib没装的话先执行pip install matplotlib。6. 下一步学什么三条从 kmodes 出发的进阶路径第一个例子跑通之后你其实已经掌握了 kmodes 最核心的用法准备数据、创建模型、传入分类列索引、读取标签和簇中心。接下来可以从三条路径里挑一条继续走。进阶方向建议做的事完成后你能得到什么吃透参数组合对同一份数据依次改变 init、n_init、gamma记录每次的 cost_知道不同参数如何影响结果不再靠猜接入 scikit-learn 生态把 kmodes 放进 sklearn 的 Pipeline用聚类评估指标量化结果能对外说明“这个聚类效果到底好不好”拿真实数据练手找一份问卷数据或订单数据做完整的分类数据聚类流程形成自己的聚类分析基线遇到新数据不慌我的建议是优先走第三条把前面第 4 章的客户数据换成本地一份稍微大一点的数据集比如几百行的销售记录属性里既有金额也有商品类别直接跑一遍 KPrototypes再看每个簇的画像。我第一次跑 kmodes 时就卡在categorical参数上明明数据没问题结果却完全不对。后来我把每次建模前要做的事固定成一套动作确认哪些列是分类列、统一转成字符串、固定random_state、最后打印cost_。从那以后每次跑分类数据聚类我都强制走一遍这个流程翻车率直线下降。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑