ARTICLE DETAIL

资讯详情

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

用加州房价数据入门商业数据分析:从清洗到建模的完整实战

用加州房价数据入门商业数据分析:从清洗到建模的完整实战 简介面向初学者数据分析员的这份PDF文档围绕加州房价数据集(housing.csv)给出完整商业数据分析案例适合对商业智能和假设检验感兴趣的学员。文档从数据基本描述与缺失值确认入手深入探索房价中位数、湾区接近度等关键变量并完成多变量相关性分析针对变量预缩放问题文档对比不同缩放比例并给出处理建议还可引导读者新建“每户人口数”等新特征以提升分析信息量。通过分组计算均值与标准差结合假设检验论证湾区位置对房价中位数的期望差异使读者借助Python工具掌握从数据探索到建模分析的全流程。资源为单个PDF文件压缩包大小为167KB文档结构清晰、步骤完整适合跟随实践。目前已有126人学习是商业数据分析入门者可参考的案例资料。1. 商业数据分析入门从加州房价数据开始商业数据分析入门最怕第一课就把时间耗在调参数、跑模型上。加州房价数据正好能帮你绕开这个问题它只有10来个字段量纲、缺失、异常都差不多齐了而且背后是地段、人口收入、房屋属性如何影响房价这个非常真实的商业问题。整个项目跑下来其实是在练一个分析师最核心的套路先把业务问题拆成可计算的指标再让数据回答影响因素的大小和方向最后把结果放回业务场景里解释。我会按平时做项目的顺序展开适合刚入门数据分析、或已经是开发但想补商业视角的读者。2. 载入数据与业务口径让加州房价数据变成可算的指标第一件要做的事不是pd.read_csv而是先把价格说清楚。加州房价数据里的median_house_value单位是美元统计的是某个区块的中位数房价不是单套成交价。如果把它当成单套房屋价格去建模后面所有解释都会变味。我一般先把字段列表打印出来逐列确认业务含义再动手写代码。下表是这份数据最常见的字段及其口径后续所有特征和预测都围绕这些输入列展开。字段业务含义单位/取值建模时怎么用longitude / latitude区块中心经纬度十进制数转成地理特征不能直接当数值用housing_median_age区块内房屋年龄中位数年可做分箱或原样输入total_rooms区块内总房间数间结合人口做人均间数total_bedrooms区块内总卧室数间缺失较多需处理population区块总人口人和总收入配对计算households区块内家庭数户构造户均人口median_income区块内家庭收入中位数万美元通常已缩放最重要的连续特征median_house_value目标变量美元预测的 y2.1 先固定价格口径再做载入看这张表时会发现median_house_value在很多公开版本里被截断到上限附近通常是一串类似 500001 的数字。这带来的直接后果是如果直接拿它做回归模型会把所有高于上限的样本都当成同一个数从而低估高价区域。处理方式通常有两种一是把截断样本单独标记在业务上另列一个是否超上限的二值特征二是用专门处理截断的模型但对入门阶段太复杂。最务实的做法是先给这些样本打一个标记列保留在训练集里但不要把它们的预测值当成精确价格。确定口径之后载入代码就变成了一段很短的惯例import pandas as pd df pd.read_csv(california_housing.csv) print(df.shape) print(df.head()) # 给截断值打标业务上超上限和刚好上限是两回事 df[is_price_capped] (df[median_house_value] 500001).astype(int) # 先看缺失率不急着填充 miss_rate df.isnull().mean().sort_values(ascendingFalse) print(miss_rate[miss_rate 0])这段代码里read_csv之后先看行列数和前几行是为了确认列名和单位没有因来源不同而变样。is_price_capped把价格截断变成显式标记后面建模时可以作为特征也可以用来单独评估子集表现。最后输出缺失率而不是直接填充是因为total_bedrooms这类字段的缺失可能隐含小户型区块没有统计需要结合业务判断。2.2 缺失值、异常值与不该删的样本入门项目最容易犯的错误是把total_bedrooms的缺失值行整行删掉。实际处理时我会先算一下缺失率如果只是少量随机缺失用中位数填充就够如果缺失集中在某个地理区域就不能全局填充而要按经纬度分组后取组内中位数。对于异常值total_rooms除以households得到一个户均房间数如果大于 10 或小于 1先查原始数据是不是口径错了而不是直接删。# 按地理分桶填充先把经纬度切成 20 段再拼接成桶 df[geo_cluster] pd.cut(df[longitude], bins20).astype(str) _ \ pd.cut(df[latitude], bins20).astype(str) df[total_bedrooms] df.groupby(geo_cluster)[total_bedrooms] \ .transform(lambda x: x.fillna(x.median()))这段逻辑是用pd.cut把经纬度分别切成 20 个区间拼接成地理分桶再在分桶内部填充缺失值替代全局中位数。transform保证填充后的 DataFrame 索引不变方便原地赋回。切 20 桶是经验值样本量少时可以降到 10保证每个桶至少有几十个样本落进来避免中位数因为样本太少而噪声过大。填充完可以再用df.isnull().sum()复查一遍如果仍有缺失说明某些桶内全是空值那就退回全局中位数。2.3 数据类型的隐性问题经纬度不要当普通数字另一个入门阶段容易踩的坑是longitude和latitude看起来是浮点数直接塞进线性模型也能跑但位置诱导的房价变化是非线性的。湾区、洛杉矶和内陆山谷的房价差距来自地理区块属性而不是经纬度的线性单调关系。因此我会把这两个字段拆出两种用法一是保留原始值给树模型做切分二是基于经纬度构造地理分桶特征。入门阶段更简单的方式是先保留经纬度同时加入geo_cluster作为类别特征后面在特征工程章节再详细展开。提示不要用df.describe()里的数值范围去判断经纬度是否异常。加州的经度在 -124 到 -114纬度在 32 到 42这本来就是真实分布不是离群点。到这一步你已经得到的是一份口径清楚、缺失填好、价格上限被标记的干净表格。这个阶段花掉整个项目 30% 的时间是正常的后续建模再快也弥补不了脏数据导致的错误商业结论。3. 特征工程与洞察从人口、收入、地理位置里拆出房价解释变量数据清洗完之后不要马上跑模型。商业数据分析的核心步骤是让解释变量有明确的业务含义这一步也叫特征工程。加州的房价数据天然自带这样一个优势它包含的社会经济变量收入、人口、家庭数和地理变量经纬度、房龄都能直接映射到住在这里的人是谁、住得怎么样的商业叙事。在做建模前我会先做一个相关性矩阵目的是找到单变量与房价关系最强的字段避免一开始就陷入复杂模型。3.1 用相关性矩阵找初步解释力numeric_cols df.select_dtypes(include[number]).columns corr df[numeric_cols].corr()[median_house_value].sort_values(ascendingFalse) print(corr)这里select_dtypes排除geo_cluster这种字符串列corr()默认用皮尔逊相关系数评估的是线性相关性。在公开的加州房价数据上median_income通常是唯一一个相关系数超过 0.6 的字段其他如房间数、家庭数都是靠合成特征才能提高解释力。这说明两件事第一模型的起点应该放在收入上第二单纯把total_rooms、population都塞进模型不会带来太多增量反而会增加多重共线性风险。如果是用 Jupyter 而不是纯脚本pd.plotting.scatter_matrix可以画散点矩阵但字段一多就看不清楚。我一般只挑median_income、median_house_value、latitude三个字段做两两散点因为latitude能直观看到南北加之间的价格分层。3.2 构造有可解释性的合成特征人均而不是总量原始字段里有很多总量字段但商业决策更关心密度。total_rooms大不一定代表居住密度高可能是这个区块以大户型的独栋为主。所以我会构造这三个比例特征df[rooms_per_household] df[total_rooms] / df[households] df[bedrooms_per_room] df[total_bedrooms] / df[total_rooms] df[people_per_household] df[population] / df[households]这三个特征分别回答三个问题房屋大不大房间数/户数卧室密度高不高卧室数/房间数住得挤不挤人口/户数。它们比原始字段更接近居住形态这个真实概念而且量纲统一在 1 附近线性模型更容易收敛。构造完之后我习惯用df[[median_house_value] 这三个列].corr()再跑一次相关性确认这些比例特征没有偷走原始字段的所有信息。3.3 对房龄、收入做分箱把连续变量改成业务段位连续数值直接进模型时默认的是每增加一个单位房价变化一个固定斜率这对收入并不成立。收入在 2 万和 5 万之间的房价增量远大于 8 万和 11 万之间的增量。所以我习惯把收入分成几个业务档位2.5 万以下、2.5-5 万、5-7.5 万、7.5-10 万、10 万以上然后做 one-hot 或 ordinal。同样房龄也可以分成新房10年内主力房20-50年老房50年以上。分箱不是越多越好。过细的区间会让某些类别在后续交叉分析时样本太少过粗又会丢掉曲线形态。入门阶段先用 4-6 个区间再看每个区间里的平均房价确认单调性是否和业务直觉一致。如果出现收入越高房价反而下降的异常箱先回到数据里看该箱的样本量通常是因为样本太少或包含被截断的异常值。商用落地时不要把这个分箱阈值写死应该把它和模型注册表放在一起方便后续版本回溯。3.4 经纬度怎么用分桶、局部均值与聚类编码把geo_cluster直接作为类别特征是一种方式但它太粗糙20×20 分桶在数据稀疏时会产生大量空桶。更稳妥的做法是用局部均值编码对每个桶计算历史平均房价把这个平均值作为一个特征给到模型。这会带来标签泄漏风险必须用交叉验证内部分开计算入门阶段也可以简化为用KMeans对经纬度做聚类把簇号作为类别特征。from sklearn.cluster import KMeans kmeans KMeans(n_clusters15, random_state42, n_initauto) df[region_id] kmeans.fit_predict(df[[longitude, latitude]])n_clusters15是把加州按地理位置聚成 15 个片区fit_predict给每个样本打上片区标签。这个特征进来之后等于告诉模型这个位置属于南加沿海还是中央山谷。实际使用中可以把n_clusters调成 20-30 看验证集变化如果提升不明显就保留 15避免过拟合。还要注意KMeans对尺度敏感经纬度本身已经处于同样的度量尺度度数所以不需要标准化这是特征工程里一个少见的例外。完成这一步后模型可用的特征扩展到十几个人均类特征 3 个分箱类别 2 个区域编号 1 个再加上原始 8 个字段。接下来要做的不是继续堆特征而是用一版简单模型评估它们的实际贡献。4. 用线性回归和随机森林跑通一版基准模型参数怎么设、指标怎么读特征工程做完就可以进入建模。商业数据分析的建模和算法比赛不一样目的不是为了把 RMSE 压到最低而是搞清楚哪些因素在解释房价差异、解释力度有多强。所以我会同时做两个模型一个线性回归作为可解释基线一个随机森林作为非线性对照。如果线性回归已经能解释 60% 以上的方差通常不需要太重的高阶模型如果差距明显说明变量之间确实有非线性关系再上树模型。4.1 训练集和测试集按区域切分避免同区信息泄漏做切分前先想一个问题同一套数据里相邻经纬度的样本环境高度相似随机切分会让模型记住地理位置而不是学到规律。我一般会先按geo_cluster分组用GroupShuffleSplit来切分而不是默认的train_test_split。入门项目如果不想引入额外复杂度至少也要设置random_state固定并记录切分结果否则后续修改特征后很难对比效果。from sklearn.model_selection import train_test_split feature_cols [ longitude, latitude, housing_median_age, median_income, rooms_per_household, bedrooms_per_room, people_per_household ] X df[feature_cols].copy() y df[median_house_value] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(train size:, X_train.shape, test size:, X_test.shape)这里先用 7 个字段做一版最小模型。test_size0.2是常见的 80/20 切分如果样本量只有几千建议用test_size0.25或交叉验证保证测试集上的误差估计更稳定。random_state42只是固定种子换成其他数字结果差别不大但必须有。真正到商业环境里我会把这次切分的索引文件另存下来以便以后加入新数据时做等长对比。4.2 线性回归基线标准化、共线性与系数解释线性回归对特征尺度敏感度不高但如果你后续要做Lasso或Ridge就必须标准化。至少对树模型以外的模型我会统一加一个StandardScaler避免收入和房龄量纲差异造成数值问题。同时检查一下特征间的相关性特别是rooms_per_household和people_per_household之间若不放心可以做一个方差膨胀因子检验但入门阶段可以直接观察相关性矩阵。from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error, r2_score model_lr make_pipeline(StandardScaler(), LinearRegression()) model_lr.fit(X_train, y_train) y_pred_lr model_lr.predict(X_test) print(MAE:, mean_absolute_error(y_test, y_pred_lr)) print(R2:, r2_score(y_test, y_pred_lr)) # 查看系数方向是否符合业务直觉 coef model_lr.named_steps[linearregression].coef_ for col, c in zip(feature_cols, coef): print(f{col}: {c:.1f})make_pipeline把标准化和回归串起来fit时会先在训练集上计算均值和方差再用同一组参数变换测试集避免测试集信息混入训练过程。MAE是直接的单位误差能让你理解平均差多少钱R2则是模型解释力。系数方向很关键正常情况下median_income系数为正、latitude系数为负加州北部大多数项目相对便宜如果方向反了说明前面清洗时把字段单位搞错了或者存在严重的共线性。4.3 随机森林对照先设n_estimators和max_depth随机森林在表格数据上通常比线性回归好但代价是解释性下降。我用它主要做两件事一是验证线性模型是否有捕捉不到的非线性二是用feature_importances_反向检查特征构造有没有用。参数设置上先固定n_estimators500max_depth控制在 15 以内其余用默认值。n_estimators过大只会增加训练时间500 棵树足够稳定max_depth限制单棵树深度避免模型为记忆一个异常样本无限分裂。from sklearn.ensemble import RandomForestRegressor model_rf RandomForestRegressor( n_estimators500, max_depth15, min_samples_leaf5, random_state42, n_jobs-1 ) model_rf.fit(X_train, y_train) y_pred_rf model_rf.predict(X_test) print(RF MAE:, mean_absolute_error(y_test, y_pred_rf)) print(RF R2:, r2_score(y_test, y_pred_rf)) for name, imp in zip(feature_cols, model_rf.feature_importances_): print(f{name}: {imp:.3f})min_samples_leaf5强制叶子节点至少要有 5 个样本是防止过拟合的常见做法。n_jobs-1告诉模型用满所有 CPU 核。特征重要性输出后如果某个没人见过的合成特征比如bedrooms_per_room排很靠前说明它对预测有实质贡献如果原始字段全部排后面则需要回到特征工程审视是否构造出了噪声。4.4 残差落在哪里用空间分布发现模型盲区训练结束后比指标更有价值的是残差分析。把y_test - y_pred_rf按经纬度画成散点图能直接看出模型在哪些地理片区系统性高估或低估。我通常的做法是把测试集预测结果和经纬度放进一个临时 DataFrame按残差绝对值分组超过 10 万美元的标成高误差统计高误差样本集中在哪些region_id并回去查这些区域的平均房龄、收入判断是数据稀疏还是特征缺失。这一步能给出直接的商业建议如果你要为一个新的在建楼盘估值但这个楼盘位于模型高误差地带那么模型输出只能作为粗筛需要人工实地校准。残差空间化是商业数据分析与普通机器学习练习的分水岭前者必须把模型失败的地方翻译成业务风险。5. 用模型结果给业务一个可操作的答案从预测价格到分析结论最后一章不再追求调参而是把模型翻译成业务口径。你在交付时可能面对的是运营、投资或数据产品负责人他们不关心R2更关心哪个因素每上升一档房价大约变化多少。所以我会把回归系数转成业务语言同时给出验证清单防止后续别人重复使用时踩坑。5.1 把回归系数做成每单位影响的表格用线性回归时系数可以直接作为解读。注意由于做了标准化系数代表的是该特征变化一个标准差时房价的变化。如果要看原始单位需要对比每个特征的均值差。给业务看时不如做一个简易对照表比如收入从 3 万到 6 万美元时模型预测房价变化的幅度。import numpy as np baseline X_train.median().to_dict() income_lo baseline.copy(); income_lo[median_income] 3.0 income_hi baseline.copy(); income_hi[median_income] 6.0 pred_lo model_lr.predict(pd.DataFrame([income_lo]))[0] pred_hi model_lr.predict(pd.DataFrame([income_hi]))[0] print(f收入从 3 万到 6 万房价预测变化: {pred_hi - pred_lo:,.0f} 美元)切换到income_lo和income_hi时其他特征统一用训练集中的中位数这叫单变量边际测算。它反映的是在其他条件相同时收入档位的价格影响是向业务解释回归系数最直观的方式。这个表格可以直接导出到 Excel 作为交付物的一部分。5.2 三个隐藏的业务坑截断、共线性、不可外推第一价格截断导致的高价区域被低估。模型预测值不应该被当作精确报价而是看作在当前样本分布内的排序估计。第二median_income和地理位置高度相关某个系数可能吸收了片区的影响不要对它的绝对值过度解读。第三模型只适用于加州范围内类似形态的区块推广当你把预测应用到全新开发的区域时特征范围可能超出训练数据树模型会给一个平庸的平均值而不是合理外推。5.3 给分析师的快速验证清单最后分享一个我每次交模型成果前都会过一遍的清单重新跑一次df.info()和df.describe()确认没有把字符串列漏进模型检查train_test_split的随机种子是否固定能不能复现看训练集和测试集的目标变量均值是否接近如果差异超过 10%说明切分不随机对特征系数/重要性做一次方向检查反向的必须有解释不能带着模型自己学的就交付找一个高误差区域人工调查一下该区域最近有没有新建大型基础设施这些无法从静态数据中看到的变化是模型误差的来源。把这个清单固化成一个脚本每次拿到新数据都自动跑一遍你就把一次性的项目变成了可持续迭代的日常分析流程。本文还有配套的精品资源点击获取
返回列表