首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
房价租金预测竞赛复盘:EDA如何避开数据陷阱?
📅 2026/10/17 11:59:45
✍️ 爱科研究院
👁 阅读 3,247
这个竞赛我打完之后复盘最深的感触是很多人一上来就急着调模型、跑LightGBM结果成绩一直上不去回头才发现连数据分布都没看清楚。房价租金预测这类任务数据里全是“坑”——缺失值、离群值、时间漂移、区域不平衡任何一个没在探索性分析阶段暴露出来都会在后面模型环节加倍偿还。这篇是第一篇总结专门说数据探索性分析EDA怎么做、怎么从原始表里挖掘出对预测真正有用的信息以及我在这次竞赛里踩过哪些值得记下来的坑。适合刚开始接触房价/租金类预测任务、或者想系统梳理EDA流程的朋友参考。1. 竞赛任务与EDA的整体思路先说这次竞赛的背景。主办方给了一份城市房屋租赁与出售相关的数据集包含房屋基本信息、所在位置、周边设施、以及最终成交或挂牌的价格。目标是分别预测“租金”和“售价”两个目标变量这两个目标本质上属于连续值回归问题但分布形态差异极大处理方式完全不同。拿到这样的任务第一反应不是读文档而是先做一件看起来“不紧急”但决定下限的事把数据结构彻底摸透。我的EDA目标不是“画几张图交差”而是回答四个问题数据是不是可用的有没有严重的缺失、重复、错误编码目标变量长什么样有没有极端值、多峰分布、量纲问题特征和目标之间有没有真实信号哪些特征值得进入后续建模数据层面有没有结构性陷阱比如采样偏差、时间偏差、空间拥堵这四个问题直接决定了后续特征工程的方向和模型策略。我见过不少参赛者上来就把类别变量做LabelEncoder、缺失值填均值然后开始调参最后在验证集和测试集上结果漂移巨大。根源就是EDA阶段没有发现训练集和测试集分布不一致的问题。1.1 房价与租金预测的特殊性房价和租金虽然都是价格但驱动因素完全不同。房价更多看地段、面积、房龄、供需关系受投资属性影响大租金更多看通勤便利、生活配套、租赁人群结构。在同一个数据集中这两个目标往往有强相关性——某区域租金高通常房价也高——但噪声来源不一样。所以EDA时不能把两个目标混在一起分析要分开看、分开验证。我在探索时特别关注了一个点房价的分布往往是长尾的有少量豪宅或老洋房把均值拉得很高租金的分布相对集中但存在大量低价合租带来的左偏。如果直接拿原始值建模模型会被那几个极端值牵着走。后面我会专门讲如何通过目标变换解决这个问题。1.2 EDA在竞赛流程中的真实定位有人觉得EDA是“前期工作”做完就可以扔了。实际上EDA应该贯穿整个竞赛周期。我在特征工程陷入瓶颈时会经常回到EDA阶段重新审视数据——比如某类缺失值可能不代表随机缺失而是“无此项服务”的语义某个类别在训练集里样本极少在测试集里却大量出现——这些问题只有反复看数据才能发现。这次竞赛我的固定流程是先做一遍全局EDA形成初步假设然后做一轮基线模型用模型权重反推哪些特征被忽略再针对性做深度EDA挖掘间隔交互特征最后在特征工程完成后做一轮验证集的分布对比。所以这篇总结里的内容属于第一轮全局EDA但其中很多结论影响到了后面的所有环节。2. 数据概览与基础质量检查拿到数据后我一般先不看具体内容而是跑一个数据质量检测脚本输出字段数量、行数、每列数据类型、缺失率、唯一值数量、以及每列的简单统计量。这一步能快速暴露数据是否“诚意满满”还是“需要自己造轮子”。这次竞赛的数据集有20多个字段其中大约一半是数值型一半是类别型另外有经纬度坐标和发布时间。第一眼看上去“信息量很丰富”但实际上隐藏的问题比想象中多。2.1 字段类型与含义梳理我建议在EDA阶段做一个字段字典不是简单把原始说明抄一遍而是自己去理解每个字段的真实含义。比如“户型”字段看起来是类别栏实际却包含“3室1厅”“2室0厅”“5室3厅”这种混合信息直接当类别用会丢失房间数和厅数这两个关键信号。我在首次EDA时就把这个字段拆成了房间数和厅数两个数值特征。还有个典型的字段叫“面积”看起来是连续的但分布上存在严重的峰值堆积很多房源面积被刻意写成整数或者某个小区统一值。这部分会在异常值处理时重点讨论。2.2 缺失值不只是“填上就行”缺失值是最需要小心的部分。我统计了一下大约有8个字段存在缺失其中有几个缺失率超过30%。一开始我想直接删掉这些列后来发现完全不是一回事。以“物业费”为例缺失率高达45%。按理说缺失这么多要么删除要么统一填充。但我检查了缺失样本的区域分布后发现物业费缺失的小区集中在某个特定城区的老旧住宅圈而其他城区基本有值。这说明缺失并不是随机的而是与区域发展水平相关。如果我们简单用全局均值填充相当于抹掉了“老旧小区”这个信号。这种情况下更好的做法是把“是否缺失”变成一个新的二值特征同时按区域分组进行条件填充。这个经验非常重要我在很多次竞赛里都是靠“缺失值也是一种信息”这个想法打开局面的。2.3 重复数据与近似重复数据里有一批完全相同的行一般是数据采集或合并时产生的。直接去重即可但要注意“近似重复”——比如同一套房源在不同时间点被重复记录面积、价格完全一样只是发布时间差了几天。这种情况在租赁数据里极其常见不能简单删除因为可能是带时间变化的真实记录。需要判断是同一套房源重复挂牌还是不同房源恰好相似。我使用了一个折衷方案先按关键字段小区ID、面积、户型、朝向聚类如果同一组内价格完全相同且时间差超过一定阈值就保留最新的一条如果价格有变化则作为时间序列信息保留下来。2.4 表格数据质量检查清单检查项操作方式发现的问题处理策略字段类型查看dtypes与unique值“户型”含复合信息拆分、重构缺失率按列统计缺失比例“物业费”缺失45%构造缺失标记分组填充重复行全字段比对存在少量重复全重复去重近似重复按关键字段聚簇同房源重复挂牌结合时间保留有效记录离群值箱线图/分位数面积极端值过多区间截断特征缩放编码错误检查类别不一致“朝向”存在同义不同词人工映射归一期间我建议每检查一项就顺手记录一条“数据问题日志”。这个日志会伴随整个竞赛过程每往后推进会不断补充。真要复现一个方案时日志比代码注释有用得多。3. 目标变量探索与变换策略很多人在目标变量上只看个均值方差就过了。但房价与租金预测的成败很大程度上取决于你对目标分布的敏感度。我可以直接说如果目标分布严重右偏你用MSE做损失函数模型会拼了命去拟合那些高价格的样本因为高价格样本带来的误差绝对值大梯度更新也更剧烈导致整体预测效果失衡。3.1 房价与租金的目标分布对比我画了房价和租金的直方图以及正态QQ图。房价的直方图明显呈现右偏且右侧拖着一条长尾——最贵的那批房子价格是指定中位数的10倍以上这导致均值被严重拉高。租金虽然也偏但偏度相对温和只是存在一个非常尖锐的低价峰大量合租或小户型以及一个较宽的中间平台。面对右偏最标准的做法是做对数变换也就是log1p(price)。我用对数变换后重新画图房价和租金都更接近正态分布。这个变换不是随便做的它同时也改变了损失函数下模型对误差的定义——从绝对误差变成了对数空间的相对误差意味着低价格和高价格的预测精度被拉到了更合理的权重上。不过这里有个陷阱对数变换会把目标范围压缩但竞赛评价指标如果用的是RMLSE均方根对数误差那么直接预测原始值再取log也是可以的如果指标是MAE或MSE则必须基于变换后的目标训练模型预测后再反向变换。3.2 目标变换的复现代码示例我自己用Python做EDA时常用的一段代码是这样import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt # 假设 df 为目标数据rent 和 sale_price 是两列目标 fig, axes plt.subplots(2, 2, figsize(12, 8)) # 原始分布 sns.histplot(df[rent].dropna(), bins100, axaxes[0][0]) axes[0][0].set_title(Raw Rent Distribution) sns.histplot(df[sale_price].dropna(), bins100, axaxes[0][1]) axes[0][1].set_title(Raw Sale Price Distribution) # 对数变换后 sns.histplot(np.log1p(df[rent].dropna()), bins100, axaxes[1][0]) axes[1][0].set_title(Log Rent Distribution) sns.histplot(np.log1p(df[sale_price].dropna()), bins100, axaxes[1][1]) axes[1][1].set_title(Log Sale Price Distribution) plt.tight_layout() plt.show()从图上能明显看到log变换后的租金仍有一个次峰——这个次峰其实代表的是不同区域之间的价格分层。我建议在EDA阶段多画几个层次的分布图不同城区、不同户型、不同装修状态下的目标分布。因为即使全局做了log变换子群体内的分布仍然可能不同这会影响后续分组建模或类别特征的编码方式。3.3 极端价格的样本要不要删这是我最常被问到的问题。从普遍经验来说直接删除极端价格样本不是好主意。房价数据集中的豪宅样本虽然少但它们代表了真实的市场情况删掉它们等于人为缩小了预测范围测试集里一旦出现豪宅模型直接崩盘。正确的思路是“截断”而不是“删除”。我用的是分位数截断法把售价超过99%分位数或者在log空间里偏离均值超过3倍标准差的样本单独标记出来但保留在训练集里只是给它们一个更低的样本权重。或者在特征工程环节单独为这些极端样本做一组二值特征例如“是否豪宅”让模型有机会学习到“它就是很贵”这个规律。还有一个实际经验如果极端样本对应的特征面积、地段、房龄本身也具有极端值那就不能把它们定义为“离群点”因为数据是自洽的。只有当某个样本价格极高但特征普通时它才可能是脏数据或某些特殊因素如稀缺历史建筑这时才需要考虑剔除。4. 特征维度探索——数值、类别、时间与空间EDA的第二大块就是各种特征类型与目标变量之间关系的挖掘。这个过程不是画一堆图就完事而是要形成可操作的特征工程假设。4.1 数值特征的分箱与非线性关系连续特征和价格之间很少是线性关系。因为面积超过一定阈值后价格边际增速可能放缓房龄越老价格先降后升老破小因为地段或学区溢价反而更贵。针对“面积”这个字段我做了分箱统计发现面积在60到120平米之间单价最高面积较小单价反而因为总价低而偏高面积超大单价则下降。如果直接拿原始面积做线性特征模型很难捕捉这种U型关系。后续特征工程时我把“面积”不仅作为连续值使用还生成了面积分箱、面积平方项、以及面积与房间数的比值。这些都是在EDA阶段看分布图时才想到的。4.2 类别特征的基数与融合类别特征需要统计其唯一值个数也就是基数。基数特别高的类别比如小区名称、街道如果直接做独热编码会让矩阵爆炸如果做编码标签又容易过拟合。我一般先统计每个类别在训练集和测试集的分布差异——有些类别在训练集中只出现几次测试集里却出现几十次这类特征就要考虑在更高层级上聚合比如从小区聚合到街道。另外我发现“朝向”这个字段参差不齐有“南”“南北”“三面朝南”“朝南”等各种写法指数轻轻一算就差很多。此时需要先做映射归一统一标准才能继续后续分析。4.3 时间特征别忽略发布/交易时间数据里有“挂牌时间”字段这容易被人忽略。我把挂牌时间解析成年月日再计算与数据集中最早记录的时间差就获得了“挂牌时长”这个衍生特征。更关键的是我发现价格走势存在明显的时间漂移房源的挂牌价格在每个月都会整体上移尤其在某些月份如年初和年末变化更剧烈。这说明模型不能只依赖空间特征时间维度同样重要。我在后续特征工程中加入了“是否最近半年挂牌”等时间窗口特征每次交叉验证时也注意按时间切分以免把未来信息泄漏到训练集。4.4 空间特征经纬度与区域聚合这份数据里有两个坐标字段经纬度信息如果不做处理模型很难直接拿来用。坐标本身只有二维特征但需要转化为有意义的空间特征比如计算每个房源到城市中心点的距离、到最近地铁站的距离、聚类出商圈ID、区域内房源数量与平均价格。我通过经纬度做K-Means聚类聚出了8个空间簇分析每个簇的价格中位数和租金中位数发现有些簇的房价高但租金低有些簇相反。这些空间簇不仅仅是特征也是后续模型选择时非常重要的分组依据因为不同簇的数据分布差异太大分别建模比全局建模的效果要好。值得一提的是用经纬度做聚类时需要先对坐标做局部投影转化到平面坐标系再使用欧氏距离不然直接用经纬度计算距离在维度上会严重失真。这里我推荐用哈弗辛公式或某城市的UTM投影坐标。4.5 交叉特征的预探索EDA阶段不需要先生成交叉特征但可以观察哪些特征的组合能带来更大的目标区分度。例如“户型所在城区”这个组合在分组统计租金中位数时差异巨大同样是1室1厅不同城区的租金能差两三倍。这种发现往往能指导后续做组合特征时是把两个特征直接拼接还是用目标编码。我在探索“楼层”和“是否有电梯”时发现楼层超过某一阈值且无电梯的房源租金和房价都会显著下降。这种非线性组合如果靠模型自己学需要很高的模型容量但如果我们能提前在EDA中发现并直接做成交互特征那么模型的学习压力会小很多。5. 相关性与共线性分析相关性分析是EDA的标配但很多人在竞赛里做的相关矩阵其实用途不大。我通常把这项工作拆成两个层面一是特征与目标的相关性用于筛除无关特征二是特征之间的相关性用于发现冗余避免多重共线性。5.1 特征与目标之间的Spearman相关因为目标做了log变换但特征本身还有很多离散值和极端值直接用Pearson相关系数不够稳健所以我优先看Spearman秩相关系数。我计算出面积、房间数、是否近地铁、城区等特征与log房价、log租金的相关性。面积和房间数与价格的相关性都比较高这是符合直觉的。也有几个出乎意料的特征——比如“装修状态”它的相关性并不高但后面分组看时发现装修状态在高价位段才会展现明显作用说明相关性弱不代表没有用很可能只是非线性关系。5.2 特征与特征之间的冗余高相关特征有两个典型问题一是会扰乱线性模型的系数稳定性二是很多树模型虽然对共线性不敏感但冗余特征会分散特征重要性导致剪枝后模型性能下降。本次数据中“面积”和“户型房间数”的相关性高达0.92因为房间数很多时候是由面积决定的。这种高度冗余意味着同时保留它们会稀释单一特征的重要性。我的经验是保留更直接、噪声更小的“面积”把“房间数”做成面积与房间数的比值或者房间密度的形态反而更能表达户型结构。5.3 利用VIF做数值特征体检在做特征筛选时我加了方差膨胀因子来评估数值特征的多重共线性。运行结果里面积和卫浴数的VIF都超过了10。此时没必要马上删特征因为树模型并不受此影响但我通过这些信息决定在线性模型比如Ridge回归作为基线时只放低共线性的核心特征而树模型则保留全部特征。VIF计算也有小坑分类特征如果在数据上已经被哑变量编码其VIF通常异常大所以一般只对连续数值变量计算VIF。如果特征中混入了高基数的标签编码VIF会失效需要改为减少偏差或使用条件数。5.4 相关矩阵可视化的小技巧不推荐直接做全特征热力图因为特征一多热力图黑乎乎一片根本看不出规律。我通常只选出与目标相关性Top10的特征生成“聚类热力图”这样既能直观看到特征间的分块结构也不会淹没在噪音里。同时把Spearman相关系数标注在热力图上方便阅读。6. 空间区域分析与数据漂移检查竞赛中有一个绕不开的问题训练集和测试集并不是从同一个分布中随机切分的。主办方往往按城市区域或时间时间段分区导致训练集和测试集的区域分布截然不同。这种事情在EDA阶段如果不做主动检查后面模型就会在测试集上翻车。6.1 训练集/测试集的区域分布对比我把数据中的“区域”字段做成交叉表比较训练集和测试集中每个区的占比。结果发现训练集中某个核心城区的样本占比高达35%但测试集中该城区的占比只有15%有些老城区在训练集中占比不到5%在测试集中却涨到了12%。这意味着如果模型过度依赖“区域”这个类别特征就会学习到对核心城区的偏好而忽略老城区样本的规律。解决办法有很多但如果不在EDA阶段发现这个问题后面做得再多都是盲目调整。6.2 用分布散点图检查数据泄漏我习惯在同一张三维散点图中绘制训练集和测试集的样本点分布用颜色区分。如果两个分布几乎重叠那么随机切分概率较大如果呈现明显分块就要小心区域偏差。本次分析发现有些样本的经纬度落点完全一样疑似同一房源同时出现在训练集和测试集。此时速度检查一下“房源标识”字段如果同ID存在两个不同的标签值说明有数据泄漏如果同ID在两个数据集都是同一套特征但没有标签值说明是刻意重复模型应尽量避免记忆该ID。我的处理方式是在特征工程中不把房源ID作为特征仅在索引时使用。6.3 区域特征在不同目标上的表现用分组箱线图看每个区域下房价与租金的分布能发现区域特征的重要性并不均匀。核心区域的房价和租金之间高度相关而老城区由于拆迁或学区因素房价极高但租金平稳造成两者脱钩。这说明如果模型要同时预测两个目标区域特征需要在两个模型中分别编码因为同一区域对房价和租金的影响系数完全不一样。后来我选择分成两个模型分别使用区域目标编码——即用各区域的房价中位数和租金中位数分别对区域做编码而不是使用同一个区域ID。这个经验在后续模型融合时也派上了用场。6.4 时间漂移的检测与处理除了空间偏移还需要检查时间上的趋势。我按月度统计房源发布数量的均值变化发现数据采集时间跨度为8个月房价整体上涨约7%租金上涨约3%。训练集和测试集如果切分时间跨度不同模型就会被旧数据困住无法预测新环境下的价格水平。我在EDA阶段就把“发布时间”转化为“距离发布起点月数”并检验这个序列特征交叉验证中的稳定性。如果测试集的月份范围与训练集没有交集那么必须考虑使用时间加权或者把时间趋势作为特征加入模型。7. 常见问题与排查技巧实录做EDA这件事最难的不是写代码而是不断验证你对数据的判断是否准确。这里我整理了自己在本次竞赛中最常踩的五个问题并给出排查思路。7.1 缺失值填充后模型效果反而变差因为缺失值并非随机缺失我用全局均值填充后训练集表现正常但交叉验证分数明显下降。后来改为分组均值填充并用“是否缺失”作为新特征后分数回升。经验是每次填充之后都要跑一轮简单的决策树基线对比填充前和填充后的交叉验证结果不要只盯着枯燥的缺失率表格。7.2 类别特征中的高低频冲突有个类别特征“小区类型”包含几十个等级标签低频标签占了大头。做目标编码后低频标签的目标均值容易受极端样本影响造成过拟合。我在排查时发现测试集中的低频标签比例更高于是对目标编码做了平滑处理即使用全局均值与组内均值的加权平均同时限制最小样本量。经验是对类别特征做目标编码前先统计每个类别的样本量并在训练集上做K-Fold编码防止向未来信息泄漏。7.3 经纬度聚类结果不稳定K-Means对初始中心敏感迭代两三次后发现聚类结果变化很大。由于样本数量较多最终采用Mini-Batch K-Means并固定随机种子对聚类结果做到可复现。同时聚类数用轮廓系数和业务理解综合判断不止依赖指标。7.4 离群值在交叉验证中的波动少数极端价格高的样本在随机交叉验证中一旦被分到训练集会明显抬高验证集误差一旦被分到测试集验证误差又下降。这说明离群值对模型的稳定性威胁很大。我在交叉验证中加入“按价格分位分层抽样”确保每个折中包含相似比例的极端价格样本。这比单纯做离群值处理更能稳定模型评估。7.5 数据日志是救命稻草最后也是最重要的一个习惯维护数据发现日志。每做一次EDA我就写几行文字记录当时观察到的现象、怀疑的原因以及可能的处理方向。三个星期后回看日志能很快定位很多“当时以为不重要”的细节比如某个字段存在长期不变的默认值、某个区域的租金在特定月份出现跳升。这个习惯让我的复现成本大大降低也是我每次竞赛后期能快速调整方案的原因。8. 从EDA到特征工程的衔接做完完整的EDA后我通常会整理一份“特征工程候补清单”。这份清单分为三部分需要清洗的字段面积截断、朝向归一、户型拆分需要构造的特征区域均值编码、面积分箱、时间距离、空间聚类需要留意的陷阱训练/测试分布差异、目标分布偏态、类别低频标签在实际建模前我会先拿最原始的清洗数据跑一个LightGBM基线记录分数然后按照清单逐步构造特征每增加一组特征就重新跑一遍同样的模型配置观察分数的变化。这样能客观评估每个特征组是否真的有效而不是一股脑把所有特征堆进去。本次竞赛中单纯依靠EDA阶段发现的“目标对数变换”和“区域目标编码”在没有做任何复杂交互特征的情况下我的分数就已经超过了不做的基线模型约8%的RMLSE。这说明EDA在竞赛里的投资回报率非常高远高于后期漫无目的的模型调参。如果非要用一句话总结我对EDA的理解那就是数据探索性分析不是流程而是博弈。你要在数据集中找出哪里藏着信号哪里藏着噪声哪里藏着陷阱。做完这一步后面的建模才谈得上有方向感。下一篇总结我会继续整理特征工程中的实际做法和代码片段尤其是目标编码、时间特征和空间聚类在模型中的具体生效方式。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/17 11:59:45
小白程序员必看:AI Agent安全新挑战,从“模型”到“模型+工具+执行环境”的全链路防护指南
2026/10/17 11:59:45
生命体AI理论视角下:数据驱动范式在具身智能中的失效与工程替代方案
2026/10/17 11:54:45
美赛A题非线性建模与算法嵌入核心考点解析
2026/10/17 15:30:39
影视聚合源码搭建避坑指南:从解压到播放的完整实践
2026/10/17 15:30:39
Claude Code 模板库实战:CLI 配置、MCP 接入与 npm 避坑指南
2026/10/17 15:30:39
高压线下智能防垂钓告警装置:从看得见到喊得出
2026/10/17 15:30:39
Git报错深度解析:从拒绝推送、合并冲突到仓库损坏的根因与修复
2026/10/17 15:30:39
RK3588S硬件设计四大翻车点与信号完整性实战指南
2026/10/17 15:25:38
二元分类为什么可行?从PAC理论到VC维的泛化误差解析
2026/10/17 0:02:58
NetSpot 实战:用信道分析与频宽优化解决 Wi-Fi 卡顿
2026/10/17 0:02:58
C++ split() 字符串分割的五种实现与性能实测
2026/10/17 0:02:58
R语言坐标轴范围设置:从xlim/ylim到coord_cartesian的完整指南
2026/10/17 10:00:11
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/17 10:16:23
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/17 10:16:02
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/17 10:15:50
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/16 18:46:48
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/16 18:46:46
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)