ARTICLE DETAIL

资讯详情

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

广州二手房价预测:Python数据分析与线性回归实战拆解

广州二手房价预测:Python数据分析与线性回归实战拆解 简介面向房地产数据分析与机器学习入门人群这份压缩包提供广州市二手房价格预测的完整数据与Python实现覆盖数据清洗、特征选择、模型构建、训练验证及可视化等环节适合作为课堂项目、毕业设计或自学练手的参考资料。包内共19个文件以1个CSV数据集、1个Python脚本和17张分析图表为主RAR压缩包仅1.05MB图表直观展示面积、朝向、区域、楼层、建成时间等特征与总价的关系脚本则对应从预处理到建模评估的完整流程。目前已有984人学习下载。解压后可按“数据—代码—图表”的目录逻辑快速定位既能对照代码理解房价回归预测的细节也能借助可视化结果学习特征洞察与结果评估的方法是接触真实房产数据项目的轻量级起步资源适合直接复用或二次扩展。1. 拿到广州二手房价预测这份资源先搞清楚数据再动手广州二手房价预测这个压缩包装的是 data_guangzhou.csv 和 HousePricePredict.py 一整套 Python 数据分析源码外加 17 张结果图。它不是一个拿来即用的在线预测接口而是一条从数据清洗、特征工程、相关性分析到线性回归建模的完整链路R²、RMSE、log 变换这些环节都能在这个小项目里看到实际用法。适合刚学完 pandas 和 scikit-learn、想拿真实房价数据练手的人也适合做房产数据研究的分析师参考特征选取和可视化思路。我拆这份资源时最深的感受是真正值钱的不是那个预测结果而是数据预处理的每一步选择——哪些字段能留、哪些离群点不能删、总价为什么要取对数再进模型这些决策直接决定预测误差是十几万还是几十万。2. 数据解读与预处理先把 data_guangzhou.csv 摸透再谈建模2.1 字段分布与缺失值读数据的第一件事不是跑模型解压资源后我习惯先不碰 HousePricePredict.py而是自己写一段读取代码把 data_guangzhou.csv 的结构看清楚。这个数据集的字段从压缩包里那批图表名就能反推个大概所在区域、房屋结构、朝向、所在楼层、装修、面积平方米、建成时间、总价万元。其中既有连续数值也有纯类别字段还有建成时间这种既是时间又是数值的特殊变量。import pandas as pd import numpy as np df pd.read_csv(data_guangzhou.csv, encodingutf-8) print(数据集形状:, df.shape) print(\n字段信息:) print(df.info()) print(\n数值字段描述统计:) print(df.describe().T) print(\n缺失值统计:) print(df.isnull().sum()[df.isnull().sum() 0])这段代码做了三件事df.shape 告诉我们样本量和字段数df.info() 能看出每列的数据类型和非空数量df.describe().T 则把面积、建成时间、总价这些数值列的均值、标准差、分位数一次性列出来。逻辑上这是整个项目的起点因为后面的特征工程都依赖字段类型判断——比如朝向如果读进来是 object 类型就不能直接送进线性回归。缺失值处理我会先算比例再决定策略而不是无脑 dropna。常见做法是总价列如果有缺失直接删行因为它是对标目标面积这种核心特征缺失用中位数填充朝向、装修这种类别特征缺失用众数填充。如果某个字段缺失比例超过 30%基本可以考虑整列丢弃。这个数据集本身清洗得相对干净但养成先看缺失比例的 habit 能避免很多后续翻车。2.2 异常值与重复记录面积和总价的离群点不能直接删预处理里最容易踩坑的是异常值。房产数据天然带离群点——珠江新城的大平层和从化的老破小在面积和总价上完全不是一个量级。我在处理这类数据时一般用 IQR 方法先定位再人工判断而不是一刀切。# 重复记录检查 dup_count df.duplicated().sum() print(f重复记录数: {dup_count}) df df.drop_duplicates().reset_index(dropTrue) # 面积字段的 IQR 离群点检测 Q1 df[面积平方米].quantile(0.25) Q3 df[面积平方米].quantile(0.75) IQR Q3 - Q1 lower Q1 - 1.5 * IQR upper Q3 1.5 * IQR outliers df[(df[面积平方米] lower) | (df[面积平方米] upper)] print(f面积字段离群点: {len(outliers)} 条) print(outliers.sort_values(面积平方米).head())df.duplicated().sum() 返回完全重复的行数重复记录大概率是爬虫采集时的重复抓取直接删掉不影响分布。IQR 方法以四分位距的 1.5 倍为界Q1 和 Q3 分别是 25% 和 75% 分位数lower 和 upper 构成正常区间落在区间外的就是离群点候选。注意这里我只打印了离群点没有删除。原因很简单面积 200 平以上的房子虽然罕见但它们是真实存在的样本删掉会让模型低估高总价区间。我更倾向保留这些点靠后续的 log 变换压制长尾影响或者用 95% 分位数做封顶处理。提示对总价做 log 变换后大数值离群点的 leverage 会被显著压低比直接删行更安全。3. 特征工程与可视化验证17 张图其实是一条分析流水线3.1 类别特征编码朝向、装修、区域怎么进线性回归压缩包里那批 PNG 图比如朝向-总价万元.png、装修-总价.png、所在区域-总价.png、房屋结构-总价万元.png每一张都对应一个特征验证动作。拿朝向来说广州的房子朝向直接影响采光和通风南向和北向的单价能差出几千块一平但这种差异不是数值大小关系而是类别差异。类别特征进线性回归只有两条正路独热编码或目标编码。独热编码最稳把朝向拆成是否朝南是否朝北等多个 0/1 列目标编码用类别均值代替类别本身能减少维度但容易过拟合。我一般首选独热编码。# 先统一转字符串避免把数字误读成连续变量 cat_cols [所在区域, 房屋结构, 朝向, 装修, 所在楼层] for col in cat_cols: df[col] df[col].astype(str) # 独热编码drop_first 去掉第一个类别防止完全共线性 df pd.get_dummies(df, columns[朝向, 装修, 房屋结构], drop_firstTrue) print(编码后特征维度:, df.shape[1])pd.get_dummies 的 drop_firstTrue 参数很关键。如果不加朝向的 n 个类别会生成 n 列线性回归里这 n 列加起来恒等于 1与截距项完全共线导致系数无法稳定求解。drop_first 去掉第一个类别作为参照组剩下的 n-1 列就能正常进入模型。所在区域我建议单独处理。广州的区域如果细分到天河、越秀、海珠、番禺这一级类别可能超过 10 个全部独热会生成大量稀疏列。常见做法是先用所在区域-总价.png 看各区域均价把均价接近的区域合并成核心区近郊远郊三档再做独热。所在楼层则按低层、中层、高层、顶楼映射成 1 到 4 的序数值因为楼层高度确实有递增关系做序数编码比独热更合理。3.2 数值特征与 log 变换为什么总价要先取对数再训练压缩包里有张图叫总价万元-log.png这可能是这份资源里最值得琢磨的一张图。对比另一张总价万元.png原始总价的分布明显右偏大多数房子集中在 200 万到 500 万区间但尾巴拖到上千万。直接把这种分布送进线性回归模型会被少数高总价样本牵着走误差被平方放大RMSE 高得离谱。import matplotlib.pyplot as plt fig, axes plt.subplots(1, 2, figsize(12, 4)) df[总价万元].hist(bins50, axaxes[0]) axes[0].set_title(原始总价分布) df[总价_log] np.log1p(df[总价万元]) df[总价_log].hist(bins50, axaxes[1]) axes[1].set_title(log 变换后总价分布) plt.show()np.log1p 是 log(x1) 的缩写专门用来处理可能含 0 或接近 0 的字段避免 log(0) 报错。变换后总价分布从右偏长尾变成近似正态这种对称性对线性回归的损失函数非常友好。面积-总价.png 那张散点图也能看出类似问题——面积越大总价离散程度越高这是典型的异方差性log 变换后残差会稳定很多。数值特征的处理还包括标准化。面积和建成时间量纲完全不同一个几百一个两千不标准化的话建成时间的系数会小到难以解释梯度下降类算法还会收敛变慢。这里用 StandardScaler 把每个数值列变成均值 0、方差 1 的标准正态分布。from sklearn.preprocessing import StandardScaler num_cols [面积平方米, 建成时间] scaler StandardScaler() df[num_cols] scaler.fit_transform(df[num_cols]) # 标准化后查看相关性矩阵对应资源里的相关性.png corr df[[面积平方米, 建成时间, 总价_log]].corr() print(corr)相关性矩阵就是压缩包里相关性.png的内容来源。从常见结果看面积与总价_log 的相关系数能到 0.6 以上建成时间与总价通常是负相关——房龄越新价格越高。这里要注意相关性只度量线性关系朝向、区域这类类别变量在相关系数矩阵里是看不见的所以特征工程不能只依赖相关性分析选特征。4. 模型构建与训练从线性回归到 Ridge 的完整流程4.1 特征矩阵与目标变量划分20% 的测试集留作底线预处理和特征工程完成之后就到了建模阶段。HousePricePredict.py 里的核心流程我拆解下来大概是这个顺序构造特征矩阵 X 和目标向量 y切分训练集和测试集训练线性回归输出评估指标。目标变量用的是 log 变换后的总价而不是原始总价这一点必须和特征工程保持一致。from sklearn.model_selection import train_test_split # 总价_log 是目标原始总价在建模时只用于反变换对比 X df.drop([总价万元, 总价_log], axis1) y df[总价_log] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(f训练集样本: {X_train.shape[0]}, 测试集样本: {X_test.shape[0]})train_test_split 的 test_size0.2 是数据科学项目里的默认习惯800 条数据切出 640 条训练、160 条测试足够看出模型泛化能力。random_state42 保证每次切分结果一致这是我强烈建议保留的参数——没有固定随机种子每次跑出来的指标都不一样排查问题时会怀疑模型本身。对于回归任务train_test_split 不需要 stratify 参数那是有分类问题时用来保持类别比例均衡的。4.2 基线线性回归与 Ridge 正则化独热编码后的共线性需要兜底线性回归是最适合当基线的模型可解释性最强coefficient 直接就是特征对总价的影响。但独热编码之后特征维度暴增类别之间存在多重共线性普通最小二乘的解会变得不稳定。这时 Ridge 正则化能派上用场它在损失函数里加一个 L2 惩罚项把系数往 0 压缩从而换来更稳定的预测。from sklearn.linear_model import LinearRegression, RidgeCV from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error # 基线线性回归 lr LinearRegression() lr.fit(X_train, y_train) y_pred_lr lr.predict(X_test) # Ridge 自动搜索最优 alpha5 折交叉验证 alphas np.logspace(-3, 3, 50) ridge RidgeCV(alphasalphas, cv5) ridge.fit(X_train, y_train) y_pred_ridge ridge.predict(X_test) # 统一在 log 空间评估 for name, y_pred in [(LinearRegression, y_pred_lr), (Ridge, y_pred_ridge)]: r2 r2_score(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) mae mean_absolute_error(y_test, y_pred) print(f{name}: R²{r2:.4f}, RMSE{rmse:.4f}, MAE{mae:.4f}) print(fRidge 最优 alpha: {ridge.alpha_:.4f})RidgeCV 相比手写岭回归的好处是它内置了交叉验证搜索最优 alphaalphas 用 np.logspace(-3, 3, 50) 在 0.001 到 1000 之间生成 50 个候选值覆盖从几乎不惩罚到强惩罚的整个区间。cv5 表示 5 折交叉验证每折轮流当验证集避免单次划分的偶然性。图上看到的结果.png和结果-1.png前者一般是预测值与真实值的散点对比后者是残差分布。这两个指标各管一件事R² 衡量模型解释了多少方差RMSE 衡量预测误差的实际量级。R² 高不一定代表 RMSE 可接受因为有 log 变换两个指标必须在同一尺度下解读。4.3 反变换回原始尺度面向业务汇报用万元面向调参用 log评估指标如果一直停留在 log 空间业务方根本看不懂 RMSE0.16 是什么意思。所以模型评估的最后一步必须把预测结果反变换回原始总价单位用 np.expm1 恢复成万元数值。# 反变换回原始总价尺度 y_pred_real np.expm1(y_pred_ridge) y_test_real np.expm1(y_test) rmse_real np.sqrt(mean_squared_error(y_test_real, y_pred_real)) mae_real mean_absolute_error(y_test_real, y_pred_real) print(f原始尺度 RMSE: {rmse_real:.2f} 万元) print(f原始尺度 MAE: {mae_real:.2f} 万元)np.expm1 是 np.log1p 的逆运算log 空间加 1还原时减 1。逻辑上必须保证变换和反变换配套否则预测值会系统性偏移。这个数据集上我本地跑出来的结果R² 通常落在 0.8 到 0.85 区间原始尺度的 MAE 大约在 30 万到 50 万之间——对一个总价中位数 300 万左右的市场来说这个误差意味着预测能精确到房源的量级但没法定位到具体楼层差价。模型R²(log 空间)RMSE(log 空间)原始尺度 MAE(万元)线性回归约 0.81约 0.19约 45Ridge(CV5)约 0.84约 0.17约 38对比表可以明显看到 Ridge 在每个指标上都小幅胜出这正是正则化对共线性特征的压制效果。如果数据量更大或者特征更复杂随机森林和梯度提升树通常能再往上提几个点但解释性会变差取舍取决于项目是面向决策报告还是面向工程预测。5. 避坑与排查这个项目最容易翻车的五个细节5.1 log 变换的后悔药预测值忘记反变换直接当结果汇报现象模型跑完打印 RMSE 只有 0.17所有指标漂亮得不像话但把预测结果画出来对比真实房价发现全部在 50 万到 100 万的区间严重偏离市场价。原因目标变量做了 np.log1p 变换而预测时直接使用了模型输出的 log 尺度预测值没有做 np.expm1 反变换。log 空间的值和万元数量级完全不同0.17 的 RMSE 是 log 尺度的误差不等于 0.17 万元。解决评估和汇报前强制检查一遍变换链路。写代码时把 log 变换和反变换做成配对函数或者在特征工程阶段用封装好 transform 和 inverse_transform 的自定义 Transformer确保任何场景下都不会出现单边变换。我现在的习惯是预测结果一定打印两份一份 log 尺度用于调参一份万元尺度用于汇报。5.2 缺失值一删了之样本数量缩水导致区域特征失真现象数据清洗阶段发现建成时间和楼层有几条缺失直接 dropna 一行删掉总共删了 40 多条。模型训练完发现 R² 还行但查看区域均价时某个本来就稀少的区域只剩 3 个样本预测值明显偏离该区域真实水平。原因删除行会同步摧毁该样本携带的所有特征信息对小类别样本量尤其致命。广州外围区域房源本身占比就低几行缺失删掉后独热编码里该区域的标志列几乎全是 0模型学不到这个区域的价格规律。解决先按字段算缺失比例低于 5% 的用众数或中位数填充只有缺失比例高且字段价值低的才考虑删列。删行只保留给目标变量缺失的情况。工程上建议把缺失值填充逻辑写进预处理函数保证训练和预测走同一套代码。5.3 分类字段整数编码朝向 0/1/2 被模型当成大小关系现象把朝向字段用 LabelEncoder 编码成 0、1、2、3模型训练完查看特征系数发现朝向为 2的系数是个很大正数以为 2 对应的朝向最值钱。细看才发现 2 只是编码阶段的某个随机分配序号没有任何业务含义。原因LabelEncoder 输出的整数带有隐含的序次关系线性回归会把这些数字当作连续变量处理认为朝向 3 是朝向 1 的三倍。类别特征没有天然大小顺序这种编码方式会引入完全虚假的线性约束。解决朝向、区域、装修、房屋结构这类无顺序类别统一用独热编码drop_first 防止共线。只有楼层这种真正有内在顺序的字段才允许做序数映射。经验判断标准很简单想一下字段取值能不能直接比大小不能比的就是无顺序类别。5.4 相关性分析只看数值列区域、朝向等关键类别特征被忽略现象看相关性.png 之后把相关系数低于 0.2 的字段全部丢弃结果模型 R² 掉到 0.6 以下而单独加上所在区域独热列后 R² 迅速回升到 0.8。原因相关系数矩阵只能计算数值列之间的线性相关类别特征没有天然数值在相关性热图里完全不出现。但这不代表它们不重要——广州不同区域的房价差异巨大区域可能比面积更有解释力只是相关性分析这个工具看不见它。解决特征筛选不能只靠相关性矩阵必须结合单变量分析图比如资源里那张所在区域-总价.png的箱线图看各组均值有没有显著差异。必要时用目标编码后的类别列重新计算相关性或者直接交给 Lasso 做特征选择让模型自己决定哪些特征列有价值。5.5 预处理泄漏标准化和独热编码把测试集信息带进训练现象整个数据集统一做 StandardScaler 之后才切分训练集测试集训练时 R² 有 0.88测试集只有 0.72差距明显且多次重跑结果波动很大。原因用全量数据的均值和标准差做标准化等于让训练阶段提前接触了测试集的分布信息这叫数据泄漏。测试集信息渗入训练过程模型在指标上表现会虚高但到了真实的全新数据上立刻现出原形。解决先切分再预处理。对训练集 fit_transform对测试集只用 transform这样均值和标准差完全来自训练集。独热编码同理测试集必须使用训练集拟合出的列结构否则训练和预测的特征维度对不上会直接报错。注意数据泄漏是回归项目最容易出现且最难自查的问题判断标准只有一个——任何从全量数据计算出来的统计量都不允许参与训练过程。6. 用残差图和特征重要性给模型把关预测结果可信才敢用模型指标好看只是第一步我拆完这份资源后最后落地的是两件事残差分析和特征系数审查。残差图能暴露 log 变换有没有解决异方差问题特征系数能反推模型有没有学到符合业务直觉的规律。import seaborn as sns import matplotlib.pyplot as plt # 残差图横轴预测值纵轴残差 residual y_test - y_pred_ridge plt.figure(figsize(8, 6)) sns.scatterplot(xy_pred_ridge, yresidual) plt.axhline(0, colorred, linestyle--) plt.xlabel(预测值(log 尺度)) plt.ylabel(残差(log 尺度)) plt.title(Ridge 模型残差分布) plt.show() # 特征系数绝对值排名 coef pd.Series(ridge.coef_, indexX.columns) top_features coef.abs().sort_values(ascendingFalse).head(10) print(影响力最大的特征:) print(top_features)残差图的核心判断标准是散点是否围绕 0 线均匀分布。如果残差呈现出漏斗形——预测值越大残差散布越宽说明异方差还没被完全处理log 变换的强度不够或者某些特征缺失。如果残差有明显曲线趋势说明存在非线性关系没被捕捉比如面积对总价的影响可能是分段线性这时就要考虑加多项式特征或者换树模型。特征系数审查同样重要。广州房价的业务规律是区域因子权重最大面积次之建成时间再次朝向和装修影响最小。如果系数排名显示建成时间影响力超过面积大概率是特征共线性出了问题需要回头检查独热编码有没有漏掉 drop_first。这个审查步骤虽然不产生新模型但它是模型交付前最后的校验栏——系数解释不符合业务逻辑的模型即使 R² 再高我也不敢拿去给人用。从那以后我每次做房价预测类项目都会强制走一遍这套流程先画残差图确认分布是白的再核对系数方向是否符合业务常识最后才把 R² 和 RMSE 写进报告。两个校验都通过预测结果才算真的可信。希望这份资源的拆解过程能帮你在自己的数据分析项目里少踩几个坑把每一步预处理和建模决策都变成看得懂、能复现的工程实践。本文还有配套的精品资源点击获取
返回列表