☰
Python房价可视化预测实战:从数据清洗到随机森林模型调优
2026/10/1 3:26:58 网站建设 项目流程

简介:基于Python的房价可视化预测项目是一套完整的课程设计/期末大作业方案,面向需要完成数据挖掘类作业的高校学生及Python入门者。项目围绕二手房价格数据展开,覆盖数据清洗、特征处理、模型预测与可视化展示等环节,可帮助使用者快速理解房价分析的全流程。压缩包共158个文件,总大小40MB,包含18个Python源码文件、18个CSV数据集(含原始数据与清洗后数据等多个版本)、15个HTML交互页面、65张PNG图表,以及PPTX答辩讲解和文档说明,目录划分明确,便于按模块查阅。已有223人学习下载,资源内附代码注释,新手也能顺畅阅读;文档涵盖需求设计与实现思路,最终PPT可直接用于答辩展示。项目部署简单,界面完善,功能齐全,适合作为期末大作业或课程设计的高分参考方案。

1. 房价可视化预测项目到底要做什么:先搞清楚交付物和考核点

拿到“基于Python的房价可视化预测项目”这个题目,大多数人是懵的:它到底是一门课的课程设计,还是一个毕业设计?是重代码还是重报告?我帮你拆一下,这个题目的核心交付物其实是三样——能跑的源码、能讲清过程的文档说明、能撑住答辩的PPT讲解。它要解决的不是把房价预测到多准,而是让你把一条“数据获取、清洗、可视化探索、建模预测、结论复盘”的完整链路做出来,并且每一步都能讲出道理。这个项目最适合两类人:一类是计算机、大数据、信管专业要做课程设计或毕设的学生,另一类是刚入门Python数据分析、想用一个完整案例把pandas、Matplotlib、scikit-learn串起来练手的人。方向很成熟,难度适中,坑也相对集中,属于值得投入、性价比很高的实战选题。

2. 搭建房价预测的数据底座:数据集选型、清洗与特征工程

2.1 数据集怎么选:加州房价数据集比波士顿更稳

先说结论:做房价预测课程设计,首选scikit-learn内置的加州房价数据集(California Housing),不要再死磕波士顿房价。波士顿房价在2019年后就被scikit-learn标记为过时,新版本里已经逐步移除,而且它没有经纬度字段,你想画“价格随地理位置变化”的可视化都没法画。加州房价有20640条样本、8个特征,还自带经纬度,非常适合做地理散点图,这在答辩时是很好的加分项。

如果你拿到的课程设计题目文档里明确写了“使用波士顿房价数据集”,那就绕不开它;但如果没有指定,直接用加州房价,并且在文档里写一句“选择该数据集是因为它更新、字段更丰富、支持地理可视化”,这本身就是一次很漂亮的选型答辩。

from sklearn.datasets import fetch_california_housing import pandas as pd # 加载加州房价数据集,返回的是类似字典的结构 housing = fetch_california_housing() # data 是特征矩阵,target 是房价中位数(单位:十万美元) df = pd.DataFrame(housing.data, columns=housing.feature_names) df["price"] = housing.target print(df.head()) print(df.info()) print(df.describe())

这里的关键点有两个。第一,housing.data和housing.target是分开存储的,要自己拼成一份完整的DataFrame;第二,price的单位是十万美元,也就是0.5代表5万美元,不是普通的美元。很多同学在这里第一次翻车,画出来的图价格轴全是小数,还以为数据集坏了。这个单位问题会在后面讲坑的时候再重点说一遍,因为它在PPT讲解里被老师问到的概率很高。

各字段含义如下:MedInc是街区收入中位数,HouseAge是房龄中位数,AveRooms是平均房间数,AveBedrms是平均卧室数,Population是街区人口,AveOccup是平均入住人数,Latitude和Longitude是经纬度。这8个字段里,对房价影响最大的一般是MedInc,这个结论后面会被可视化和模型双重验证,整个项目的叙事线索就靠它串起来。

2.2 数据清洗与特征工程流程

