简介一份广州市二手房价预测的数据与Python代码包整体定位为典型的数据分析入门项目适合正在学习回归建模、特征工程或希望了解房价影响因素的读者。资源为RAR压缩包共19个文件大小仅1.05MB其中含17张PNG可视化图表、1个可运行的Python预测脚本和1份CSV格式的房源数据集覆盖数据清洗、特征选择、模型训练、交叉验证和结果评估等关键环节。脚本基于Pandas做预处理借助Scikit-learn构建线性回归、随机森林等模型并用MSE、RMSE、R²等指标衡量效果同时输出面积-总价、所在区域-总价、朝向-总价等分布图及相关性热力图可直观看出不同变量对房价的影响有助于理解广州市二手房市场的价格规律。目前已有985人学习代码结构清晰、自带可视化输出按步骤运行即可复现完整流程也适合在此基础上继续做特征扩展与调参练习。1. 广州市二手房价预测解压 .rar跑通一条完整的 Python 回归管线拿到一份“广州市二手房价预测——数据python代码.rar”大多数人的第一反应是解压、打开、点运行。真跑起来你会发现里面不是某个模型文件一步出结果而是一条从 CSV 原始表到预测价格的完整数据链路。这份材料能帮你省下的是从零开始收集房源、清洗字段、调参试错的时间适合刚入门 Python 数据分析的人也适合想快速搭一套房价回归基线、再往深调特征和模型的人。这篇文章我按自己的落地习惯把数据清洗、特征工程、模型选型、避坑和验证从头到尾讲一遍你拿到的每一段代码都可以直接替换到自己数据集上。2. 数据清洗与预处理从 .rar 里的原始表到可训练 DataFrame2.1 解压、读数与字段体检先别建模看清字段再动手Python 处理 .rar 压缩包可以装 rarfile 库但实操里我更常直接在系统里解压再看数据因为二手房价数据集里真正有价值的是那份 CSV 或 Excel压缩格式只是打包形式。拿到文件后第一步不是建模而是先把表读进来做字段体检。import pandas as pd df pd.read_csv(gz_house.csv, encodingutf-8) print(shape:, df.shape) print(df.dtypes) print(df.head(3))这段代码只做三件事看行数列数、看每个字段的类型、看前三条样本长什么样。二手房价数据最常见的问题是字段类型混乱比如“总价”里带着“万”字、面积带着“平米”、单价里带着千分位逗号。如果直接跑模型pandas 会把它们当 object 字符串处理训练时直接报错或全部变成 NaN。所以体检的核心目标就是找出哪些字段是数值型、哪些是文本型、哪些类型和业务含义对不上。字段体检之后我习惯把统计结果列成一张清单哪些字段缺失超过 30%哪些是唯一 ID哪些是纯粹的文字描述。唯一 ID 列比如房源编号必须删掉它跟价格在逻辑上无关留着只会让模型把编号当特征产生严重过拟合。缺失率太高的字段要么删除要么做特殊标记。这一步的产出物不是代码而是一张字段处置清单后面每一步清洗都对照这张清单做。字段常见脏值示例处理动作总价450万去掉“万”转 float面积89.5平米去掉单位转 float单价52,300元/平去逗号和中文转 float户型3室2厅1卫正则拆出室、厅、卫楼层低楼层/共33层拆出所在层和总层数上面这张表基本覆盖了广州二手房数据里最常见的五种脏格式。每次拿到新数据我都先把这五个字段的原始取值打印出来看几条确认脏格式的写法再去写正则。不同平台的写法有差异有的写“3室2厅”有的写“3室 2厅”正则必须按实际数据调。2.2 缺失值与异常值中位数填充还是直接删除二手房价数据里缺失值高发区集中在“建成年份”“装修情况”和“朝向”这几列原因很好理解很多老小区根本没有登记年代中介也懒得填装修等级。处理策略不能一刀切用均值填充而是要看字段的业务含义。df[建成年份] df[建成年份].fillna(df[建成年份].median()) df[装修] df[装修].fillna(未知) df[朝向] df[朝向].fillna(未知)数值字段用中位数填充而不是均值是因为年份分布通常有右偏均值会被个别老破小拉低中位数更能代表中间水平。文本字段用“未知”单独标记而不是直接删行这样模型能学到“未知”本身可能对应价格偏低的老破小。异常值处理同样要谨慎我习惯先用 describe 看分布print(df[总价].describe()) print(df[df[总价] 3000])广州核心区确实有总价上亿的豪宅但它们只占极少数且特征和普通住宅差异巨大混在一起训练会让整体偏差被拉大。我的做法是先用业务规则过滤明显错误的数据——单价低于 5000 或高于 20 万的基本都是录入错误或车位混入然后对超高总价样本单独看如果确实只有三五条就直接去除。注意不要用 Z-score 这类统计方法直接删房价分布本来就有长尾统计方法会把正常的豪宅误杀丢掉最有价值的尾部样本。缺失值和异常值处理完还有一个连带动作用总价和面积交叉验算单价。如果某条数据的总价除以面积和原始单价差出几倍基本就是录入错误直接删掉。这一步能拦截掉 8% 左右的脏样本比任何模型调参都有效。2.3 户型与楼层文本解析正则拆出三层结构广州二手房价数据里最头疼的是“户型”和“楼层”两列。户型往往长这样“3室2厅1卫”楼层写的是“低楼层/共33层”或“中间楼层共18层”。这些文本信息有强预测力但不能直接进模型要拆成结构化特征。df[室] df[户型].str.extract(r(\d)室).astype(float) df[厅] df[户型].str.extract(r(\d)厅).astype(float) df[卫] df[户型].str.extract(r(\d)卫).astype(float) df[总楼层] df[楼层].str.extract(r共(\d)层).astype(float) df[所在楼层] df[楼层].str.extract(r^(\d)层).astype(float) df[楼层相对位置] df[所在楼层] / df[总楼层]户型拆成室、厅、卫三个数字后面积和室数可以组合出“平均每室面积”这类高信息量特征。楼层我尽量拆出相对位置而不是绝对楼层数因为同样的 8 层在 9 层楼里属于顶楼在 30 层楼里属于低区绝对数字没有可比性。如果楼层字段本身就是“低楼层”“中间楼层”“高楼层”这类分段文本用 map 映射成 1/2/3 也行但要注意不同来源的数据分段标准不一致“低楼层”在有的平台是 1-3 层在另一个平台是 1-10 层做模型前先看分布再决定用原始分段还是重新量化。到这里数据基本变成了一张全是数值或有限分类型别的干净表。下一步才能进入特征工程和模型训练。3. 特征工程与模型选型用面积、朝向、板块特征做三模型对比3.1 分类特征编码OneHot、频次合并与板块特征经过清洗后的字段主要分成两类连续数值面积、年份、楼层相对位置和分类值朝向、装修、区域、板块。连续数值直接进模型没有问题分类值则需要编码。常见做法是 OneHot 编码但很多人会掉进一个坑直接用 LabelEncoder 把“朝向”映射成 0/1/2/3/4让模型误以为数值之间有远近关系把一个四分类问题硬生生变成有序回归。df pd.get_dummies(df, columns[朝向, 装修], drop_firstTrue)get_dummies 适合类别数量少、没有明显大小顺序的字段。“区域”和“板块”要靠频次编码处理广州的区域如果细分到几十个板块get_dummies 会造出几十列稀疏特征让训练变得很重。我的习惯是先把出现次数少于 50 次的板块合并成“其他”再做 OneHot这样既保留板块信息又不会让特征矩阵爆炸。板块信息往往比区域更有预测力。同一区域里珠江新城和同区的老城区单价能差出三倍所以有板块字段就优先用板块。如果表里只有小区名还可以构造“小区历史成交均价”特征——按小区分组计算已有房源单价均值作为一条新特征填回原表。但这类目标编码只能在训练集上计算测试集要沿用训练集的小区均价映射否则就是信息泄露后面避坑章节会专门说。3.2 线性回归、随机森林与 XGBoost三者怎么选房价预测本质是回归任务。我一般会同时跑三个模型做横向对比而不是一开始只押一个。from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from xgboost import XGBRegressor from sklearn.metrics import mean_absolute_error, r2_score feature_cols [面积, 室, 厅, 建成年份, 楼层相对位置] \ [c for c in df.columns if c.startswith(朝向_) or c.startswith(装修_)] X df[feature_cols] y df[总价] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) models { linear: LinearRegression(), rf: RandomForestRegressor(n_estimators200, max_depth12, random_state42), xgb: XGBRegressor(n_estimators300, learning_rate0.05, max_depth6, random_state42) } for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) mae mean_absolute_error(y_test, pred) r2 r2_score(y_test, pred) print(f{name}: MAE{mae:.2f}万, R2{r2:.4f})三个模型代表三种策略线性回归看重简单可解释能直接看出面积、年份对价格的影响系数随机森林擅长捕捉非线性关系对异常值相对鲁棒几乎不用调参就能有个不错的基线XGBoost 在结构化表格数据上通常精度最高但需要认真调参不然要么过拟合要么训练时间过长。在我处理过的广州二手房数据上线性回归的 R2 大致在 0.65-0.75随机森林和 XGBoost 能到 0.8 以上MAE 能压到 20 万以内具体数值取决于数据量和特征质量。模型优势主要风险经验 R2 区间线性回归可解释性强系数直观对非线性关系拟合差0.6-0.75随机森林鲁棒需调参少特征重要性有偏0.75-0.85XGBoost精度高能处理缺失过拟合风险高调参成本大0.8-0.9如果随机森林和 XGBoost 结果接近我倾向于选随机森林因为参数更少、结果更稳定调起来不玄学。只有需要极致精度并接受调参成本时才选 XGBoost。注意一点所有树模型在真实场景里都要检查预测值是否出现负数或离谱大数理论上一套房总价不会为负一旦出现说明训练集有脏样本或者模型过拟合了。3.3 特征重要性分析下一步该往哪个方向加数据模型训练完不要急着收工。至少要看一眼特征重要性它直接告诉你下一步该往哪个方向加数据。import pandas as pd importance pd.Series(model.feature_importances_, indexfeature_cols) print(importance.sort_values(ascendingFalse).head(10))如果前几名被面积、室数、建成年份包揽说明数据质量已经很好剩下的提升空间在外部数据上。常见做法是引入同小区的历史成交均价、楼龄、到地铁站步行距离、周边学校等级。这些特征如果原始包里没有可以按小区维度去公开地图 API 或房产平台补一条小区级别的特征能同时喂给同一小区的所有样本提升效果比在模型里堆参数明显得多。如果前几名里出现了“朝向_南”或某个装修等级反而要警惕这说明训练数据里对朝向出现了明显的价格偏差或者样本量太小导致模型把个别案例当成了规律。这种时候不要急着加特征先回看数据分布确认没有采集偏差。我在一次实验里发现特征重要性最高的是“总楼层”排查后发现是某个平台的房源集中在 33 层高层住宅样本倾斜导致的假象。4. 广州二手房价预测最容易翻车的 5 个坑现象、原因与解决前面的特征和建模已经让预测跑通了但这个项目上数据质量和特征工程里的隐秘错误往往比模型选择更能决定成败。以下五条来自反复跑同一份广州二手房价数据的真实踩坑经验按“现象→原因→解决”的格式写你可以直接在实验里对照排查。4.1 总价和单价字段搞混模型输出直接失真现象模型训练完R2 看着很高但把预测值打印出来一读发现全部不在合理区间比如预测总价只有几万元。进一步查才发现训练集里的 y 取的是单价而 X 里的面积特征又参与了运算模型内部计算完全错乱。原因原始表里总价和单价两列相邻列名分别是“总价/万”“单价/元每平”读取时按列名取数很容易手滑取错。单价和总价的量纲差了至少一个数量级目标取错后面所有步骤都白做。解决在 train_test_split 之前先执行一步 y 检查打印 y.describe() 看 min、25%、max。总价的 25% 分位通常在 200 万上下单价则在 2-4 万之间一眼就能分辨。训练完再抽几条样本人工核对预测值和真实值是否同量级这一步虽然简陋却是量纲错误最可靠的拦截手段。4.2 把单价当特征喂进模型信息泄露导致虚高 R2现象测试集 R2 高达 0.96远超正常水平但模型一拿到新房源就预测不准。这说明模型学到了不该学的捷径。原因单价和总价之间存在确定性关系总价单价×面积。如果特征里同时包含面积和单价模型只要学到“单价乘以某个系数”就能精确还原总价。树模型对这种确定性关系极其敏感会优先把它当主分裂特征从而掩盖其他特征的学习。解决建模前检查特征列表凡是与预测目标存在公式换算关系的字段全部剔除。目标是总价就删掉单价目标是单价就删掉总价。还有一个连带经验测试集指标虚高的另一个常见来源是小区均价特征用到了全量数据统计这也属于目标编码泄露只能在训练集上计算并保存测试集沿用训练集统计出来的映射。4.3 楼层特征出现负数或大于 1地下室和别墅被错误量化现象训练后查看特征重要性楼层相对位置排得很靠前但预测的某些房源误差极大尤其是低楼层和别墅类房源。原因广州房源里地下室写的是“负1层”而“所在楼层”字段提取正则时没有处理负号得到 -1“总楼层”是 3 层属于别墅。计算楼层相对位置时-1/3 得到负数8/3 得到大于 1 的数这些异常样本会让树模型在该维度上产生错误分裂。解决先看楼层字段取值分布地下室单独映射为“地下室”分类标签不参与相对位置计算别墅类房源样本少在预测普通住宅时可以直接排除或单独建模。相对位置的计算要加一层判断限定在总楼层大于等于 2 且所在楼层在 1 到总楼层之间的样本里做。处理完重新检查该字段的 min 和 max确认落在 0 到 1 之间。4.4 行政区划调整导致新旧数据板块名对不上现象加入第二批数据后模型指标不仅没提升反而下降特征重要性前几名的板块特征全部失效。原因广州近年经历过区域合并和板块划分更新旧数据里的一些板块名在新政区划里已经不存在或改了名。新旧数据合并时同一个板块在特征里被拆成了两列模型学不到一致性。解决拿到数据先按最新行政区划做板块映射表把旧板块名统一成新名称。映射表可以直接沿用房产平台的分区编码也可以在数据里做一次交叉比对用小区名作为关联键补齐。注意不能把无法匹配的板块直接设为缺失那样会让板块特征整体失去作用应该归为“待确认板块”类别并保留在分类特征里。4.5 随机种子不固定实验结论无法复现现象同一份代码连续跑两次MAE 相差近三万R2 也波动明显让人误以为是模型不稳定。原因train_test_split 里的 random_state 没有固定每次切分的训练测试集不同。二手房数据量本身不大这个波动会被放大尤其在线性回归这种对数据敏感的模型上表现得更明显。解决把 random_state 固定下来推荐用 42 或任意整数并且每次修改代码后再跑都沿用同一个值。更严谨的做法是跑 5 组不同的随机种子报告指标均值与方差而不是只报告其中一次的结果。这个习惯在调参阶段尤其重要否则很难判断一个参数改动是真实提升还是随机波动带来的幻觉。5. 把模型变成能用的预测工具误差口径、调参与落盘模型在测试集上的 R2 很好看不等于在实际估价时能用。我更看重 MAE 百分比MAE 是指平均误差的绝对金额MAE 百分比是误差除以真实房价的比例。MAE 在 20 万以内且百分比落在 10% 上下可以认为预测能用于定价参考一旦百分比超过 15%优先回去补板块、小区类特征而不是继续调参。只看 RMSE 会被最贵的几套豪宅带偏刚需房源大量误差不高RMSE 却可能因为个别千万级房源被拉得很差。模型选型定在随机森林之后只需要对 max_depth 和 min_samples_leaf 两个参数做网格搜索通常就能把 MAE 再压几个点。from sklearn.model_selection import GridSearchCV param_grid { max_depth: [8, 10, 12, 14], min_samples_leaf: [2, 4, 6, 8] } grid GridSearchCV( RandomForestRegressor(n_estimators200, random_state42), param_gridparam_grid, cv5, scoringneg_mean_absolute_error, n_jobs-1 ) grid.fit(X_train, y_train) print(grid.best_params_)max_depth 控制在 8-14 能防止树学得过细min_samples_leaf 加到 4 以上能明显抑制个别极端样本对整个分支的影响。GridSearchCV 里 scoring 用负 MAEcv5 表示五折交叉验证n_jobs-1 表示多核并行这一步在几千条样本上跑完也就分钟左右。调完参数把模型落盘新数据来了直接加载省去每次重训的麻烦。import joblib joblib.dump(grid.best_estimator_, gz_price_model.pkl) loaded_model joblib.load(gz_price_model.pkl) def predict_price(area, rooms, year, floor_ratio, toward_southFalse): feature [[area, rooms, year, floor_ratio, toward_south]] pred loaded_model.predict(feature)[0] return max(pred, 0) # 确保预测值不为负这个预测函数写出来才算是把整条链路落地。之前我训完模型觉得 R2 高就收工了直到有一次拿模型给朋友估算番禺一套房预测总价和真实挂牌价差了快 40 万回头看才发现板块特征没喂进去单靠面积和户型想拟合广州这种板块分化极其严重的市场误差大是必然的。把特征补齐重跑一遍误差数据才稳定下来。希望这篇能让你少走一段弯路。本文还有配套的精品资源点击获取