波士顿房价数据分析项目实战
一、项目背景
该项目是给Ashely同学写的基础入门教程,所以会尽量避免涉及机器学习算法,尽可能的简单易懂,方便其快速上手。
波士顿房价数据(Boston Housing Dataset)是一个经典的房价数据集,包含了波士顿不同地区的一些特征信息(例如房屋面积、房产税率、空气污染指数、周边犯罪率等),以及相应的房价中位数。我们将以此数据集作为示例,进行一次基础的数据分析项目,体验完整的数据分析流程。
本次项目的主要目标:
- 理解数据结构:包括数据集中有哪些列,每列代表的含义,以及数据的基本分布情况等。
- 数据清洗与预处理:检查缺失值、异常值等,保证数据质量。
- 探索性数据分析(EDA):进行可视化和统计分析,初步了解变量之间的关系、分布特点等。 本次项目会尽量避免机器学习的内容,更多聚焦于数据本身的清洗、统计和可视化分析上。
开发环境的安装
- 执行下面命令,用于安装项目依赖的库函数
!pip install -r requirements.txt
- 前面有一个感叹号,代表这行代码是一个命令行命令,而不是 Python 代码
- 这行代码的作用是安装项目依赖的库函数,具体来说是安装 requirements.txt 文件中列出的所有库函数
- 安装完成后,就可以在项目中使用这些库函数了
Python库函数入门指南
Ashely同学看黑板(敲一敲):
在开始数据分析的旅程前,让我先为你介绍一下Python中的"库函数"。想象一下,如果把Python比作一个巨大的工具箱,那么这些库函数就像是里面不同功能的工具。我们不需要每次都自己制造工具,只要通过import
语句,就能直接使用这些现成的强大工具。
为什么要使用库函数?
想象你要在Excel里处理数据时,会用到"求和"、"平均值"等函数,这些都是Excel内置的工具。在Python中,库函数就起着类似的作用,但功能更加强大和灵活。
我们项目中用到的主要库函数
1. pandas (pd)
pandas就像是一个超级强大的Excel,专门用来处理表格数据。
python
import pandas as pd
# 例子:创建一个简单的成绩表
成绩表 = pd.DataFrame({
'姓名': ['小明', '小红', '小华'],
'语文': [85, 92, 78],
'数学': [95, 88, 82]
})
print(成绩表)
2. numpy (np)
numpy就像是一个超级计算器,特别擅长处理数学计算。
python
import numpy as np
# 例子:计算班级平均分
班级分数 = [85, 92, 78, 95, 88]
平均分 = np.mean(班级分数)
print(f"班级平均分是:{平均分}")
3. matplotlib.pyplot (plt)
matplotlib就像是一个画图工具,可以把数据变成图表。
python
import matplotlib.pyplot as plt
# 例子:画出销售趋势图
月份 = ['1月', '2月', '3月', '4月']
销量 = [120, 150, 180, 210]
plt.plot(月份, 销量)
plt.title('月度销售趋势')
plt.xlabel('月份')
plt.ylabel('销量')
plt.show()
类比
-
pandas就像一个图书管理员:
- 可以帮你整理、查找、排序大量的数据
- 就像图书管理员知道每本书在哪个架子上一样
-
numpy就像一个数学天才:
- 可以快速进行复杂的数学运算
- 比如计算100个数字的平均值,只需一行代码
-
matplotlib就像一个画家:
- 能把枯燥的数字变成生动的图表
- 帮助我们更直观地理解数据
小贴士
- 我们用
import pandas as pd
这样的写法,是给pandas起了个简短的别名"pd",这样后面使用时就不用每次都写完整的名字了,就像给好朋友起昵称一样。 - 在代码中看到
plt.
、np.
、pd.
开头的命令,就表示我们在使用这些库提供的功能。
python
# pandas 是一个处理表格数据(DataFrame)的常用库
import pandas as pd
# numpy 是常用的数值运算库
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# 设置全局字体为支持中文的字体
# 你是windows系统,所以使用SimHei字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用SimHei
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # MacOS系统
# plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei'] # Linux系统
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False
读取数据
python
# 通常我们会有一个 CSV文件 从中获取波士顿房价数据
df = pd.read_csv('house_data.csv')
# 现在 df 就是一个 DataFrame 类型的数据表格
df.head() # 查看前 5 行数据
输出结果如下:
初步了解数据的维度、列信息和数据类型
python
# 查看数据的形状(行数 x 列数)
print("数据集的行数和列数:", df.shape)
# 查看所有列及其对应的数据类型
print("\n列名和数据类型:")
print(df.dtypes)
# 查看各个列的简要统计描述
print("\n各列的统计摘要:")
df.describe()
输出结果如下:
df.describe()
就不在此贴出来啦,自己上notebook上看
特征变量含义
现在我们知道了数据的各行各列都是些什么东西了,但是具体是表示什么含义呢?接下来我解释一下各个特征变量的含义:
基础特征
-
CRIM:该地区的人均犯罪率
- 反映了社区的安全程度
- 通常与房价呈负相关关系
-
ZN:占地面积超过25000平方英尺的住宅用地比例
- 反映了区域的开发密度
- 较高的比例通常意味着更多的低密度住宅区
-
INDUS:每个城镇非零售商业用地的比例
- 反映了工业化程度
- 可能影响居住环境质量
环境特征
-
CHAS:是否临近查尔斯河
- 1 = 临近
- 0 = 不临近
- 临近水域通常被视为较好的居住环境
-
NOX:一氧化氮浓度(每千万份中的部分)
- 重要的空气质量指标
- 反映了环境污染程度
房屋特征
-
RM:每栋住宅的平均房间数
- 直接反映房屋大小
- 是最重要的价格影响因素之一
-
AGE:1940年之前建成的自住单位比例
- 反映建筑年代
- 可能影响房屋维护成本
位置特征
-
DIS:到波士顿五个就业中心的加权距离
- 反映通勤便利性
- 对房价有显著影响
-
RAD:到径向公路的可达性指数
- 反映交通便利程度
- 影响生活便利性
经济因素
-
TAX:每一万美元的房产税率
- 直接影响房屋持有成本
- 较高税率可能降低房价
-
PTRATIO:城镇师生比例
- 反映教育资源质量
- 是购房者重要考虑因素
人口统计学特征
-
B:1000(Bk - 0.63)^2
- 其中Bk是城镇中黑人的比例
- 反映种族构成情况
-
LSTAT:低收入人群的比例
- 反映邻里社会经济状况
- 通常与房价呈负相关
目标变量
- MEDV :自住房的中位数价值
- 单位:千美元
- 这是我们要分析的目标变量
数据清洗与预处理
做完上面的事情,你仔细思考一下这个问题---我们是不是遗漏了什么?很明显,我们拿到 数据,不管三七二十一直接开始用了,这是非常不好的习惯!我们应该先对数据进行一些处理,让它符合我们进行分析的标准,这才是正确的顺序!
为什么需要数据清洗?
想象你在整理一个Excel表格,可能会遇到:
- 有些单元格是空的(缺失值)
- 有些行的内容重复了(重复值)
- 有些数据明显不对(异常值)
这些问题如果不处理,就会影响我们后面的分析结果。
我们要检查什么?
-
缺失值检查
- 查看是否有空值
- 如果有,需要决定是删除还是填补
-
重复值检查
- 查看是否有完全相同的数据行
- 重复的数据可能会影响分析结果
-
异常值检查(这个我们后面会用可视化的方式来看)
- 比如房价不可能是负数
- 房间数不可能是小数
处理方法
-
对于缺失值:
- 可以直接删除(如果缺失很少)
- 可以用平均值填充
- 可以用相似数据的值填充
-
对于重复值:
- 通常直接删除重复行
- 但要先确认是否是真的重复
让我们开始动手清洗数据吧!
python
# 数据清洗与预处理:检查缺失值、重复值
# 1)检查缺失值
print("缺失值情况:")
print(df.isnull().sum())
# 一般如果有缺失值,可以考虑删除或填补。这里我们先查看是否存在缺失值。
# 如果不存在缺失值,就不需要额外处理。
# 2)检查重复值
# 对于绝大多数数值型数据来说,不常见完全重复的行,但还是可以检查一下:
duplicates = df.duplicated()
print("\n是否存在重复行:", duplicates.any()) # any() 若有一个True就返回True
if duplicates.any():
# 如果有重复行,可以考虑删除
df = df.drop_duplicates()
print("删除重复行后,数据形状:", df.shape)
else:
print("太好啦!没有发现重复行。")
运行后的结果:
统计分析
通过上面的操作,我们已经对数据有了基本的了解,并且也对其进行了清洗和预处理。现在让我们通过图表的形式,进行一些统计学上的分析! 这里我们主要从以下几个方面来了解数据:
- 各列的大致分布,如均值、中位数、标准差
- 房价(MEDV)的分布和一些有意义的特征关系
- 有些特征之间可能存在强相关
python
# 1) 直接查看房价(MEDV)的分布情况
plt.figure(figsize=(10,5))
plt.hist(df['MEDV'], bins=30, color='skyblue', edgecolor='black')
plt.title("房价(MEDV)分布直方图")
plt.xlabel("房价中位数")
plt.ylabel("频数(出现次数)")
plt.show()
# 2) 查看其它一些重要特征的分布,如 'CRIM'(犯罪率)、'RM'(平均房间数)
# 为了演示,这里举例只画一个特征的分布,其他特征可以相同方式自行分析
plt.figure(figsize=(10,5))
plt.hist(df['CRIM'], bins=30, color='salmon', edgecolor='black')
plt.title("犯罪率(CRIM)分布直方图")
plt.xlabel("犯罪率")
plt.ylabel("频数")
plt.show()
结果显示如下:
波士顿房价分布特征分析
从直方图中我们可以观察到以下特点:
-
分布形状
- 整体呈现不对称的分布
- 有明显的右偏(正偏)特征
- 在20-25千美元区间出现最高峰
-
数据集中趋势
- 大多数房价集中在15-35千美元之间
- 最集中的区域在20-25千美元附近
- 中位数约在20千美元左右
-
异常值特征
- 在50千美元附近有一个小峰值
- 这可能表示存在一些高端房产
- 这些高价房产可能位于特殊位置或具有特殊特征
-
价格区间分布
- 低价区(<15千美元):较少
- 中价区(15-35千美元):最常见
- 高价区(>35千美元):逐渐减少,但在50千美元处有小幅上升
-
市场启示
- 波士顿房市以中等价位房产为主
- 存在一定数量的高端房产市场
- 低价房产相对较少
犯罪率(CRIM)分布特征分析
从直方图中我们可以观察到以下特点:
-
高度偏斜分布
- 呈现极度右偏(正偏)的分布形态
- 绝大多数区域的犯罪率都集中在较低水平
- 在接近0的位置有一个非常高的峰值
-
数据集中特征
- 约350个地区的犯罪率都非常低(接近0)
- 随着犯罪率增加,地区数量急剧减少
- 大部分地区的犯罪率都在20以下
-
长尾特征
- 图形右侧有一个很长的尾部
- 少数地区的犯罪率明显高于平均水平
- 最高犯罪率接近80,但这种情况非常罕见
-
社会意义
- 波士顿大多数地区的治安状况良好
- 存在少数犯罪率较高的"问题区域"
- 这种分布对房价可能有显著影响
-
数据分析建议
- 由于分布极度偏斜,在后续分析中可能需要考虑数据转换
- 对异常高值的区域可能需要特别关注
- 在建模时需要考虑这种非正态分布的特征
进一步的可视化分析:关系分析
python
# 数据分析中,一个常用的手段就是查看不同特征之间是否存在关联性。
# 我们可以用散点图(Scatter Plot)来查看两个连续数值型变量之间的关系。
# 比如,房间数(RM) 与 房价(MEDV) 之间是否有关系。
plt.figure(figsize=(8,6))
plt.scatter(df['RM'], df['MEDV'], alpha=0.6, color='teal')
plt.title("平均房间数(RM) vs. 房价(MEDV)")
plt.xlabel("平均房间数(RM)")
plt.ylabel("房价中位数(MEDV)")
plt.show()
平均房间数与房价关系分析
从散点图中我们可以观察到以下重要特征:
-
明显的正相关关系
- 随着房间数的增加,房价总体呈上升趋势
- 这种关系呈现出较为明显的线性特征
- 说明房间数是影响房价的重要因素之一
-
数据分布特征
- 大多数房屋的平均房间数在5-7间之间
- 房价主要集中在10-35千美元区间
- 数据点的密度在中间区域最高
-
特殊点分布
- 在房价50千美元附近有一条"天花板"
- 存在一些离群点,特别是在高房间数区域
- 少数房屋即使房间数较多,房价也可能较低
-
趋势变化
- 在房间数较少时(<6),房价变化相对平缓
- 在房间数较多时(>7),房价上升更快
- 这表明房价与房间数的关系可能不是完全线性的
-
实际意义
- 房间数可以作为预测房价的重要指标
- 但仅凭房间数无法完全解释房价变化
- 可能还需要考虑其他因素(如位置、年代等)
相关系数矩阵(Correlation Matrix)
python
# 相关系数矩阵可以让我们快速查看各个变量之间的相关程度。
# 相关系数在 -1 和 1 之间,越接近 1 表示正相关,越接近 -1 表示负相关,接近 0 表示弱相关。
corr_matrix = df.corr()
plt.figure(figsize=(12, 10))
sns.heatmap(corr_matrix, annot=False, cmap='YlGnBu')
plt.title("相关系数矩阵(Heatmap)")
plt.show()
# 我们可以重点关注 MEDV 那一列(或那一行),看看哪个特征和房价相关性最强。
corr_with_MEDV = corr_matrix['MEDV'].sort_values(ascending=False)
print("与房价(MEDV)相关性由高到低排序:\n", corr_with_MEDV)
相关系数矩阵分析
这个热力图展示了各个特征之间的相关性,颜色越深表示相关性越强,颜色越浅表示相关性越弱。
-
与房价(MEDV)的相关性
- 最强正相关:RM(房间数),呈现深蓝色
- 最强负相关:LSTAT(低收入人群比例),呈现浅色
- 其他显著相关:
- PTRATIO(师生比):负相关
- NOX(空气污染):负相关
- CRIM(犯罪率):负相关
-
特征间的重要相关性
- TAX(房产税)和RAD(公路可达性)强相关
- NOX(空气污染)和INDUS(工业用地比例)强相关
- AGE(房龄)和DIS(到就业中心距离)有一定相关性
-
关键发现
- 房价与房屋物理特征(如房间数)呈正相关
- 房价与环境问题(污染、犯罪)呈负相关
- 房价与社会经济因素(低收入人群比例)呈负相关
-
数据建议
- 在建模时需要注意多重共线性问题
- 可以考虑选择相关性强且互不相关的特征
- 特别关注与房价强相关的几个关键变量
-
实际意义
- 房价受多个因素综合影响
- 物理特征、环境因素和社会因素都很重要
- 这些相关性可以帮助购房者更好地评估房产
参数解析:
波士顿房价相关性分析
各特征与房价(MEDV)的相关性排序(从高到低):
特征变量 | 相关系数 | 相关性说明 |
---|---|---|
MEDV | 1.000000 | 完全相关(房价本身) |
RM | 0.695360 | 强正相关(房间数量) |
ZN | 0.360445 | 弱正相关(住宅用地比例) |
B | 0.333461 | 弱正相关(黑人比例) |
DIS | 0.249929 | 弱正相关(距离就业中心的加权距离) |
CHAS | 0.175260 | 极弱正相关(是否临近查尔斯河) |
AGE | -0.376955 | 弱负相关(建筑年龄) |
RAD | -0.381626 | 弱负相关(公路可达性) |
CRIM | -0.388305 | 弱负相关(犯罪率) |
NOX | -0.427321 | 中等负相关(一氧化氮浓度) |
TAX | -0.468536 | 中等负相关(财产税率) |
INDUS | -0.483725 | 中等负相关(非零售业务比例) |
PTRATIO | -0.507787 | 中等负相关(师生比例) |
LSTAT | -0.737663 | 强负相关(低收入人群比例) |
相关性强度参考标准:
- 相关系数 > 0.7:强相关
- 0.4 < 相关系数 ≤ 0.7:中等相关
- 0.2 < 相关系数 ≤ 0.4:弱相关
- 相关系数 ≤ 0.2:极弱相关
分箱分析或箱线图(Box Plot)- 检查异常值、分布特征
python
# 以房价(MEDV)为例,画一个箱线图(Box Plot)。
# 箱线图能帮助我们判断数据的四分位数、是否有离群点等。
plt.figure(figsize=(8,6))
sns.boxplot(y=df['MEDV'], color='lightblue')
plt.title("房价(MEDV)的箱线图")
plt.ylabel("房价中位数(MEDV)")
plt.show()
# 我们也可以同时对多个特征做箱线图,但通常情况下,先针对关键特征逐一查看。
房价箱线图分析
箱线图展示了房价数据的分布特征和异常值情况:
-
核心分布区间
- 箱体(第一四分位数到第三四分位数)大约在17-25千美元之间
- 中位数(箱体中的横线)约在21千美元
- 这表示50%的房价都集中在这个区间
-
离群值特征
- 上方有多个离群点,主要集中在40-50千美元区间
- 这些可能是高档住宅区或特殊位置的房产
- 离群值的存在表明房价分布有明显的右偏特征
-
分布对称性
- 箱体中位线不在箱体中央
- 上下须线长度不等
- 这表明房价分布不是完全对称的
-
数据范围
- 正常范围(不包括离群值):约5-35千美元
- 总体范围(包括离群值):约5-50千美元
- 价格跨度较大,反映了房市的多样性
-
实际意义
- 大多数房产价格较为集中
- 存在明显的高端房产市场
- 极低价房产相对较少
分组对比分析(假设有分类变量时)
python
# 在波士顿房价数据集中,大多数特征都是连续数值,没有明显的分类变量。
# 如果有,比如你希望查看在不同 "CHAS"(查尔斯河) 取值(0 或 1)下,房价分布如何,
# 可以使用分组对比分析。
# CHAS: 是否靠近查尔斯河(1表示是,0表示否)
plt.figure(figsize=(10,6))
sns.boxplot(x=df['CHAS'], y=df['MEDV'], palette='Set2')
plt.title("是否靠近查尔斯河与房价的关系")
plt.xlabel("CHAS (0 = 不靠近, 1 = 靠近)")
plt.ylabel("房价中位数(MEDV)")
plt.show()
# 通过这个箱线图,可以对比靠近和不靠近查尔斯河的两个组在房价上的差别。
查尔斯河临近性与房价关系分析
这个箱线图比较了临近查尔斯河(CHAS=1)和不临近查尔斯河(CHAS=0)的房价分布情况:
-
中位数比较
- 临近查尔斯河的房价中位数约为23千美元
- 不临近查尔斯河的房价中位数约为21千美元
- 临近河边的房价普遍更高
-
价格分布范围
- 临近河边(CHAS=1):
- 箱体范围更大,约20-33千美元
- 价格波动更大
- 不临近河边(CHAS=0):
- 箱体范围较小,约17-25千美元
- 价格相对更稳定
- 临近河边(CHAS=1):
-
离群值特征
- 两类位置都存在高价离群值
- 不临近河边的房产有更多的离群值点
- 最高价格都接近50千美元
-
分布形态
- 临近河边的房价分布更分散
- 不临近河边的房价分布更集中
- 两种情况都呈现右偏分布
-
市场启示
- 临近查尔斯河确实会带来房价溢价
- 但位置因素并不是决定房价的唯一因素
- 其他因素(如房屋条件)可能影响更大
总结
至此,我们已经完成了第一阶段的学习。在这个教程中:
- 我们初步了解了波士顿房价数据的结构、各列含义以及分布情况。
- 做了简单的数据清洗,发现数据中并没有缺失值,也没有重复值。
- 通过可视化和相关性分析,了解到以下有趣的现象:
- 房间数(RM)和房价(MEDV)之间存在明显的正相关。
- LSTAT(低收入人群比例)和房价(MEDV)有显著的负相关。
- 其他一些特征,如犯罪率(CRIM),对房价也有一定影响,但强度不及RM和LSTAT。
- 在没有深入机器学习的情况下,我们也可以通过 EDA(探索性数据分析) 找到很多关于房价的影响因素。