加州房价数据集的“干净”程度在公开数据集里算比较高的,没有缺失值,但这不代表你可以跳过清洗环节。课程设计文档和PPT里必须体现出“我处理过数据”,所以即使数据本身没坑,也要把检查流程走一遍,并把结果写进文档。

# 1. 缺失值检查 print("缺失值统计:") print(df.isna().sum()) # 2. 重复值检查 print("重复行数量:", df.duplicated().sum()) # 3. 价格分布看一下是否偏态 df["price"].hist(bins=50, edgecolor="white")

真实项目里,缺失值处理的做法是有缺失的列先用df.isna().sum()看数量,少于5%用中位数填充,多于5%就要考虑这个特征还能不能用。重复值在房价场景里多数是真实房源重复录入,直接删除。价格分布右偏的话,log变换是常见做法;不过在加州房价上不建议动价格,因为后续模型用R²评估时,直接预测原始价格更直观,答辩讲起来也容易懂。

特征工程才是拉开档次的地方。光用原始8个字段做线性回归,R²大概在0.6上下,这不够好看。但如果你构造几个有业务含义的组合特征,再把模型换成随机森林,R²能明显提升,而这一切在答辩里都能讲出理由。

# 构造组合特征:房间密度、卧室占比、人均收入 df["rooms_per_household"] = df["AveRooms"] / df["AveOccup"] df["bedrooms_ratio"] = df["AveBedrms"] / df["AveRooms"] df["income_per_room"] = df["MedInc"] / df["AveRooms"] print(df[["rooms_per_household", "bedrooms_ratio", "income_per_room"]].describe())

这三个特征不是凑数的。rooms_per_household衡量的是人均居住空间,比单纯的AveRooms更合理,因为一套有10个房间但住了12口人的房子,和一套有10个房间但住了2口人的房子完全不是一个概念;bedrooms_ratio反映的是户型结构;income_per_room把收入和居住密度结合,通常能看出明显的价格分层。特征构造完成后,用df.corr()看一眼新特征和price的相关性,把结果贴到文档里,说明特征筛选是有依据的。

2.3 训练集测试集切分与数据泄漏预防

这一步必须先做,而且要做得规范。切分和标准化之间存在一个很多人都会踩的顺序坑:如果先对整个数据集做标准化再切分,测试集的信息就已经通过均值和标准差“泄漏”进了训练过程,模型的评估结果会虚高,答辩时被深问很容易露馅。

from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 选定建模用的特征 features = ["MedInc", "HouseAge", "AveRooms", "AveOccup", "rooms_per_household", "bedrooms_ratio", "income_per_room"] X = df[features] y = df["price"] # 先切分,再标准化 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test)

注意这里X_train用的是fit_transform,先拟合再转换;X_test用的只是transform,直接用训练集学到的均值和标准差做转换。这一步是全项目最容易被忽略但最容易被答辩老师看穿的地方。你把这两行的区别写在文档里,老师就知道你是真懂而不是只会调库。

提示:随机森林和XGBoost这类树模型不要求特征标准化,线性回归和KNN才需要。如果你后面决定用随机森林做主力模型,训练它时应该传原始的X_train,而X_train_scaled只给线性回归用。

3. 可视化先行:用Matplotlib和PyECharts把房价数据讲成故事

3.1 Matplotlib画四张核心图:分布、空间、相关性、单特征

可视化是这类项目里性价比最高的环节,因为答辩老师翻PPT时,最先看的永远是图,不是代码。我一般建议画四张核心图:价格分布直方图、经纬度价格散点图、特征相关性热力图、MedInc与价格的散点图。这四张图画完,数据探索部分就立住了。

