波士顿房价数据集的十三面棱镜:从数据清洗到模型实战的深度解析
每次打开波士顿房价数据集,我都有种面对一个经典谜题的感觉。这个看似简单的数据集,包含了506个样本和13个特征,却成为了机器学习入门者必须跨越的一道门槛。但真正深入其中,你会发现它远不止是一个教学工具——它是一面多棱镜,折射出数据科学从探索到建模的完整思考过程。对于数据分析师和房地产从业者来说,理解这些特征背后的现实含义,远比记住几个模型参数重要得多。
今天,我想带你一起重新审视这个经典数据集,不只是跑一遍代码,而是真正理解每个特征如何影响房价,以及如何构建一个既专业又实用的分析流程。我们会从最基础的数据探索开始,一步步深入到特征工程和模型选择,最后还会讨论如何将这种分析方法应用到实际的房地产评估场景中。
1. 数据初探:理解每个特征的真实含义
在开始任何分析之前,我们必须先理解数据在说什么。波士顿房价数据集中的13个特征,每个都对应着现实世界中的某个社会经济指标。但很多人只是机械地使用这些特征,却忽略了它们背后的实际意义。
CRIM - 城镇人均犯罪率。这个指标直接影响居民的安全感,进而影响房价。但有趣的是,犯罪率与房价的关系并非简单的线性负相关。在某些区域,即使犯罪率较高,如果其他条件(如教育资源、交通便利性)足够优越,房价仍可能保持在一定水平。
ZN - 住宅用地比例(大于25,000平方英尺的地块)。这个特征反映了区域的开发密度和住宅类型。高ZN值通常意味着更多独立住宅,而低值则可能表示公寓或密集住宅区。
INDUS - 非零售业务用地比例。这个指标衡量的是区域的工业化程度。工业区附近的住宅通常价格较低,因为存在噪音、污染等问题。
CHAS - 查尔斯河虚拟变量。这是一个二值特征(1表示临河,0表示不临河)。水景房在任何城市都是稀缺资源,这个简单的0/1变量往往能带来显著的价格溢价。
NOX - 氮氧化物浓度。空气质量指标,直接关系到居住健康。在现代城市中,这个指标的重要性日益凸显。
RM - 每户平均房间数。这是最直观的房屋特征之一,但需要小心解读——房间数多不一定意味着居住舒适,还要考虑房间大小和布局。
AGE - 1940年前建造的房屋比例。这个特征反映了区域的老化程度。老社区可能有历史价值,但也可能面临设施陈旧的问题。
DIS - 到就业中心的加权距离。通勤时间是现代城市居民最关心的因素之一,这个距离指标直接影响房屋的吸引力。
RAD - 高速公路可达性指数。交通便利性对房价的影响是复杂的——太近可能有噪音问题,太远则不便。
TAX - 财产税率。税率直接影响持有成本,是投资决策的重要考虑因素。
PTRATIO - 师生比。教育资源的直接体现,对于有孩子的家庭来说是关键决策因素。
B - 黑人比例系数。这个特征需要特别谨慎对待,因为它涉及种族因素。在分析中,我们更关注其反映的社会经济结构信息。
LSTAT - 低收入人群比例。这个指标往往与房价有最强的相关性,因为它综合反映了区域的经济状况和社会阶层构成。
理解这些特征后,我们开始数据清洗的第一步——检查数据质量。
import pandas as pd
import numpy as np
from sklearn.datasets import load_boston
# 加载数据
boston = load_boston()
df = pd.DataFrame(boston.data, columns=boston.feature_names)
df['PRICE'] = boston.target
# 基础信息检查
print("数据集形状:", df.shape)
print("\n数据类型:")
print(df.dtypes)
print("\n缺失值统计:")
print(df.isnull().sum())
print("\n描述性统计:")
print(df.describe().round(2))
运行这段代码,你会立即发现几个关键信息:数据集没有缺失值,所有特征都是数值型,但数值范围差异很大。比如,CRIM的范围是0.006到88.976,而CHAS只有0和1两种值。这种量纲差异会在后续分析中带来问题,我们需要进行标准化处理。
注意:虽然波士顿房价数据集是经典数据集,但需要注意它发布于1978年,反映的是当时波士顿的房地产市场情况。在实际应用中,应该使用更新的数据,并考虑通货膨胀等因素对价格的影响。
2. 可视化探索:发现特征间的隐藏关系
数据可视化不是为了让报告好看,而是为了发现那些数字表格中难以察觉的模式。对于波士顿房价数据集,我习惯从三个层次进行可视化探索:单变量分布、双变量关系和相关性网络。
2.1 单变量分布分析
每个特征的分布形态都讲述着一个故事。让我们先看看房价本身的分布:
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12, 5))
# 房价分布直方图
plt.subplot(1, 2, 1)
sns.histplot(df['PRICE'], bins=30, kde=True)
plt.title('房价分布直方图')
plt.xlabel('房价(千美元)')
plt.ylabel('频数')
# 房价箱线图
plt.subplot(1, 2, 2)
sns.boxplot(y=df['PRICE'])
plt.title('房价箱线图')
plt.ylabel('房价(千美元)')
plt.tight_layout()
plt.show()
从分布图中,我们可以看到房价大致呈正态分布,但右侧有轻微拖尾,说明存在一些高价房产。箱线图显示有几个可能的离群值,但需要谨慎处理——这些"离群值"可能代表真实的豪宅,而非数据错误。
接下来,我们看看关键特征的分布情况。我特别关注LSTAT和RM,因为经验告诉我这两个特征往往最重要:
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# LSTAT分布
sns.histplot(df['LSTAT'], bins=30, kde=True, ax=axes[0, 0])
axes[0, 0].set_title('低收入人群比例(LSTAT)分布')
axes[0, 0].set_xlabel('LSTAT (%)')
# RM分布
sns.histplot(df['RM'], bins=30, kde=True, ax=axes[0, 1])
axes[0, 1].set_title('平均房间数(RM)分布')
axes[0, 1].set_xlabel('房间数')
# 犯罪率分布(取对数后)
sns.histplot(np.log1p(df['CRIM']), bins=30, kde=True, ax=axes[0, 2])
axes[0, 2].set_title('犯罪率对数分布')
axes[0, 2].set_xlabel('log(CRIM+1)')
# PTRATIO分布
sns.histplot(df['PTRATIO'], bins=30, kde=True, ax=axes[1, 0])
axes[1, 0].set_title('师生比(PTRATIO)分布')
axes[1, 0].set_xlabel('PTRATIO')
# NOX分布
sns.histplot(df['NOX'], bins=30, kde=True, ax=axes[1, 1])
axes[1, 1].set_title('氮氧化物浓度(NOX)分布')
axes[1, 1].set_xlabel('NOX浓度')
# TAX分布
sns.histplot(df['TAX'], bins=30, kde=True, ax=axes[1, 2])
axes[1, 2].set_title('财产税率(TAX)分布')
axes[1, 2].set_xlabel('TAX')
plt.tight_layout()
plt.show()
这些分布图揭示了几个重要发现:
- LSTAT呈右偏分布,大多数区域低收入人群比例较低,但少数区域比例很高
- RM接近正态分布,平均房间数集中在6左右
- 犯罪率严重右偏,取对数后分布更合理
- 师生比在不同区域差异明显
2.2 特征与房价的关系散点图
散点图能直观展示特征与房价的关系。让我们重点关注几个关键特征:
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# LSTAT vs PRICE
sns.scatterplot(x='LSTAT', y='PRICE', data=df, alpha=0.6, ax=axes[0, 0])
axes[0, 0].set_title('低收入比例 vs 房价')
axes[0, 0].set_xlabel('LSTAT (%)')
axes[0, 0].set_ylabel('房价(千美元)')
# RM vs PRICE
sns.scatterplot(x='RM', y='PRICE', data=df, alpha=0.6, ax=axes[0, 1])
axes[0, 1].set_title('房间数 vs 房价')
axes[0, 1].set_xlabel('平均房间数')
axes[0, 1].set_ylabel('房价(千美元)')
# PTRATIO vs PRICE
sns.scatterplot(x='PTRATIO', y='PRICE', data=df, alpha=0.6, ax=axes[0, 2])
axes[0, 2].set_title('师生比 vs 房价')
axes[0, 2].set_xlabel('PTRATIO')
axes[0, 2].set_ylabel('房价(千美元)')
# NOX vs PRICE
sns.scatterplot(x='NOX', y='PRICE', data=df, alpha=0.6, ax=axes[1, 0])
axes[1, 0].set_title('氮氧化物浓度 vs 房价')
axes[1, 0].set_xlabel('NOX浓度')
axes[1, 0].set_ylabel('房价(千美元)')
# DIS vs PRICE
sns.scatterplot(x='DIS', y='PRICE', data=df, alpha=0.6, ax=axes[1, 1])
axes[1, 1].set_title('就业中心距离 vs 房价')
axes[1, 1].set_xlabel('加权距离')
axes[1, 1].set_ylabel('房价(千美元)')
# CRIM vs PRICE(取对数)
sns.scatterplot(x=np.log1p(df['CRIM']), y='PRICE', data=df, alpha=0.6, ax=axes[1, 2])
axes[1, 2].set_title('犯罪率(对数) vs 房价')
axes[1, 2].set_xlabel('log(CRIM+1)')
axes[1, 2].set_ylabel('房价(千美元)')
plt.tight_layout()
plt.show()

&spm=1001.2101.3001.5002&articleId=153106267&d=1&t=3&u=6a9fb1bf574646f09d20385ba6ce327f)
3993

被折叠的 条评论
为什么被折叠?



