Python数据挖掘实战:南京二手房K-means聚类代码包解析

📅 发布时间:2026/10/1 4:56:01
Python数据挖掘实战:南京二手房K-means聚类代码包解析
简介这份配套教学资源包面向正在学习或讲授Python数据挖掘的初学者与高校师生提供从数据预处理到建模评估的完整实操素材帮助把Numpy、Pandas、Scikit-learn、Matplotlib、Seaborn、Statsmodels等库的理论知识落到真实案例中。包内共约2000个文件以txt说明、py脚本、png图表、csv数据集为主另含少量html、js、xls与pptx等压缩包约119.72MB覆盖数据加载、探索、清洗、特征工程、模型选择与调优、结果可视化等环节。内容预览可见k-means聚类分组结果、南京二手房总价分布等图表说明案例涉及聚类与业务数据分析场景。已有224人学习下载适合希望按目录逐步复现端到端流程、积累特征处理与模型评估经验的读者参考。1. 从一份二手房聚类代码包说起它到底能跑出什么很多人第一次接触数据挖掘卡的不是算法公式而是数据从哪来、代码怎么串、结果长什么样。这份 Python 数据挖掘实践配套代码及数据.zip 就是冲着这个痛点来的——它把南京二手房数据、K-means 聚类脚本和可视化结果打包在一起解压后能看到原始代码.html、k-means聚类结果分组0到4的普通版与 dark 版页面以及南京二手房总价小于200万的分布图.html。换句话说它不是一份只讲理论的课件而是一套能直接打开、能对照结果反推代码逻辑的实操素材。它适合三类人刚学完 pandas 和 sklearn、想找一个完整案例练手的新手需要给学生演示聚类到底在分什么的讲师以及想快速验证自己环境配置是否正确的从业者。你不需要先啃完统计学习只要能把 Python 环境跑起来就能顺着这份资源把加载数据→清洗→聚类→出图这条链路走一遍。下面我按实际拆包和复现的顺序把这份资源怎么用、参数怎么调、哪里容易翻车讲清楚。2. 拆包先看结构HTML 结果页与代码的对应关系2.1 资源里到底有什么先建立文件地图解压之后目录里最显眼的是两类文件一类是原始代码.html另一类是k-means聚类结果分组0.html到分组4.html每个分组还配了一个- dark.html版本。再加上一张南京二手房总价小于200万的分布图.html。这里有个容易误解的点这些.html不是网页源码意义上的网站而是 Jupyter Notebook 导出后的静态快照——代码单元格、输出结果、图表都嵌在同一个文件里用浏览器直接打开就能看。文件作用打开方式原始代码.html完整的数据处理与聚类流程浏览器直接打开k-means聚类结果分组0~4.html每个簇的可视化结果浏览器直接打开分组N - dark.html深色主题版本图表配色不同浏览器直接打开南京二手房总价小于200万的分布图.html价格分布可视化浏览器直接打开我一般会先把原始代码.html用浏览器打开按 CtrlF 搜read_csv、KMeans、fit这几个关键词快速定位数据加载、建模、训练三段核心代码。这样做的原因是导出后的 HTML 里代码和输出是混排的直接从头读到尾效率很低先抓关键调用再回头看上下文能省一半时间。2.2 为什么是 K-means而不是别的聚类二手房数据做分群常见做法是 K-means原因很实际它的输出是每个样本属于哪个簇 每个簇的中心点而簇中心可以直接翻译成业务语言——比如低总价小户型高总价大户型中间价位改善型。这份资源把结果分成 0 到 4 共 5 个组说明聚类数 k 取的是 5。k 的选择不是拍脑袋通常用肘部法看 inertia 随 k 下降的拐点或轮廓系数来定资源里既然直接给了 5 组结果说明作者已经做过这步筛选。K-means 的另一个好处是对新手友好它只依赖特征之间的距离不需要标签也不需要复杂的概率假设。但它的边界也很明确——对异常值敏感、要求先做标准化、簇形状偏向球形。二手房数据里总价、面积、房龄的量纲差很多如果不做标准化直接聚类总价这个数值大的特征会主导距离计算结果基本等于按总价硬切。这一点在后面避坑章节会展开。2.3 把 HTML 结果还原成可运行的 NotebookHTML 只能看不能改想真正动手调参得把代码还原成.ipynb或.py。常见做法是在浏览器打开原始代码.html把每个代码单元格的内容复制出来粘贴进 Jupyter Notebook 或 VS Code 的.py文件。复制时注意别把输出结果里的Out[]:也带进去。还原后先别急着改逻辑原样跑一遍确认环境和数据路径没问题。# 还原后的最小可运行骨架先跑通再谈调参 import pandas as pd from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt # 读取二手房数据路径按自己解压位置改 df pd.read_csv(nanjing_ershoufang.csv) # 只保留参与聚类的数值特征具体列名以原始代码为准 features df[[total_price, area, room_num]].dropna() # 标准化这一步不能省否则量纲大的特征会吃掉距离 scaler StandardScaler() X scaler.fit_transform(features) # k5 对应资源里的分组0~4 kmeans KMeans(n_clusters5, random_state42, n_init10) df[cluster] kmeans.fit_predict(X) print(df[cluster].value_counts())这段代码的逻辑说明StandardScaler把每个特征变成均值 0、方差 1消除量纲影响n_clusters5对应资源里的 5 个分组random_state42固定随机种子保证每次跑出来的分组编号一致n_init10让算法用 10 组不同初始中心跑取最优结果避免陷入局部最优。参数怎么改如果想让分组更细把n_clusters调到 6 或 7但要重新看肘部图确认是否合理如果数据里异常值多可以在标准化前先用 IQR 或分位数把极端值剔掉。3. 数据预处理与特征选择聚类效果的地基3.1 二手房数据里哪些列能进模型不是所有列都能直接喂给 K-means。字符串列比如小区名、朝向描述必须先编码或直接排除ID 类列房源编号没有任何区分意义留着只会引入噪声。我一般会先跑一遍df.info()和df.describe()把数值列和类别列分开看。数值列里总价、面积、房龄、楼层这些是天然可用的类别列里朝向、装修情况可以用独热编码转成 0/1 特征但要注意别把维度撑得太大。# 先摸清数据底细再决定哪些列进模型 print(df.info()) print(df.describe()) # 数值特征直接选类别特征做独热编码 num_cols [total_price, area, age] cat_cols [orientation, decoration] df_encoded pd.get_dummies(df[cat_cols], drop_firstTrue) X_raw pd.concat([df[num_cols], df_encoded], axis1).dropna()get_dummies的drop_firstTrue是为了避免虚拟变量陷阱——如果朝向有 4 个取值生成 3 列就够了第 4 种情况由全 0表示。这一步不做特征之间会存在完全共线性虽然对 K-means 影响不如回归那么致命但会让距离计算偏向类别多的特征。3.2 缺失值和异常值怎么处理才不伤结果二手房数据里缺失值很常见房龄没填、楼层没写、面积是空。处理方式无非三种删行、填均值/中位数、用模型预测填充。我的习惯是缺失比例低于 5% 的列直接删掉缺失行高于 5% 的列用中位数填充因为中位数对异常值不敏感。异常值方面总价特别高或面积特别小的记录往往是录入错误或特殊房源留着会把簇中心拉偏。# 缺失值低比例删行高比例中位数填充 for col in X_raw.columns: missing_rate X_raw[col].isna().mean() if missing_rate 0.05: X_raw X_raw.dropna(subset[col]) else: X_raw[col] X_raw[col].fillna(X_raw[col].median()) # 异常值用 IQR 把总价和面积的极端值剔掉 for col in [total_price, area]: q1, q3 X_raw[col].quantile([0.25, 0.75]) iqr q3 - q1 lower, upper q1 - 1.5 * iqr, q3 1.5 * iqr X_raw X_raw[(X_raw[col] lower) (X_raw[col] upper)]IQR 的 1.5 倍是常规阈值如果数据本身分布很偏可以放宽到 3 倍避免把正常的高价房源误删。这一步做完样本量会减少但聚类结果的稳定性会明显提升。资源里的分组结果之所以看起来干净大概率也做过类似清洗只是 HTML 里不一定每一步都展示出来。3.3 标准化与特征权重别让总价一个人说了算标准化之后所有特征在距离计算里的权重是一样的。但业务上总价和面积的重要性可能不同。如果想让某个特征影响更大可以给它乘一个权重系数。常见做法是先跑一版纯标准化的结果看簇中心在业务上是否可解释如果发现某个特征完全被淹没再手动加权。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X_raw) # 如果想让总价权重更高乘一个系数再聚类 weights {total_price: 1.5, area: 1.0, age: 1.0} # 实际实现时按列位置乘对应权重权重的选择没有标准答案靠业务理解和反复试。我一般会保留一版不加权的基准结果再对比加权后的簇中心变化如果簇的划分明显更符合业务直觉就采用加权版。但要注意加权会让结果更难复现团队协作时最好把权重写进配置文件别硬编码在脚本里。4. 聚类结果解读与可视化从分组编号到业务含义4.1 簇中心怎么翻译成人话K-means 跑完kmeans.cluster_centers_给出的是标准化空间里的中心点直接看数值没意义要反标准化回原始量纲。反标准化后每个簇的中心就是这个群体的平均总价、平均面积、平均房龄。比如簇 0 中心是总价 150 万、面积 60 平、房龄 15 年那它对应的就是低总价老小户型簇 3 中心是总价 450 万、面积 120 平、房龄 5 年对应高总价新大户型。# 把标准化空间的中心点还原回原始量纲 centers scaler.inverse_transform(kmeans.cluster_centers_) centers_df pd.DataFrame(centers, columnsX_raw.columns) print(centers_df) # 每个簇的样本量看分组是否均衡 print(df[cluster].value_counts())如果某个簇只有几十个样本而其他簇有几千个说明这个簇可能是异常值聚集或者 k 选大了。资源里分成 5 组如果每组样本量差距不大说明聚类结果比较健康如果某一组特别小就要回头检查是不是异常值没清干净。4.2 分布图与分组图怎么配合看资源里有一张南京二手房总价小于200万的分布图.html这张图的作用是给你一个价格分布的整体印象。先看这张图知道大部分房源集中在哪个价格区间再看聚类分组图就能判断聚类是不是把不同价格段合理切开了。如果分布图显示 200 万以下房源占 70%而聚类结果里有 3 个簇的中心都在 200 万以下说明聚类在低价段做了更细的划分这是合理的如果 5 个簇里有 4 个中心都在 200 万以上那就要怀疑数据采样是否有偏。# 用散点图看两个特征下的簇分布 plt.figure(figsize(10, 6)) scatter plt.scatter(df[area], df[total_price], cdf[cluster], cmapviridis, alpha0.6) plt.colorbar(scatter, labelcluster) plt.xlabel(Area) plt.ylabel(Total Price) plt.title(Nanjing Second-hand Housing Clusters) plt.show()cdf[cluster]让颜色按簇编号区分cmapviridis是常用配色alpha0.6让重叠点也能看清。如果散点图里不同颜色的点混在一起、没有明显边界说明这两个特征对聚类的贡献不大或者 k 选得不合适。dark 版 HTML 里的配色不同但数据是一样的看哪个顺眼用哪个。4.3 分组编号的稳定性问题K-means 的簇编号是随机的这次跑出来簇 0 是低价组下次换个随机种子可能簇 0 就变成高价组。这不是 bug是算法特性。解决办法有两个固定random_state保证同一台机器上结果可复现或者在得到结果后按簇中心的某个特征比如总价重新排序编号让编号有业务含义。# 按总价中心排序重新映射簇编号让编号稳定 centers_df[cluster] centers_df.index centers_sorted centers_df.sort_values(total_price).reset_index(dropTrue) mapping {old: new for new, old in enumerate(centers_sorted[cluster])} df[cluster_stable] df[cluster].map(mapping)这样处理后簇 0 永远是最低总价组簇 4 永远是最高总价组团队里不同人跑出来的结果可以直接对比。这一步在资源里不一定有但实际工作中很实用建议加上。5. 避坑与排查这份资源跑不起来时先看这几条5.1 现象打开 HTML 只有文字没有图表原因HTML 里的图表是 base64 编码嵌进去的如果文件在传输过程中被截断或者用某些编辑器打开时没按 UTF-8 解码图表就显示不出来。解决换 Chrome 或 Edge 浏览器打开别用记事本或 Word如果还是不行重新解压一次确认文件大小和压缩包里的原始大小一致。5.2 现象还原代码后报ModuleNotFoundError: No module named sklearn原因环境里没装 scikit-learn或者装在了另一个 Python 解释器下。解决先python -c import sys; print(sys.executable)确认当前解释器路径再用pip install scikit-learn pandas matplotlib安装。如果用的是 VS Code检查右下角选的解释器是不是你装包的那个。5.3 现象聚类结果每次跑都不一样原因没设random_state或者n_init太小。解决KMeans(n_clusters5, random_state42, n_init10)n_init至少设 10数据量大时设 20 更稳。如果设了还是不稳定检查数据里有没有重复行或极端异常值这些会让初始中心的选择波动很大。5.4 现象所有样本被分到同一个簇原因特征没标准化某个数值特别大的列主导了距离或者 k 设成了 1。解决确认StandardScaler在fit_predict之前执行检查n_clusters是不是大于 1如果标准化后还是这样看下特征之间是不是高度相关考虑做 PCA 降维后再聚类。5.5 现象簇中心反标准化后数值离谱原因inverse_transform用的 scaler 和聚类时用的不是同一个或者特征列顺序对不上。解决把fit_transform和inverse_transform放在同一个 scaler 对象上并且保证X_raw的列顺序在标准化前后一致。列顺序错位是血泪经验里最常见的一种建议在标准化前先X_raw X_raw[cols]固定列序。6. 进阶技巧用轮廓系数和肘部法把 k 定下来资源里直接给了 5 组结果但实际项目中 k 往往要自己定。我一般会跑一个循环把 k 从 2 到 10 都试一遍同时看两个指标inertia簇内平方和和轮廓系数。inertia 随 k 增大一直下降要找下降变缓的拐点轮廓系数在 -1 到 1 之间越接近 1 说明簇内越紧凑、簇间越分离。两个指标结合看比拍脑袋定 k 靠谱得多。from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt inertias, silhouettes [], [] k_range range(2, 11) for k in k_range: km KMeans(n_clustersk, random_state42, n_init10) labels km.fit_predict(X_scaled) inertias.append(km.inertia_) silhouettes.append(silhouette_score(X_scaled, labels)) fig, ax1 plt.subplots(figsize(10, 5)) ax1.plot(k_range, inertias, bo-, labelInertia) ax1.set_xlabel(k) ax1.set_ylabel(Inertia, colorb) ax2 ax1.twinx() ax2.plot(k_range, silhouettes, ro-, labelSilhouette) ax2.set_ylabel(Silhouette, colorr) plt.show()这段代码的逻辑inertia_是 K-means 自带的簇内平方和直接取silhouette_score需要真实标签和特征矩阵计算量比 inertia 大k 范围大时可以先抽样再算。参数上k_range从 2 开始是因为 k1 没有聚类意义到 10 是因为二手房数据的分群通常不会超过 10 类再多就失去业务可解释性。跑完看两条曲线的拐点和峰值如果 inertia 在 k5 后明显变缓轮廓系数也在 k5 附近最高那 5 就是合理选择和资源里的分组数对上了。还有一个细节轮廓系数对异常值敏感如果数据没清干净k2 的轮廓系数可能虚高。所以我会先按第 3 章的流程把异常值处理掉再跑这个循环。另外如果两个 k 的轮廓系数很接近比如 k4 是 0.52、k5 是 0.53那就选小的那个因为簇越少越容易解释业务方也更容易接受。从那以后我每次拿到一份新的聚类资源都强制先跑一遍 k 扫描再对照资源里的分组数看是否吻合——吻合说明作者选参合理不吻合就说明数据版本或预处理步骤有差异得回头查。希望这份拆解能帮你把这份代码包真正跑起来而不是只停留在打开 HTML 看个热闹。本文还有配套的精品资源点击获取