import matplotlib.pyplot as plt import seaborn as sns # 中文字体设置,否则标题会显示成方块 plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # 第一张:价格分布 axes[0, 0].hist(df["price"], bins=50, edgecolor="white", color="#4C72B0") axes[0, 0].set_title("房价分布直方图") axes[0, 0].set_xlabel("房价(十万美元)") # 第二张:地理空间分布,颜色映射房价 sc = axes[0, 1].scatter( df["Longitude"], df["Latitude"], c=df["price"], cmap="viridis", s=2, alpha=0.6 ) axes[0, 1].set_title("房价随地理位置分布") axes[0, 1].set_xlabel("经度") axes[0, 1].set_ylabel("纬度") fig.colorbar(sc, ax=axes[0, 1], label="房价(十万美元)") # 第三张:相关性热力图 corr_cols = ["MedInc", "HouseAge", "AveRooms", "AveOccup", "rooms_per_household", "bedrooms_ratio", "income_per_room", "price"] sns.heatmap(df[corr_cols].corr(), annot=True, fmt=".2f", cmap="coolwarm", ax=axes[1, 0]) axes[1, 0].set_title("特征相关性热力图") # 第四张:收入中位数与房价的关系 axes[1, 1].scatter(df["MedInc"], df["price"], alpha=0.4, s=2) axes[1, 1].set_title("收入中位数与房价的关系") axes[1, 1].set_xlabel("收入中位数") axes[1, 1].set_ylabel("房价(十万美元)") plt.tight_layout() plt.savefig("房价数据探索四图.png", dpi=150) plt.show()

第一张图解答“价格分布是什么形态”,看出价格右偏、长尾集中在低价区;第二张图解答“房子贵在哪”,能看到旧金山湾区和洛杉矶周边明显偏亮,中部荒漠区域偏暗;第三张图解答“哪个特征跟房价最相关”,你会看到MedInc和income_per_room的颜色块最深,这直接为后面建模选特征提供了依据;第四张图则是把“收入决定房价”这个结论摆到明面上。四张图连起来读,就是一个完整的数据故事。

3.2 PyECharts做交互式可视化大屏:控制在两个小时内搞定

如果只想拿个及格分,Matplotlib静态图就够了。但很多学校对课程设计的“可视化”环节有硬性要求,或者你想冲高分,那就可以用PyECharts把图表搬到浏览器里,做成可交互的可视化大屏效果。PyECharts生成的HTML文件双击就能打开,答辩现场鼠标悬停能看到具体数值,这种动态效果非常加分。

from pyecharts.charts import Bar from pyecharts import options as opts # 统计各区间的房源数量 import numpy as np price_bins = pd.cut(df["price"], bins=[0, 0.5, 1.0, 1.5, 2.0, 5.0]) counts = df.groupby(price_bins, observed=True).size() bar = Bar() bar.add_xaxis([str(b) for b in counts.index]) bar.add_yaxis("房源数量", counts.values.tolist()) bar.set_global_opts(title_opts=opts.TitleOpts(title="加州房价分布区间统计")) bar.render("price_dist_bar.html") print("已生成 price_dist_bar.html,使用浏览器打开即可查看")

这里pd.cut的作用是把连续价格切成离散区间,observed=True是pandas 2.x版本里的必要参数,不加会报警告。实际项目里你还可以把地理散点图也换成PyECharts的Map或Scatter,但我的建议是不要过度投入。课程设计的时间预算有限,交互大屏做到“有两张能动的图”这个程度就够支撑“可视化”这个关键词了,剩下的时间留给建模和文档更划算。

3.3 可视化图表如何对应答辩讲解逻辑

这里分享一个很有用的技巧:每张图只讲一句话就够了。分布图讲“数据集中在低价区间”,地理图讲“房价有明显的区域聚集效应”,热力图讲“收入特征与房价相关性最强”,散点图讲“收入与房价存在明显正向趋势”。四句话连起来就是PPT的整页讲解词,不需要背长段文字。答辩老师真正想听的是你能不能把图背后的业务含义说清楚,而不是图本身多好看。

4. 从线性回归到随机森林:房价预测模型的实现与调参

4.1 模型选型:为什么线性回归打底、随机森林当主力

房价预测课程设计最稳妥的建模策略是“两个模型对比”:先用线性回归跑出一个基线结果,再用随机森林做提升。这样答辩PPT里就能放一张对比表,表格比单一模型的展示有说服力得多。

