ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python房价可视化预测项目拆解:数据清洗到回归建模全流程

Python房价可视化预测项目拆解:数据清洗到回归建模全流程 简介这是一份基于Python的房价可视化预测项目完整资料包面向正在完成期末大作业、课程设计或毕业设计的计算机相关专业学生尤其适合希望快速搭建一个可演示、可讲解的完整项目的初学者。资源包含158个文件其中18个Python源码文件承担数据处理、特征分析与模型预测核心逻辑18个CSV数据集提供真实二手房样本15个HTML和11个JS文件构成前端可视化展示另有大量PNG图表、PPT讲解及Markdown文档说明压缩包整体约40MB结构清晰、便于部署。目前已有223人学习使用项目自带详细代码注释配合文档和PPT可轻松理解实现思路直接运行即可看到效果也可作为模板二次开发。对于需要提交高分课程设计或期末大作业的同学这套资源提供了从数据清洗到可视化预测的完整链路和呈现方案具有很高的参考价值。1. 房价可视化预测项目期末大作业凭什么拿满分做课程设计最怕的不是代码跑不起来而是答辩时答不上老师一句「为什么」。我最近完整拆了一遍这份 Python 房价可视化预测项目它是典型的「数据清洗 可视化分析 回归预测」三段式期末大作业原始 CSV 和清洗后 CSV 都配了双编码版本源码带注释文档说明和最终答辩 PPT 都在包里。整个链路从编码混乱的原始表格走到相关性热力图和房价预测结果每一步都有可演示的落点正好对应评分表上数据预处理、图表呈现、模型结果、答辩讲解四个模块。新手能按注释读懂每一段逻辑熟手可以拿它当骨架换一套数据就能迁到别的课题。期末大作业、Python 课程设计、数据分析综合实践都适合。2. 数据文件家族与预处理编码、清洗、抽样一次说清拿到资源包第一件事不是直接跑代码而是把六个 CSV 认清楚。这些文件不是随手导出的缓存每一份都对应开发和答辩里的一个环节命名里也藏着作者的工作流。2.1 六份 CSV 的定位与编码选择文件定位ershoufang-origin-utf8.csv原始数据UTF-8 编码Linux / macOS 下的默认读取版本ershoufang-origin-ansi.csv原始数据ANSI 编码Windows 下双击用 Excel 打开不乱码ershoufang.csv汇总后的总表ershoufang - 20000.csv抽样 2 万条调试代码、快速跑模型用ershoufang-clean-utf8-v1.1.csv清洗后 v1.1UTF-8 编码ershoufang-clean-ansi-v1.1.csv清洗后 v1.1ANSI 编码看命名就能还原作者的工作流原始数据进来做了一轮清洗输出成 v1.1 干净数据每个版本都同时导出 UTF-8 和 ANSI 两份。为什么这么干因为 pandas 在 Linux 和 macOS 上默认按 UTF-8 读文件而 Windows 的 Excel 存 CSV 默认走 ANSIGBK。作者显然被编码问题坑过才把双编码当成标配。这说明第一个该学的不是建模而是读文件时怎么处理编码。import pandas as pd # 优先按 UTF-8 读取报错则回退到 GB18030 try: df_raw pd.read_csv(ershoufang-origin-utf8.csv, encodingutf-8) except UnicodeDecodeError: df_raw pd.read_csv(ershoufang-origin-utf8.csv, encodinggb18030) print(df_raw.shape) # 行数和列数 print(df_raw.info()) # 字段类型与缺失统计 print(df_raw.head(10)) # 确认字段名和样例值这段代码的逻辑是「先试再退」优先用 UTF-8 读一旦抛 UnicodeDecodeError就换成 GB18030。GB18030 是 GBK 的超集能覆盖绝大多数中文场景比直接写 encodinggbk 更抗错。另一种常见情况是文件带 BOM表现是数据没问题但第一列列名带着一个看不见的 \ufeff 前缀用 encodingutf-8-sig 可以顺带解决。我一般会把三种编码写进一个小工具函数后面所有读取都复用省得每个脚本里都贴一遍 try-except。提示拿到任何陌生 CSV先不要急着做分析把 shape、info、head 三件套打出来看一遍再决定后面怎么走。2.2 自己动手走一遍清洗再和 v1.1 对比项目里已经有清洗好的 v1.1 文件但答辩时老师一定会问「清洗逻辑是什么、每一步删了多少行」。所以最稳的做法是自己从 origin 文件走一遍清洗再把结果和 v1.1 对比行数对上了逻辑也就讲得清了。import pandas as pd df pd.read_csv(ershoufang-origin-utf8.csv, encodingutf-8-sig) # 第一步统计缺失值别上来就 fillna missing df.isnull().sum() print(missing[missing 0]) # 第二步price 是预测目标空值直接删行 df df.dropna(subset[price]) df df[(df[price] 0) (df[total_price] 0)] # 第三步面积过滤到 5 ~ 500 平米清掉录入错误 df df[(df[area] 5) (df[area] 500)] # 第四步完全重复的行删除 df df.drop_duplicates() # 第五步文本字段的空值统一填「未知」 for col in [layout, direction, decoration]: df[col] df[col].fillna(未知) print(df.shape)每一步都有明确理由price 是回归目标它的空值不能填只能删否则等于造假数据总价和单价小于等于 0 是明显的录入异常面积小于 5 平或大于 500 平多半是单位写错或手误留着会拉偏可视化坐标轴也会干扰模型文本字段填「未知」是因为后面要做独热编码NaN 会被 get_dummies 直接跳过导致同一份数据不同行产生不同列数。整个清洗顺序也讲究先删缺失再过滤异常最后补文本空值这样补出来的「未知」不会被后面的异常过滤误伤。清洗完别急着直接建模先跟 v1.1 对一下行数。如果差得不多说明你的清洗逻辑跟作者一致如果差很多把每步打印出来的中间行数贴到文档里答辩时这就是你的「数据处理过程说明」比空口说做了清洗有说服力得多。2.3 双编码导出与抽样调试的技巧# 清洗结果保存两份utf-8-sig 给 pandas 跨平台读gbk 给 Windows Excel 直接开 df.to_csv(my_clean_utf8_v1.1.csv, indexFalse, encodingutf-8-sig) df.to_csv(my_clean_ansi_v1.1.csv, indexFalse, encodinggbk)indexFalse 是必须的不然读出来的数据凭空多一列 Unnamed: 0。至于为什么导两份前面说过你的队友或老师很可能用 Windows Excel给他一份 ANSI 版他双击就能看不用跟编码较劲。至于 ershoufang - 20000.csv 这种抽样文件是作者在数据量偏大时用来调代码的。sklearn 的随机森林在几万行数据上还好但如果跑特征组合搜索或者反复调参全量数据会拖慢迭代速度。常见做法是先用 2 万条抽样把代码和参数跑通确定方案后再上全量出最终结果。我也一直这么干——任何数据集第一轮调试都拿子集跑跑通了再回头看全量。3. 可视化分析图表怎么选才能撑起答辩可视化是这门大作业里出图最多、也最容易拿到印象分的部分。图不是画得越多越好而是每一张都要能回答一个问题。我按「单变量分布、多变量关系、区域对比」三层来拆。3.1 单变量分布直方图与箱线图成对出现只看一张直方图答辩时容易被追问「离群值怎么办」直方图加箱线图成对出现这个问题就自己回答掉了。import matplotlib.pyplot as plt import seaborn as sns # 中文字体兜底Windows 用 SimHeimacOS 用 PingFang SC plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False fig, axes plt.subplots(1, 2, figsize(14, 5)) # 左图直方图叠加密度曲线 sns.histplot(df[price], bins50, kdeTrue, axaxes[0]) axes[0].set_title(二手房单价分布直方图) axes[0].set_xlabel(单价元/平方米) axes[0].set_ylabel(频数) # 右图箱线图看离群点和四分位范围 sns.boxplot(xdf[price], axaxes[1]) axes[1].set_title(二手房单价箱线图) plt.tight_layout() plt.savefig(price_distribution.png, dpi150) plt.show()bins50 把单价切成 50 个区间区间太少看不出形态太多又锯齿感明显50 对万级样本是个稳妥起点。kdeTrue 叠加一条密度曲线评委一眼能看出右偏——大部分房源在低位集中少数豪宅把尾巴拖得很长这个「右偏」结论就是答辩的第一个知识点。箱线图则直接画出 Q1、Q3、中位数和离群点和直方图互为补充直方图擅长讲整体形态箱线图擅长讲离散和异常。plt.savefig 里的 dpi150 是给 PPT 用的截图分辨率太低放进 PPT 会糊150 到 200 是安全区间。3.2 相关性热力图一张图回答「哪些因素影响房价」# 数值型字段的相关性矩阵 num_cols [area, price, total_price, building_age, floor_level] corr df[num_cols].corr() plt.figure(figsize(8, 6)) sns.heatmap(corr, annotTrue, fmt.2f, cmapRdBu, center0) plt.title(数值特征相关性热力图) plt.show()annotTrue 把相关系数直接标在格子里fmt.2f 保留两位小数cmapRdBu 配 center0 是经典搭配正相关偏红、负相关偏蓝、颜色深浅表示强度一眼就能定位强相关对。看这张图重点抓三个结论面积和总价强正相关符合直觉房龄和单价负相关老房子单价偏低如果某个特征和 price 的相关性接近 1要警觉——它可能直接由单价计算而来比如总价就等于单价乘面积这种特征必须从预测模型里剔除否则就是数据泄漏这一点后面的模型章节还会再讲。3.3 区域维度分组聚合后画对比图region_summary df.groupby(region).agg( mean_price(price, mean), median_price(price, median), house_count(price, count) ).reset_index().sort_values(median_price, ascendingFalse) print(region_summary.head(10)) # 取中位数最高的 10 个区域画横向条形图 top10 region_summary.head(10) plt.figure(figsize(10, 6)) sns.barplot(datatop10, xmedian_price, yregion, ordertop10[region]) plt.xlabel(单价中位数元/平方米) plt.title(Top10 区域二手房单价中位数) plt.tight_layout() plt.show()这里我特意用中位数而不是均值排序房价数据右偏严重少数高价房源会把均值拉高中位数更能代表一个区域「普通房子卖多少钱」。横向条形图在区域名较长时比纵向图更好读ordertop10[region] 保证图上条形的排列顺序和聚合表一致不会按字母序打乱。分组聚合时把 count 也算出来还能顺便讲供需——某个区挂牌量特别大、价格又不高就是典型的刚需盘聚集区。答辩时被问「为什么用中位数」是高频问题这个选择本身就是答案。4. 房价预测从特征工程到两类回归模型的落地可视化负责讲故事预测模型负责拿数字说话。这一段是评分表里「模型结果」的核心但很多作业翻车就翻在特征工程没做干净模型指标好看但经不起追问。4.1 特征工程把文本字段翻译成模型能吃的数字sklearn 的回归模型只接受数值文本字段必须先转换。转换方式取决于字段性质有顺序关系的用映射没有顺序关系的用独热编码。df_model df.copy() # 户型形如「2室1厅」用正则拆出室数和厅数 df_model[rooms] df_model[layout].str.extract(r(\d)室).astype(float) df_model[halls] df_model[layout].str.extract(r(\d)厅).astype(float) # 朝向是类别字段没有顺序用独热编码 df_model pd.get_dummies(df_model, columns[direction], prefixdir) # 装修有天然等级毛坯 简装 精装 豪装用有序映射 deco_map {毛坯: 0, 简装: 1, 精装: 2, 豪装: 3} df_model[deco_level] df_model[decoration].map(deco_map) # 楼层也是有序的低 中 高映射后空值取中楼层兜底 floor_map {低楼层: 1, 中楼层: 2, 高楼层: 3} df_model[floor_level] df_model[floor_level].map(floor_map).fillna(2) # 删除原始文本列和总价列总价是单价乘面积泄漏源 df_model df_model.drop( columns[layout, decoration, title, region, total_price] )为什么朝向不能像装修那样映射成 0、1、2、3因为东、南、西、北之间不存在大小关系硬编码出 0 到 3 会让模型误以为「北 南」或者某种朝向更大就更贵这是类别特征最常见的坑。独热编码把每种朝向拆成一列 0/1 旗标模型就能独立学习每种朝向的影响。装修和楼层用有序映射没问题因为毛坯到豪装、低楼层到高楼层确实有等级递进。户型字段用正则 extract 拆出数字这一步比直接让模型读「2室1厅」字符串靠谱得多。4.2 划分训练集与测试集固定随机种子from sklearn.model_selection import train_test_split # price 是目标值total_price 已在特征工程阶段删除 feature_cols [c for c in df_model.columns if c ! price] X df_model[feature_cols] y df_model[price] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(X_train.shape, X_test.shape)test_size0.2 是回归任务里最常见的划分比例训练集 80%、测试集 20%样本量够大时这个比例稳定不会让测试集太小、评估结果抖动。random_state42 必须固定否则每次运行划分都不同模型指标一会儿一个值答辩时前后对不上会很尴尬。42 是个习惯值任何整数都行关键是固定下来。我在特征工程阶段就把 total_price 删了因为总价和单价存在精确换算关系保留它训练模型 R² 会接近 1看起来满分评委一句「你的特征里有哪个是目标值的线性变换」就能拆穿。4.3 线性回归与随机森林对比模型不怕简单怕没对比from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error models { 线性回归: LinearRegression(), 随机森林: RandomForestRegressor( n_estimators200, max_depth12, min_samples_leaf4, random_state42, n_jobs-1 ) } for name, model in models.items(): model.fit(X_train, y_train) y_pred model.predict(X_test) r2 r2_score(y_test, y_pred) mae mean_absolute_error(y_test, y_pred) rmse mean_squared_error(y_test, y_pred, squaredFalse) print(f{name}: R² {r2:.4f}, MAE {mae:.1f}, RMSE {rmse:.1f})同时跑两个模型是课程设计里性价比最高的动作线性回归作为基线结果稳定、好解释随机森林处理非线性关系更强通常指标更好。n_estimators200 是回归任务的安全默认值太少会欠拟合、太多训练变慢200 在这个数据规模上收益和耗时平衡得最好。max_depth12 限制树深度防过拟合min_samples_leaf4 保证叶子节点至少有 4 个样本让单棵树的预测不过度极端。n_jobs-1 让 sklearn 用满所有 CPU 核训练速度肉眼可见地提升。三个指标里R² 看整体解释力MAE 看平均偏差的绝对值RMSE 对离群点更敏感——如果 R² 不错但 RMSE 明显偏大说明大部分预测挺准、但个别极端房价猜错了这和前面可视化里看到的右偏分布正好对得上答辩时能串起来讲。4.4 预测散点图与特征重要性给评委两张「看得懂」的图plt.figure(figsize(8, 6)) plt.scatter(y_test, y_pred, alpha0.3, s10) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], r--, linewidth2) plt.xlabel(真实单价元/平方米) plt.ylabel(预测单价元/平方米) plt.title(随机森林预测值 vs 真实值) plt.show() # 特征重要性前 10 importance pd.Series(model.feature_importances_, indexfeature_cols) importance.sort_values(ascendingFalse).head(10).plot(kindbarh) plt.xlabel(重要性得分) plt.tight_layout() plt.show() # 把随机森林单独存成变量后续封装演示函数要用 final_model models[随机森林]散点图里点落在红色对角线上说明预测等于真实值点越集中在对角线附近模型越准。alpha0.3 处理点重叠s10 控制点大小不然几万个点叠在一起就是一团黑。特征重要性图则给出了一个可以讲的结论面积、室数、装修等级排在前列朝向的影响相对小。这符合常识评委更愿意听到「模型结论和业务常识互相印证」而不是你背一串公式。5. 避坑指南答辩前必须自查的五个翻车点这个项目我拆了两遍第一遍按自己习惯直接跑第二遍才对照资源包的文档过细节。以下五条都是真实会翻车的位置按「现象 → 原因 → 解决」写清楚。5.1 图表里全是方块现象plt.title、xlabel 里的中文全变成一个个空心方块图能出来但没法看。原因matplotlib 默认字体不含中文字形Linux 服务器上尤其常见Windows 有时也会因为缺 SimHei 翻车。解决在画图脚本开头统一设plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC]再设plt.rcParams[axes.unicode_minus] False处理负号显示。如果设了还没效果说明系统里没有这些字体检查一下系统字体或者退回用英文标签兜底别在答辩现场跟字体较劲。5.2 模型 R² 高得离谱现象随机森林 R² 直接 0.98 以上答辩时自己都心虚。原因特征集里混进了由目标值直接计算出的字段最常见的就是 total_price总价 单价 × 面积模型等于拿着答案考试。解决进入模型的特征列表逐个确认「这个字段不依赖 price 也能独立存在」把 total_price 这类衍生字段在建模前删掉。检查方法很简单用删掉后的特征集再训练一次如果 R² 明显回落说明之前就是泄漏。答辩时主动讲一句「我排除了总价字段因为它包含目标值信息」反而是加分项。5.3 UnicodeDecodeError 和带 \ufeff 的列名现象read_csv 直接抛 UnicodeDecodeError或者读进来了但第一列列名变成 \ufeffregion。原因文件是 ANSI/GBK 保存的却按 UTF-8 读后者是 UTF-8 带 BOM 的标志BOM 被 pandas 当成列名的一部分读进来了。解决用前面写的 try-except 回退链UTF-8 失败就换 GB18030凡是遇到 \ufeff把读法换成 utf-8-sig。把这两条写成一个 read_csv_safe() 工具函数整个项目所有读取都走它省得每个脚本里贴一遍。5.4 PPT 截图和现场运行结果对不上现象演示时重新运行代码数字和 PPT 里的截图不一样满屏尴尬。原因PPT 里的结果是早期调参版本代码后来又改过特征或随机种子没有同步更新截图。解决确定最终方案后把 random_state 固定住重新跑一遍全流程用这轮的图表和指标重新做 PPT 截图。我自己的习惯是 PPT 放在项目目录下代码每改一次就顺手 rerun 一遍截图宁可多花十分钟重截图不要在答辩台上现跑现编。5.5 sklearn 版本差异导致训练报错现象本地跑得好好的代码换一台电脑报AttributeError或者mean_squared_error参数不识别之类的错。原因课程设计环境之间 pandas、sklearn 版本不一致部分参数在不同版本里被弃用或改名比如 sklearn 1.4 之后 mean_squared_error 的 squared 参数逐步被替换。解决在项目里放一个 requirements.txt把关键依赖版本钉死例如pandas1.5,2.0、scikit-learn1.0,1.4、matplotlib3.6、seaborn0.12。答辩前在准备演示的机器上按这个文件重装一遍环境跑通一次全流程再上台。6. 加分技巧把模型封装成现场演示函数前面所有代码都是「写好、跑完、出图」但答辩现场最能提神的是一个能现场交互的演示。我拆过的满分项目十有八九都在最后加了一个自定义预测函数让评委现场报参数、代码立刻给出预测价格。def predict_price(area, rooms, halls, direction, decoration, building_age5): # 构造与训练时完全同构的单行样本列名不能变 sample pd.DataFrame([{ area: area, rooms: rooms, halls: halls, deco_level: deco_map.get(decoration, 1), floor_level: floor_map.get(中楼层, 2), building_age: building_age, dir_东: 1 if direction 东 else 0, dir_南: 1 if direction 南 else 0, dir_西: 1 if direction 西 else 0, dir_北: 1 if direction 北 else 0 }]) pred final_model.predict(sample)[0] return round(pred, 2) # 演示话术评委报一套 89 平两室南向精装 print(predict_price(89, 2, 1, 南, 精装))这个函数有两个易错点一是列名必须和训练时的 feature_cols 完全一致顺序不一致 sklearn 也会报特征名不匹配二是独热编码生成的 dir_ 列要手动补全缺一列都会让 predict 崩溃。所以我在函数里把所有 dir_ 列显式写全再给 building_age 和 floor_level 设默认值兜底保证任何合理输入都能跑。演示时让评委自己报参数比单纯讲指标有感染力得多。如果还想再进一步可以用 pyecharts 把热力图或区域对比图导出成交互式 HTML答辩现场鼠标悬停就能看到具体数值观感比 matplotlib 静态图高一档代价只是多学一个绘图库的 API。至于这个交互图放不放进去取决于你的剩余时间——量力而行别为了炫技引入自己都讲不清楚的依赖。最后说句过来人的习惯从那以后我拆任何课程设计项目都会先把数据文件清单、编码情况和字段含义摸清楚再动代码因为这个顺序能避免后面八成的问题。这次拆完最大的感受是这份资源的完整度确实撑得起「满分大作业」这几个字照着复现一遍你收获的不只是一个分数还有一套以后写数据分析代码都用得上的流程希望帮到你。本文还有配套的精品资源点击获取
返回列表