资讯动态

KMeans聚类实战:用Python实现宿舍自动分配与可视化

发布时间:2026/10/2 14:55:56 来源:尧图企业网站定制
简介面向高校宿舍分配场景的K均值聚类KMeansPython实现资料包适合学习无监督算法、完成数据挖掘课程设计或进行实操训练的读者。资源围绕K均值聚类原理、scikit-learn库应用及宿舍分组案例展开源码可直接运行并配有演示视频。压缩包共13个文件主要包含Python脚本、README使用说明、requirements依赖清单、两个CSV聚类结果数据、若干XML工程配置以及两段MP4演示视频整体大小10.71MB结构清晰便于按需查阅。当前已有820人学习下载。通过源码可掌握数据预处理缺失值填充、标准化、K均值训练预测与结果可视化完整流程并理解特征选择、K值确定肘部法则与轮廓系数及KMeans初始化等关键细节演示视频则展示程序运行与文档操作帮助快速复现和验证结果适合作为课程设计参考或自学项目。1. KMeans聚类与宿舍分配把“按学号随机分”改成“按生活习惯自动聚”每年迎新季宿舍分配都是高校信息办最头疼的活。按学号随机排看起来公平结果一个宿舍四个人三种作息矛盾从入住第一天就埋下了。KMeans聚类算法能把学生按年龄、作息、生活习惯这些特征先分成几类再让同一类的人住到一起——这就是这份源码包做的事。压缩包里除了主程序宿舍分配.py还有演示视频和聚类结果CSV适合三类人高校宿管系统开发者、做课程设计的学生、想搞懂KMeans怎么落到真实业务场景的Python从业者。下面从环境准备开始一步步把这份资源跑通。2. 源码包结构与环境部署先把这个Python项目跑起来再说拿到压缩包先别急着双击py文件。这个项目虽然入口只有一个宿舍分配.py但完整链路涉及数据预处理、聚类训练、结果落盘和可视化依赖的库不止一个。我习惯先把文件结构理清再按依赖顺序把环境装好最后跑一个最小样例验证整条链路是通的。2.1 压缩包里的文件各自干什么解压之后你会看到下面这些文件我把它们按“入口、输出、配置、演示”四类拆开看文件/目录类型实际作用使用前必读.txt说明文档第一个要打开的文件写明了运行前提和注意事项宿舍分配.py入口脚本整个分配流程的主程序从读数据到出结果都在这里README.md说明文档使用流程和项目结构的补充说明requirements.txt依赖清单pip安装依赖时用列出了numpy、pandas、scikit-learn等库最终聚类结果展示.csv输出文件每条学生的原始特征加聚类标签一行为一条记录聚类中心及占比展示.csv输出文件每个簇的中心坐标和人数占比用于观察分群逻辑程序运行演示.mp4演示视频主程序实际运行一遍的录屏文档演示.mp4演示视频使用流程的录屏讲解.idea配置目录PyCharm的工程配置不影响运行可忽略这个结构说明了一个设计思路程序入口和结果展示分离。你运行宿舍分配.py之后真正要交出去的是两个CSV而不是终端里那几行print输出。后面第5章我会专门讲这两个CSV怎么读。2.2 Python环境配置与依赖安装先检查你有没有Python环境。命令行里执行python --version如果输出3.8到3.11之间的版本基本可以直接用。如果没装去python官网下个3.10.x安装包安装时记得勾选“Add Python to PATH”这一步经常被漏掉。依赖装在一个虚拟环境里更保险避免和系统里其他项目打架# 创建虚拟环境.venv是习惯叫法 python -m venv .venv # Windows下激活 .venv\Scripts\activate # macOS/Linux下激活 source .venv/bin/activate # 查看依赖清单 cat requirements.txt # 一键安装全部依赖 pip install -r requirements.txt激活命令在不同系统里不一样Windows是Scripts目录下的activatemacOS和Linux是bin目录下的activate记混了会报“找不到命令”。requirements.txt里一般包含numpy、pandas、scikit-learn、matplotlib这几个核心库它们是配齐后面所有代码的底线。如果你用的是vscode写代码装完后在vscode里按CtrlShiftP选解释器指向.venv下的python避免全局和虚拟环境混用。2.3 学生数据表应该长什么样源码包里没有自带学生数据CSV需要你自己准备。这里有个常见误解以为把学号、姓名丢给KMeans就行。KMeans算的是数值距离学号和姓名没法参与距离计算必须把特征转换成数值型而且特征要选那些真正影响住宿冲突的维度。我一般会准备一张这样的表字段类型示例说明student_id字符串2024001主键不参与聚类gender文本男/女需编码为0/1宿舍分配必须考虑性别major文本计算机/机械按类别编码不同专业的课程密度不同sleep_time数值23.5平时就寝时间数值化到24小时制get_up_time数值7.0起床时间和sleep_time一起定义作息smoking0/10是否吸烟部分高校宿舍明确分烟区非烟区study_hours数值5.0每日自习时长可作生活习惯代理特征表里gender和major是文本不能直接喂给KMeans要先做编码sleep_time这类数值特征天然就是数值可以直接用。加载和预处理的代码常见做法是import pandas as pd from sklearn.preprocessing import StandardScaler # 读CSV时建议用utf-8-sigWindows下用utf-8经常读到乱码 df pd.read_csv(student_data.csv, encodingutf-8-sig) # 文本特征编码性别映射到0/1 df[gender_code] df[gender].map({男: 1, 女: 0}) # 专业转成类别编码这里用pandas自带的cat.codes最省事 df[major_code] df[major].astype(category).cat.codes # 缺失值处理宿舍场景里直接填众数别用均值 df[sleep_time] df[sleep_time].fillna(df[sleep_time].mode()[0]) df[get_up_time] df[get_up_time].fillna(df[get_up_time].mode()[0]) # 汇总出参与聚类的特征列 features [gender_code, major_code, sleep_time, get_up_time, smoking, study_hours] X_raw df[features]这段代码有两个地方值得展开说。第一gender那行用了.map而不是replace好处是遇到没有映射到的值会得到NaN能反过来检查脏数据。第二缺失值填众数而不是均值因为作息和起床时间是“哪个人群最多”导向的分布众数比均值更贴近宿舍分配的真实习惯分布。接下来必须做标准化这一步是聚类结果是否靠谱的分水岭scaler StandardScaler() X scaler.fit_transform(X_raw) # X现在是标准化的二维数组每一列均值约为0方差约为1 # 注意fit_transform返回的是numpy数组不再是DataFrame为什么要标准化KMeans用欧式距离衡量相似度如果sleep_time的取值范围是22到24study_hours取值范围是2到10后者对距离的贡献会被放大聚类结果基本被学习时长一个特征绑架。标准化之后所有特征在一个量级上聚类才能综合多个维度。到这里数据已经可以喂给KMeans了下一章解决最关键的问题K到底选几。3. K值到底选几肘部法则、轮廓系数与KMeans参数配合KMeans有个前提你得先告诉它“我要把学生分成几类”。在宿舍分配场景里K代表的不是宿舍楼栋数而是学生行为特征的群组数。分太少一个簇里还是作息差异很大的人分太多宿管操作起来不现实。所以K值选择不能拍脑袋我用两种方法配合来定。3.1 肘部法则看WCSS曲线找拐点肘部法则的原理很直观。KMeans的迭代可以概括成两步循环先按当前质心把每个样本分到最近的簇再重新计算每个簇的均值作为新质心重复直到中心点不再明显移动。它的优化目标是让每组内部的平方距离和WCSS也叫inertia尽量小。K从1逐步增大时WCSS单调下降但下降速度会从某个K开始明显变缓这个拐点就是“再增加分类数收益也有限”的位置形状像手肘所以叫肘部法则。from sklearn.cluster import KMeans import matplotlib.pyplot as plt # 这里用上一章处理好的Xnumpy数组 wcss [] for k in range(1, 11): kmeans KMeans(n_clustersk, initk-means, n_init10, random_state42) kmeans.fit(X) wcss.append(kmeans.inertia_) plt.plot(range(1, 11), wcss, markero) plt.xlabel(K) plt.ylabel(WCSS) plt.title(Elbow Method for Optimal K) plt.show()代码里四个参数值得逐个说。initk-means是默认推荐它让初始质心尽量分散避免一开始就掉进局部最优n_init10表示用10次不同的初始化跑最后取结果最好的那一次这个值在小数据集上可以调大到20random_state42是固定随机种子保证每次运行结果一致后面第4章会专门讲这个参数不固定会出什么幺蛾子inertia_就是当前K值下的WCSS。跑完之后看曲线这类学生数据的演示集里K4或K5处通常会出现明显拐点。如果你看到的曲线没有清晰手肘说明特征选择或预处理有问题回到第2章的标准化和缺失值处理检查一遍。3.2 轮廓系数给候选K值打分肘部法则看的是“类内距离”轮廓系数则同时考察“类内紧密度”和“类间分离度”。每个样本的轮廓系数取值在-1到1之间越接近1说明这个样本分到自己的簇越合理。from sklearn.metrics import silhouette_score # 一般从2开始算K1没有“分离”可言 for k in range(2, 8): kmeans KMeans(n_clustersk, initk-means, n_init10, random_state42) labels kmeans.fit_predict(X) score silhouette_score(X, labels) print(fK{k}, silhouette{score:.4f})轮廓系数的读法有讲究。取值大于0.3才算“弱分群”大于0.5是“中等分群”0.7以上很理想。实际宿舍数据能到0.5就算不错别拿学术标准硬套。把肘部法则里候选的K4、K5拿过来对比哪个轮廓系数更高就用哪个。如果两个值差不多倾向于取较小的K因为分群越少宿管执行成本越低。这里顺带说一句如果你在别的项目里对比过dbscan聚类算法会发现dbscan不需要指定K但宿舍分配场景有个硬约束——每个床位对应一个确定的人dbscan的噪声点和任意形状簇很难映射到固定床位数上所以KMeans反而是更贴合业务的选择。3.3 正式训练fit_predict与结果落盘选定K之后训练和预测可以一步完成# 假设肘部法则和轮廓系数共同指向K4 kmeans KMeans(n_clusters4, initk-means, n_init10, max_iter300, random_state42) # fit_predict等价于fit之后再predict一步拿到标签 labels kmeans.fit_predict(X) # 把标签写回原始表格 df[cluster] labels # 落盘成CSVindexFalse避免多出一列行号 df.to_csv(final_cluster_result.csv, indexFalse, encodingutf-8-sig)fit_predict和先fit再predict结果完全一样只是省一步。max_iter300是单次迭代上限默认300就够不用特意调大。落盘时建议用utf-8-sig编码否则在Excel里打开中文列名会乱码这是Windows环境下的老毛病。为了确认训练真的收敛了可以打印一次簇中心print(kmeans.cluster_centers_) # 输出形状是(4, 6)4个簇每个簇有6维中心坐标 # 但注意这里打印的是标准化后的坐标不是原始特征的量纲看到(4, 6)维的数组不用慌它是标准化空间里的簇中心。想把它还原成“第1类的人平均23点睡、7点起”这种人话第5章会给完整代码。现在你已经拿到了每个学生的cluster标签宿舍分配最核心的一步已经完成。不过先别急着交差下一章把最容易翻车的几个问题排查一遍再说。4. 避坑手册宿舍分配KMeans实战中的5个常见问题这一章不是理论补充是我实际调试和帮别人跑这个项目时反复遇到的状况。每一条都有现象、原因和解决办法照着排查比重新看一遍文档快得多。4.1 读CSV时中文列名全是乱码现象pd.read_csv加载学生数据后print(df.columns)显示一堆乱码字符或者直接报UnicodeDecodeError。原因Excel另存的CSV在Windows下默认是GBK编码而pandas默认用utf-8解析。解决读取时显式指定编码。稳妥做法是先用chardet探测编码再交给pandasimport chardet with open(student_data.csv, rb) as f: result chardet.detect(f.read()) df pd.read_csv(student_data.csv, encodingresult[encoding])如果安装的依赖里没有chardetpip install chardet装上即可。这里最省心的习惯是程序里所有to_csv和read_csv都固定带encodingutf-8-sig从源头堵住乱码问题。4.2 跑了两次聚类结果完全不同现象同样的学生数据、同样的K值第一次跑完cluster标签的分布是3:5:4:2第二次变成了5:3:2:4而且明显不是同一种分配方式。原因KMeans的初始质心是随机的没有固定random_state时每次运行起点都不一样收敛到的局部最优也未必相同。解决所有KMeans调用都固定传random_state42任意整数都行同时把n_init设为10以上。这样既稳定又能在多次初始化里挑一个更好的结果。这个坑在演示视频里其实已经埋了伏笔视频里每次运行结果一致靠的就是random_state固定。你自己不固定就复现不了这个效果。宿舍分配方案还要公示留档跑两次结果不一致根本无法向校方交代。4.3 聚类结果几乎全挤在一个簇里其他簇人数寥寥现象4个簇里有一个簇占了80%的人剩下三个簇加起来不到20%直观上看分配完全不合理。原因特征量纲不统一。某个特征的取值范围远大于其他特征比如study_hours取值0到10而sleep_time取值22到24距离计算基本被study_hours主导学生全按学习时长分堆了。解决在fit之前用StandardScaler做标准化并且切记fit_transform要作用在特征矩阵上不是在原始DataFrame上原地改。标准化后重新算一遍肘部法则簇的分布通常会回到接近均衡的状态。另外可以打印一下每个簇的中心看看如果某一个特征在簇间的差异特别大基本就是它主导了整个聚类这时候要考虑是不是该给这个特征降权。4.4 聚类中心是小数没法跟领导解释现象聚类中心展示.csv里每个值都是带四五个小数位的数字校方问“这个簇到底是群什么样的学生”答不上来。原因簇中心是标准化后的坐标不是原始特征的量纲。sleep_time被缩放到均值0附近后打印出来当然不是23.5这种可读的值。解决用scaler.inverse_transform把中心点还原回原始量纲再映射回列名。具体代码在第5章的5.2节这里先记住一个原则任何给别人看的聚类中心报表都要逆标准化。否则输出的小数完全不可读等于没做分析。4.5 跨学年的数据复用老模型直接失效现象今年用去年的模型给新生分宿舍结果新生的聚类标签分布和去年差异巨大宿舍安排一片混乱。原因特征分布漂移了。今年的新生晚睡比例、专业分布和去年不一样模型学到的簇中心还是去年的自然对不上。解决KMeans本来就不适合“训练一次用十年”每学年的新生数据都要重新跑一遍完整流程预处理、选K、训练、落盘。旧模型可以作为初值参考但不能直接复用。9月迎新前把新数据跑一遍和去年的结果对比一下看哪些簇的人数占比变化最大这也是一份有价值的分析报告。这五个问题里4.2和4.3是代码层面最常见的坑4.4是报表解读层面的坑4.5是业务落地层面的坑。把这三层都过一遍这份源码在真实宿舍场景里才算真正接得住。排查完这些下一章讲怎么把聚类结果转化成可执行的床位表。5. 结果解读把聚类标签和中心点翻译成宿舍床位表训练跑完CSV落盘事情才做了一半。聚类结果要变成真正可执行的宿舍分配方案还得过两道关一是能向非技术人员解释清楚“每个簇是什么样的人”二是有明确的规则把簇映射到具体的楼栋和床位。5.1 两个结果CSV的字段与业务含义源码包里自带“最终聚类结果展示.csv”和“聚类中心及占比展示.csv”它们各自承担不同的作用文件主要字段业务作用最终聚类结果展示.csvstudent_id、各原始特征、cluster逐条学生的分配依据宿管按cluster编号安排房间聚类中心及占比展示.csv各特征中心值、count、ratio每个簇的画像描述用于向校方解释分类逻辑如果你自己重新落盘建议保持这两个文件的命名习惯。前者的cluster字段就是KMeans返回的labels后者的ratio等于count除以总人数反映每个簇规模是否均衡。均衡性很值得关注如果某个簇占比超过一半说明K值偏小或特征区分度不足回到第3章重新选K。这一节的表格整理本质上就是python数据分析与可视化里的常规操作只是把结果从“看数字”变成“看分配规则”。5.2 逆标准化把簇中心还原成“23点睡、7点起”这一节直接给可复现的代码。假设你已经有了训练好的kmeans和scalerimport pandas as pd import numpy as np # 簇中心是标准化坐标形状为 (n_clusters, n_features) centers_std kmeans.cluster_centers_ # 逆标准化回到原始特征量纲 centers_original scaler.inverse_transform(centers_std) # 拼成可读的DataFrame列名要和训练时的features顺序一致 features [gender_code, major_code, sleep_time, get_up_time, smoking, study_hours] centers_df pd.DataFrame(centers_original, columnsfeatures) # 统计每个簇的人数和占比 counts np.bincount(labels) centers_df[count] counts centers_df[ratio] counts / len(labels) # 按占比降序排列最主流的簇排最前面 centers_df centers_df.sort_values(ratio, ascendingFalse) print(centers_df)这段代码有三个容易出错的地方。第一scaler.inverse_transform要求输入的形状和fit_transform时的X一致features顺序必须一字不差。第二gender_code还原出来是0到1之间的小数它代表的是该簇中女性占比——因为gender只编码成0和1簇中心的均值可以解读为“女性比例”。第三count用np.bincount(labels)最省事它直接统计每个标签出现的次数比groupby更底层更快。拿到这张表之后簇画像就一目了然了比如4号簇sleep_time平均是23.8、get_up_time平均是6.9说明这是一群晚睡早起的人1号簇smoking接近0适合安排到无烟楼层。把每个簇对应到一个宿舍楼或楼层床位按簇内学生学号顺序分配即可。5.3 用PCA降维画出肉眼可见的分群效果KMeans的输出是多维标签直接画图是画不出来的。常见做法是用PCA把特征压缩到二维再按cluster上色from sklearn.decomposition import PCA import matplotlib.pyplot as plt # 降维到2个主成分保留主要方差 pca PCA(n_components2) X_pca pca.fit_transform(X) # 用tab10色板区分簇色板支持到10类 plt.figure(figsize(8, 6)) scatter plt.scatter(X_pca[:, 0], X_pca[:, 1], clabels, cmaptab10, s20, alpha0.8) plt.colorbar(scatter) plt.xlabel(PC1) plt.ylabel(PC2) plt.title(KMeans Clustering on Student Features (PCA View)) plt.show()PCA降维是给“肉眼检查”用的不是给聚类算法用的。它会把原来6维的信息压缩到2维必然损失一部分区分度所以图上有少量点跨簇重叠是正常的不代表聚类失败。真正判断聚类质量还是看第3章的轮廓系数。这个散点图的最实际用途是汇报演示校方看懂了图才认可你的分群逻辑。到这里从数据准备、K值选择、模型训练、踩坑排查到结果解读一条完整的链路已经打通。但如果你想让这份源码在真实宿舍分配里再上一个台阶还有两个值得做的实验放在下一章。6. 进阶稳定性验证与特征加权让分配方案更可信KMeans随手跑通不难难的是让分配方案在真实场景里立得住。我最后补两个实战技巧都是在这类项目里验证过有效的。第一个技巧是在固定random_state之前先做一次稳定性实验。具体做法是固定K值跑20次不带random_state的聚类然后用轮廓系数观察标签的波动范围。如果20次里轮廓系数标准差在0.02以内说明数据分群本身很稳定可以放心固定random_state如果标准差超过0.05说明当前特征组合下聚类对初始质心太敏感这时候要回到特征选择而不是指望n_init救场。第二个技巧是特征加权。宿舍分配里作息冲突是矛盾第一来源专业背景的影响远没有作息大。标准化之后每个特征都是同一量纲想突出作息特征就在标准化后的矩阵上乘权重# X已经标准化权重按业务重要性手动设定 # sleep_time和get_up_time加权1.5smoking加权1.2其余保持1.0 weights np.array([1.0, 0.8, 1.5, 1.5, 1.2, 1.0]) X_weighted X * weights # 用加权后的矩阵重新训练和评估 kmeans_w KMeans(n_clusters4, initk-means, n_init10, random_state42) labels_w kmeans_w.fit_predict(X_weighted)注意权重不能直接乘在原始特征上必须在标准化之后乘否则又破坏量纲均衡。乘完之后重新算一遍轮廓系数和加权前比较——如果轮廓系数没有明显下降说明加权方向符合数据本身的结构如果下降很多说明业务假设和数据分布打架要回看特征定义。从那以后我每次拿到一批新的学生数据都强制走一遍固定random_state、结果落盘CSV、逆标准化输出报表的完整流程把调参过程留下的随机性降到最低。这个习惯帮我少踩了很多次“结果复现不了”的坑。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