选型的理由要提前想好,否则答辩时会卡壳。线性回归的优势是简单、可解释、快,适合验证特征工程做得好不好;随机森林的优势是能捕捉非线性关系,不需要特征标准化,还自带特征重要性输出,可以和前面的热力图相互印证。如果老师问为什么不直接用神经网络,你的标准答案应该是:数据集只有两万条样本、八个特征,神经网络在这种小规模表格数据上没有优势,而且解释性差;随机森林在这个量级的数据上往往效果更好、训练更快、结果更稳。

from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 线性回归跑基线 lr = LinearRegression() lr.fit(X_train_scaled, y_train) y_pred_lr = lr.predict(X_test_scaled) print("线性回归 R2:", r2_score(y_test, y_pred_lr)) print("线性回归 MAE:", mean_absolute_error(y_test, y_pred_lr)) print("线性回归 RMSE:", mean_squared_error(y_test, y_pred_lr, squared=False))

r2_score衡量的是模型解释了多少方差,越接近1越好;MAE是平均绝对误差,单位是十万美元;RMSE是均方根误差,对大误差更敏感。这三项指标各有用处,答案里三选一全部输出。需要提醒的是,新版scikit-learn里mean_squared_error的squared=False是返回RMSE的标准写法,老教程里写math.sqrt也行,但没这么清爽。线性回归在这个数据集上R²一般会在0.6左右,有了这个基线,后面模型的提升幅度才看得出来。

4.2 随机森林必调的两个参数:n_estimators和max_depth

接下来上主力模型。随机森林有三个参数是必须理解并写进文档的:n_estimators是树的数量,越多越稳定但速度越慢;max_depth是单棵树的最大深度,控制模型复杂度;min_samples_leaf是叶子节点的最小样本数,防止树把训练集背下来。剩下的参数在课程设计层面不用深究。

from sklearn.ensemble import RandomForestRegressor # 随机森林不要求标准化,直接用原始特征 rf = RandomForestRegressor( n_estimators=200, max_depth=12, min_samples_leaf=4, random_state=42, n_jobs=-1 ) rf.fit(X_train, y_train) y_pred_rf = rf.predict(X_test) print("随机森林 R2:", r2_score(y_test, y_pred_rf)) print("随机森林 MAE:", mean_absolute_error(y_test, y_pred_rf)) print("随机森林 RMSE:", mean_squared_error(y_test, y_pred_rf, squared=False))

这里我给了三个参数的具体建议值:n_estimators=200足够稳定,再大反而浪费训练时间;max_depth=12给树留出深度来抓非线性关系,又不容易过拟合;min_samples_leaf=4保证了每个叶子节点至少有4个样本,防止极端值被记住。random_state=42固定随机种子,保证每次跑出来的结果一致,这在你调整参数对比效果时是必须的。

你可以在项目文档里加一小段“参数敏感性分析”:试一下max_depth=5和max_depth=20的R²差距,截两张图放进去。哪怕只分析一个参数,也比什么都不分析要强,因为这个过程展示了你“不是只会抄参数,而是会调参数”。

4.3 特征重要性:让模型结果和可视化结论互相印证

随机森林最有价值的一点是自带特征重要性。通过feature_importances_可以直接看出模型认为哪些特征对预测贡献最大,这恰好能和前面相关性热力图的结论对上,整个项目就能形成一个闭环。

import pandas as pd importance_df = pd.DataFrame({ "feature": features, "importance": rf.feature_importances_ }).sort_values("importance", ascending=False) print(importance_df) # 画特征重要性条形图 plt.figure(figsize=(8, 5)) plt.barh(importance_df["feature"], importance_df["importance"]) plt.xlabel("重要程度") plt.title("随机森林特征重要性排序") plt.gca().invert_yaxis() plt.savefig("特征重要性.png", dpi=150)

通常跑出来的结果里MedInc和income_per_room排在最前面,随后是Longitude和Latitude,这说明模型不仅倚重收入特征,也把地理位置信息学进去了。答辩时讲这一张图,就把“为什么前面画了地理散点图”这个问题也一并回答了:因为地理特征在模型里确实重要。

4.4 真实值与预测值散点图:评估模型的直观方式

除了R²、MAE这些数值指标,我强烈建议画一张“真实房价 vs 预测房价”的散点图。这张图的作用是直观地看模型在哪个价格区间预测得好、在哪个区间偏差大,比任何指标都直白。

plt.figure(figsize=(6, 6)) plt.scatter(y_test, y_pred_rf, alpha=0.4, s=5) plt.plot([0, 5], [0, 5], color="red", linewidth=2) plt.xlabel("真实房价(十万美元)") plt.ylabel("预测房价(十万美元)") plt.title("随机森林真实值与预测值对比") plt.savefig("预测效果散点图.png", dpi=150) plt.show()

这条红色对角线代表“预测完全正确”的理想位置。你会发现点子在低价格区间贴着对角线,到了高价格区间开始分散,说明模型对高价房源的预测偏差更大。这是一个很正常的现象,你可以把它写进文档的“局限与改进”部分:高价位房源样本量少,模型对极值的拟合能力有限。这样写,不仅不是扣分项,反而体现出你做了深入分析。

把线性回归和随机森林的结果整理成对比表格放进PPT,大致是这个样子:

模型R²MAE(万美元)RMSE(万美元)
线性回归约0.61约0.52约0.67
随机森林约0.81约0.38约0.52

不同随机种子跑出来的数字会有浮动,但线性回归在0.6上下、随机森林在0.8上下的量级是稳定的,这个提升幅度足够支撑“随机森林优于线性回归”的结论。如果跑出来随机森林0.8、线性回归0.6,这组对比就是你整份报告的核心论点。

5. 避坑指南:房价预测项目里5个最容易翻车的问题

5.1 数据泄漏:标准化写错顺序,模型成绩虚高

现象:训练集R²和测试集R²都高得离谱,比如线性回归R²直接跑到0.9以上,而常规数据根本达不到这个水平。答辩时老师随口问一句“你的数据怎么切的”,当场答不上来。

原因:写代码的时候先对整个数据集做了scaler.fit_transform(X),再切分训练集和测试集,导致StandardScaler在计算均值和标准差时用到了测试集的信息,测试集已经“泄漏”到训练过程里。模型的评估结果不是真实水平。

解决:严格按照“先切分、后标准化”的顺序执行。fit_transform只用在训练集上,测试集只调用transform。把这两行代码的差别写进文档说明里,明确写出“本项目的标准化顺序是基于训练集统计量完成”,这句话本身就是答辩的防御工事。

5.2 价格单位搞错:图上全是零点几,以为数据集有问题

现象:把price列画出来,发现数值全部集中在0.5到5之间,房价怎么可能是这么小的数?于是开始怀疑数据集损坏或者代码写错。

原因:加州房价数据集的target单位是十万美元,0.5代表的是5000美元,5代表的是5万美元。这是官方文档里写明了的,但很多人不看DESCR就直接开画。

解决:要么读数据时直接用housing.target * 100000把单位换成美元;要么在画图时把轴标签写清楚为“房价(十万美元)”。我更推荐后者,因为模型输出的MAE也会带这个单位,统一用“十万美元”反而方便对比。但文档里必须用一句话说明单位转换关系,这一条几乎白送分。

5.3 Matplotlib中文变方块:不设置字体就是一堆框

现象:图表的标题、坐标轴标签只要是中文,保存出来的图片里全是方块,看起来很糟糕。这个问题在PPT成品里尤其明显,直接影响第一印象。

原因:Matplotlib的默认字体只支持英文和某些西文字符,不包含中文字形。Windows系统下常见的解决办法是指定SimHei或Microsoft YaHei,Mac系统下是PingFang SC。

解决:在导入matplotlib之后立刻加两行配置,就是前面代码里的plt.rcParams["font.sans-serif"] = ["SimHei"]和plt.rcParams["axes.unicode_minus"] = False。第二行是处理负号显示成方块的问题。注意这两行要放在所有画图代码之前,而且要确认用到的字体在你的系统上存在,否则会静默失效。

5.4 随机森林过拟合:训练分极高而测试分一般

现象:训练集R²达到0.95以上,测试集R²只有0.7,两者差距超过0.2。答辩时老师翻到这一页,大概率会问“你的模型是不是过拟合了”。

原因:max_depth设得太大,比如不限制深度,树会不断分裂直到每个叶子节点只剩一条样本,相当于把训练集背了下来。min_samples_leaf=1也是同样的道理。

解决:给max_depth设一个合理上限,我习惯先设12,再按模型表现微调;同时把min_samples_leaf从1提高到4或5。调参的原则是训练集和测试集R²差距缩小到0.1以内,而不是追求训练集分数最高。在文档里贴出一组对比:max_depth=20时训练R²是0.96、测试R²是0.75;max_depth=12时训练R²是0.89、测试R²是0.81。这个对比一放,过拟合问题就不需要解释了。

5.5 答辩被问“这套模型能预测单套房吗”:边界没想清楚

现象:PPT讲完,老师问“你这模型能不能帮我估一下某个小区里一套具体房子的价格”,然后现场沉默。

原因:加州房价数据集的样本是“街区”级别的统计值,不是单套房源记录。没有户型、面积、朝向、楼层这些关键字段,模型预测的是区域价格中位数,不是单套房价格。

解决:在文档的“模型局限性”一节里主动写清楚这个边界。预设答案是:“该模型基于街区聚合数据,预测的是区域房价水平,适合宏观分析,若要精确估单套房,需要引入房源属性特征和更细粒度的交易数据。”把这个边界预先写出来,答辩时不仅不会被问倒,反而显得你对数据的粒度有清晰的认知。

6. 让源码、文档与PPT形成闭环:课程设计的最后一步

6.1 文档说明怎么写:目录结构、复现步骤与结果解读

课程设计的文档说明不需要写成学术论文,但必须保证另一个人按着你的步骤能完整复现整个项目。我一般建议文档包含五个部分:项目简介与数据集来源、环境依赖清单、代码结构说明、运行步骤、结果分析与局限。其中“运行步骤”要写清从打开Jupyter Notebook到运行哪个单元格、最终生成的图片和HTML文件输出到哪个目录,每一步最好配一句解释。还有一点容易被忽视:把所有依赖包的版本号列出来,requirements.txt里的版本不一致会导致别人复现失败,这会让你的文档评价打折扣。

6.2 PPT讲解的六页结构与每页讲稿

PPT不需要做十几页,六页足够,重点是每页有明确的叙事任务。首页写题目和你的姓名;第二页放数据集介绍和字段表;第三页放四张核心可视化图;第四页放模型对比表和真实值预测值散点图;第五页写结论和局限;最后一页写改进方向。其中第三页和第四页是核心,每页要准备不超过三句话的讲稿。第三页讲稿是:“数据集中在低价区间,地理聚集效应明显,收入特征是房价最重要的影响因素。”第四页讲稿是:“线性回归作为基线R²为0.61,随机森林提升到0.81,进一步提升方向是引入更多房源属性特征。”把这两段讲稿练到自然说出来,整个答辩就有了主心骨。

6.3 面对追问的策略:主动暴露局限比掩饰好

答辩最怕的不是被问倒,而是被问到没准备的点时当场编造。我的习惯是:在PPT的结论页就主动把模型的适用边界写出来,只预测区域均价、不用于单套房估价,不给极高价房源做保证。这样老师追问的空间就被压缩到了你准备好的区域里。如果被问到没准备的技术细节,比如“为什么不用XGBoost再提一版”,你可以诚实回答“当前课程设计的时间范围内,随机森林已经比基线有明显提升,XGBoost是后续优化方向”,老师基本都会满意。我带课程设计的血泪经验就一句话:模型可以不是最先进,但故事必须闭环——每张图、每个结论、每段代码之间要能互相印证。你按这套路径把数据、可视化、建模、文档、PPT串成一条线,这个项目就真正成了自己的东西。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询