美国加州房价数据分析01

1.项目简介

本数据分析项目目的是分析美国加州房价数据,预测房价中值。

环境要求:

anconda+jupyter notebook+python3.10.10

虚拟环境:

pandas == 2.1.1

numpy == 1.26.1

matplotlib == 3.8.0

scikit-learn==1.3.1

2. 导入并探索数据集

通用的数据分析前置导入和设置代码

复制代码
# 设置支持python2和python3`
`from __future__ import division, print_function, unicode_literals`

`# 项目所需的第三方库`
`import numpy as np`
`import pandas as pd`
`import os`

`# 设置随机数种子`
`np.random.seed(42)`

`# To plot pretty figures`
`%matplotlib inline `
`#内嵌图片显示`
`import matplotlib as mpl`
`import matplotlib.pyplot as plt`
`mpl.rc('axes', labelsize=14)`
`mpl.rc('xtick', labelsize=12)`
`mpl.rc('ytick', labelsize=12)`

`plt.rcParams['font.sans-serif']` `=` `'SimHei'`
`plt.rcParams['axes.unicode_minus']` `=` `False`

`# 定义生成图例的保存路径`
`PROJECT_ROOT_DIR =` `"."`
`CHAPTER_ID =` `"fundamentals"`

`def` `save_fig(fig_id, tight_layout=True):`
    `# path = os.path.join(PROJECT_ROOT_DIR, "plt_images", CHAPTER_ID, fig_id + ".png")`
`    path = os.path.join(PROJECT_ROOT_DIR,` `"plt_images", fig_id +` `".png")`
    `print("Saving figure", fig_id)`
    `if tight_layout:`
`        plt.tight_layout()`
`    plt.savefig(path,` `format='png', dpi=300)`

`# 无视一些无关紧要的警告`
`import warnings`
`warnings.filterwarnings(action="ignore", message="^internal gelsd")`
`
2.1加载数据
复制代码
import pandas as pd`
`housing = pd.read_csv("./datasets/housing/housing.csv")`
`housing.head()`
`

这里可以对数据做一些简单了解,比如基本数据结构

info()方法可以快速查看数据的描述,特别是总行数、每个属性的类型和非空值的数量

复制代码
housing.info()`   `# total_bedrooms 20433 < 20640总样本数 有缺失值`
`

有9个数值型数据,一个字符型数据

longitude 经度

latitude 纬度

housing_median_age 住房中位年龄

total_rooms 总房间数

total_bedrooms 总卧室数

population 人口

households 家庭户数

median_income 中位收入

median_house_value 中位房屋价值

ocean_proximity 近海程度

2.2查看每个类别中数据个数
复制代码
housing["ocean_proximity"].value_counts()`  `# 类别型数据`
`

<1H OCEAN: 少于1小时的海洋距离 9136

INLAND: 内陆地区 6551

NEAR OCEAN: 靠近海洋 2658

NEAR BAY: 靠近海湾 2290

ISLAND: 岛屿 5

2.3查看数值数据的统计值
复制代码
housing.describe()`  `# 数值型数据`
`

info() 适用于查看 DataFrame 的基本信息,包括数据类型和非空值的数量,而 describe() 适用于获取关于数值型列的统计摘要信息,只对数值型列有效,而ocean_proximity属于字符型数据,因此describe()没显示该属性的统计信息。

2.4查看每个特征的数据分布情况

在一张大图里展示housing数据集的九个特征的数据分布

复制代码
import matplotlib.pyplot as plt`
`housing.hist(bins=50, figsize=(20,15))`  `# 绘制数据集 housing 中数值型特征的直方图`
`

根据直方图的展示。需要注意以下几点:

1.收入中位数单位不是美元,是经过数据缩放的,数据被限制在[0.5,15](横坐标)。

2.房屋年龄和房屋价值的中位数也被限制了,后者是你的标签(目标),因此可能会问题很大。因为你的模型的预测值可能也被限制在这个区间里了,你需要搞清楚500000美元以上的房子是否需要被预测出准确值。如果要的话,你可能要重新收集这部分数据。如果这部分不需要,可以把这部分从训练集和测试集移除。

3.不同的属性有不同的量度。稍后会讨论特征缩放。

4.许多直方图的尾巴很长(左短右长),像最后一张图,对于某些机器学习算法,这会使检测规律变得更难些。我们会在后面尝试变换处理这些属性,使其变为正态分布(例如取log)。

3. 创建测试集

在决定使用哪种机器学习算法之前,确实需要先对数据进行深入分析。但是,人类的大脑很容易受到一种现象的影响,这种现象叫做"数据窥探偏差"。如果你提前看了测试集的数据,可能会不自觉地选择一个能够在这个特定测试集上表现好的模型。这样做的问题在于,当你用这个测试集来评估模型的性能时,得到的结果可能会过于乐观,而实际上,当你将模型部署到真实世界中时,它的表现可能并不如测试时那么好。简而言之,就是不要在决定模型之前看测试集,以免影响你的判断。

这也是先创建测试集的意义,创建后这一部分的数据集就不再去看,一直到验证模型的时候再使用。

3.1分层随机采样划分数据

sklearn提供了非常方便的数据集划分工具,random_state可以设定前面讲过的随机生成器种子。你可以将种子传递给多个行数相同的数据集,可以在相同的索引上分割数据集

复制代码
from sklearn.model_selection import train_test_split`

`train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)`  `# 默认随机抽样`
`

如果数据集很大,随机抽样通常没有问题的,但是如果比较小,可能会有偏差。比如在美国51.3%为女性,如果调查1000人,选女性513名,这称为分层抽样。根据一些制作这个数据集的团队得到的信息,收入中位数是预测房价中位数非常重要的属性,那么我们需要保证测试集可以代表整体数据集中的多种收入分类。也就是测试集要有普遍的意义。

复制代码
housing["median_income"].hist()`
`
3.2 分层抽样一般步骤

1.先把数据分层

2.在每层中随机抽样

3.抽取出来的数据进行合并

为了根据收入进行分层抽样,我们创建一个收入分层的特征

这里把"median_income"属性按照等宽法进行划分,也就是按照(0,1.5],(1.5,3],(3,4.5],(4.5,6],(6,正无穷)5个区间把数据进行划分(分成5类)

复制代码
housing["income_cat"]` `= pd.cut(housing["median_income"],`
`                               bins=[0.,` `1.5,` `3.0,` `4.5,` `6., np.inf],` `# 指定了划分的区间边界。`
`                               labels=[1,` `2,` `3,` `4,` `5])` `# 指定了对应于每个区间的标签`
`
复制代码
housing["income_cat"]`
`
复制代码
housing["income_cat"].hist()` `# 每个类别下数据个数分布,确定了数据层次`
`

设置数据分层后,'income_cat'的展示结果

复制代码
housing["income_cat"].value_counts()`
`

以上等价于把数据划分出来5个层次,接下来我们使用sklearn提供的函数,完成分层数据的抽样和数据合并过程

复制代码
# 随机抽样`
`from sklearn.model_selection import train_test_split`

`strat_train_set, strat_test_set = train_test_split(housing,test_size=0.2,`
`                                                 shuffle=True,stratify=housing["income_cat"],random_state=42)`
`

查看进行分层抽样的测试集各收入层次比例与原数据集是否一致,即验证通过分层抽样创建的测试集是否能具有代表意义

分层抽样:将缩放后的收入情况按照(0,1.5],(1.5,3],(3,4.5],(4.5,6],(6,正无穷)5个区间把数据进行划分(分成5类);

随机抽样:在保证各分层占比不变的情况下,在每一个层级进行随机的抽样,每一层的抽取结果组成分层抽样的总样本

复制代码
strat_test_set["income_cat"].value_counts()` `/` `len(strat_test_set)`
`
复制代码
housing["income_cat"].value_counts()` `/` `len(housing)`
`

可以看出'income_cat'各层次占总数据集的比例是一致的;

从上面分层抽样层次比例和原始数据集的层次比例对比,可以看出分层抽样的比例几乎与原始数据集的分层比例一致;

接下来,对比分层数据集划分和随机数据集划分方法后的收入层次比例。(两种数据集的划分方法,区别于上文提到的分层抽样和随机抽样,随机抽样是分层抽样的一个环节)

复制代码
def` `income_cat_proportions(data):`  `# 定义收入分层比例函数`
    `return data["income_cat"].value_counts()` `/` `len(data)`  `# 用每一个收入层级除以总数据量`

`train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)`

`compare_props = pd.DataFrame({`  `# 定义一个pd.DataFrame格式的变量compare_props,里面包含"Overall"、"Stratified"、"Random"属性`
    `"Overall": income_cat_proportions(housing),`  `# 计算整个数据集housing的收入分层比例`
    `"Stratified": income_cat_proportions(strat_test_set),` `# 计算分层抽样后测试集的收入分层比例`
    `"Random": income_cat_proportions(test_set),` `# 计算随机抽样后测试集的收入分层比例`
`}).sort_index()`
`compare_props["Rand. %error"]` `=` `100` `* compare_props["Random"]` `/ compare_props["Overall"]` `-` `100` `#计算随机抽样相对于整个数据集收入层次比例的误差`
`compare_props["Strat. %error"]` `=` `100` `* compare_props["Stratified"]` `/ compare_props["Overall"]` `-` `100` `#计算分层抽样相对于整个数据集的误差`

`compare_props`
`

由输出结果可以看出分层数据集划分的效果比随机数据集划分要好,因为

  1. 分层数据集划分后收入分层比例与原数据保持一致,减少了不确定性因素

  2. 分层抽样相对于整个数据集的误差明显更小了

3.3确定并处理数据

分层采样完毕,去掉刚才创建的"income_cat"特征

复制代码
for set_ in` `(strat_train_set, strat_test_set):#"income_cat"是要删除的列的名称。axis=1表示删除列,而不是行。`
`    set_.drop("income_cat", axis=1, inplace=True)` `# inplace=True表示直接在原始数据集上进行修改。`
`

最终确定原来训练和测试的数据集

复制代码
train_set = strat_train_set.copy()`
`test_set = strat_test_set.copy()`
`

4. 数据探索、可视化和发现规律

首先需要把测试集放在一边,只研究训练集。如果训练集很大,可能需要从中采样(也就是在训练集里面再进行提取来分批次训练),来保证我们可以快速的探索。在我们的例子中,因为数据很少,可以不用这么做。另外需要对数据拷贝,避免损坏原来的数据。

4.1 地理数据可视化

美国加州房价数据这份数据集有地理环境相关的两个特征维度:经度longitude和纬度latitude

绘制散点图,是探索地理信息的好方法(需观察训练集数据特征,因为实际情况下你不能提前得到测试集数据)

复制代码
train_set.plot(kind="scatter", x="longitude", y="latitude")` `#图1 alpha 默认为 1.0。这意味着如果有多个数据点在同一个位置,它们将会叠加在一起`
`
复制代码
train_set.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)` `#图2,数据点是相对透明的,这样可以更好地显示数据点的密度分布`
`

alpha=0表示完全透明,alpha=1表示完全不透明

还可以进一步对可视化进行优化:

1.设置点的大小表示人口数量

2.设置点的颜色表示房产价格

复制代码
#` `plot 方法是 Pandas 库中基于 Matplotlib 的绘图方法`
`train_set.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4,`
`    s=strat_train_set["population"]/100, label="population",`  `# s设置点的大小,除以100可以使点的大小更适度,避免点太大。`
`    c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True,` `#c设置点的颜色,指定使用的颜色映射。"jet" 从蓝色(低价)到红色(高价)`
`)`
`plt.legend()`
`

代码解析:

1.train_set.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4,

这行代码调用了train_set DataFrame的plot方法来创建一个散点图。参数kind="scatter"指定了图表类型为散点图。x="longitude"和y="latitude"指定了散点图的x轴和y轴分别对应train_set DataFrame中的longitude和latitude列。alpha=0.4设置了点的透明度,范围从0(完全透明)到1(完全不透明),这里设置为0.4,意味着点会有一定的透明度,以便在点重叠的地方可以看到下面的点。

2.s=strat_train_set["population"]/100, label="population",

s参数用于设置散点图中点的大小。这里使用strat_train_set["population"]列的值来确定点的大小,并且除以100来调整大小,使得点的大小更加适度,避免点太大。label="population"为这个散点图的点大小设置了一个图例标签,即"population"。

3.c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True,

c参数用于设置散点图中点的颜色,这里使用"median_house_value"列的值来确定点的颜色。cmap=plt.get_cmap("jet")指定了颜色映射,这里使用了Matplotlib的jet颜色映射,它是一个从蓝色(代表低值)到红色(代表高值)的渐变色。colorbar=True添加了一个颜色条,用于显示颜色对应的数值范围。

4.plt.legend()

这行代码是Matplotlib库的函数,用于在图表中添加图例。由于我们在plot方法中设置了label参数,这里调用plt.legend()会根据这些标签在图表中显示图例。

文件路径要根据自己的计算机文件系统中存放图片的路径来

复制代码
import matplotlib.image as mpimg`
`california_img=mpimg.imread(PROJECT_ROOT_DIR +` `'/images/end_to_end_project/california.png')`  `# 加载california地图,需检测图片路径是否正确`
`ax = train_set.plot(kind="scatter", x="longitude", y="latitude", figsize=(10,7),`
`                       s=strat_train_set['population']/100, label="Population",`  `# s设置点的大小,除以100可以使点的大小更适度,避免点太大。`
`                       c="median_house_value", cmap=plt.get_cmap("jet"),` `#c设置点的颜色,指定使用的颜色映射。"jet" 从蓝色(低价值)到红色(高价值)。`
`                       colorbar=False, alpha=0.4,` `# colorbar=False 禁用颜色条,因为颜色已经在图中的每个点上显示了。`
                      `)`
`plt.imshow(california_img, extent=[-124.55,` `-113.80,` `32.45,` `42.05], alpha=0.5,`
`           cmap=plt.get_cmap("jet"))`
`plt.ylabel("Latitude", fontsize=14)`
`plt.xlabel("Longitude", fontsize=14)`

`prices = train_set["median_house_value"]`
`tick_values = np.linspace(prices.min(), prices.max(),` `11)`
`cbar = plt.colorbar()`
`cbar.ax.set_yticklabels(["$%dk"%(round(v/1000))` `for v in tick_values], fontsize=14)`
`cbar.set_label('Median House Value', fontsize=16)`

`plt.legend(fontsize=16)`
`

代码解析(更多的是了解matplotlib库在数据分析可视化这一块的使用):

1.import matplotlib.image as mpimg

这行代码导入了Matplotlib库中的image模块,用于读取和显示图像。

2.california_img=mpimg.imread(PROJECT_ROOT_DIR + '/images/end_to_end_project/california.png')

使用mpimg.imread函数读取存储在指定路径下的加利福尼亚州地图图片。PROJECT_ROOT_DIR是一个变量,它应该包含项目的根目录路径。这里需要确保路径是正确的,以便能够成功加载图片。

3.ax = train_set.plot(kind="scatter", x="longitude", y="latitude", figsize=(10,7),

这行代码使用train_set DataFrame的plot方法创建一个散点图,并将其赋值给变量ax。kind="scatter"指定图表类型为散点图。x="longitude"和y="latitude"指定散点图的x轴和y轴分别对应train_set DataFrame中的longitude和latitude列。figsize=(10,7)设置了图表的大小。

4.s=strat_train_set['population']/100, label="Population",

s参数用于设置散点图中点的大小,这里使用strat_train_set['population']列的值来确定点的大小,并除以100来调整大小。label="Population"为这个散点图的点大小设置了一个图例标签,即"Population"。

5.c="median_house_value", cmap=plt.get_cmap("jet"),

c参数用于设置散点图中点的颜色,这里使用"median_house_value"列的值来确定点的颜色。cmap=plt.get_cmap("jet")指定了颜色映射,这里使用了Matplotlib的jet颜色映射,它是一个从蓝色(代表低值)到红色(代表高值)的渐变色。

6.colorbar=False, alpha=0.4,

colorbar=False禁用颜色条,因为颜色已经在图中的每个点上显示了。alpha=0.4设置了点的透明度。

7.plt.imshow(california_img, extent=[-124.55, -113.80, 32.45, 42.05], alpha=0.5, cmap=plt.get_cmap("jet"))

这行代码使用plt.imshow函数将加利福尼亚州的地图作为背景图像显示在散点图下面。extent参数定义了图像的边界,即地图的经纬度范围。alpha=0.5设置了地图的透明度,cmap参数设置了颜色映射。

8.plt.ylabel("Latitude", fontsize=14) 和 plt.xlabel("Longitude", fontsize=14)

这两行代码分别设置了y轴和x轴的标签,并指定了字体大小。

9.prices = train_set["median_house_value"]

这行代码从train_set DataFrame中提取median_house_value列的值,并赋值给变量prices。

10.tick_values = np.linspace(prices.min(), prices.max(), 11)

使用np.linspace函数生成一个从prices列的最小值到最大值的等间隔数值数组,用于颜色条的刻度。

11.cbar = plt.colorbar()

这行代码创建了一个颜色条,并将其赋值给变量cbar。

12.cbar.ax.set_yticklabels(["$%dk"%(round(v/1000)) for v in tick_values], fontsize=14)

这行代码设置了颜色条的刻度标签,将每个刻度值转换为以千为单位的美元值,并指定了字体大小。

13.cbar.set_label('Median House Value', fontsize=16)

这行代码设置了颜色条的标题,并指定了字体大小。

14.plt.legend(fontsize=16)

这行代码添加了图表的图例,并指定了字体大小。

从图中可以得出海景对于房价有着明显的支撑作用,并且南方的房子价值要比北方高。所以从上图可看出 Latitude和ocean_proximity可能对房价有影响。因为颜色更深。

4.2寻找数据特征的相关性

数据集不是很大,可以用标准相关系数(standard correlation coefficient,皮尔逊相关系数)来计算

皮尔逊相关系数:

皮尔逊相关系数的取值范围是[-1, 1]:

当相关系数为1时,表示两个变量完全正相关,即一个变量的增加伴随着另一个变量的增加。

当相关系数为-1时,表示两个变量完全负相关,即一个变量的增加伴随着另一个变量的减少。

当相关系数为0时,表示两个变量之间没有线性相关关系。

复制代码
corr_matrix = train_set.corr()`  `# corr 方法计算了整个数据集中每对列之间的相关系数。`
`corr_matrix`
`

分析的目标数据是'median_house_value'

复制代码
corr_matrix["median_house_value"].sort_values(ascending=False)` `# 将相关系数按照降序排列,从而得到与目标变量具有最高相关性的特征。`
`

相关系数的范围是 -1 到 1。当接近 1 时,意味强正相关;例如,当收入中位数增加时,房价中位数也会增加。当相关系数接近 -1 时,意味强负相关;例如,纬度和房价中位数有轻微的负相关性。housing_median_age 0.114110房龄与国内情况好像不太一样,加州房龄越老价格越贵,中国大部分新房比老房价格贵。

另一种检测属性间相关系数的方法是使用 pandas.plotting.scatter_matrix(数据集列名,画布大小)

复制代码
attributes =` `["median_house_value",` `"median_income",` `"total_rooms",`
              `"housing_median_age"]`
`pd.plotting.scatter_matrix(train_set[attributes], figsize=(12,` `8))`
`

读图:

1.median_house_value(房屋中位价)与 median_income(收入中位数):

这两个变量之间存在较为明显的正相关关系。随着收入中位数的增加,房屋中位价也倾向于增加。

2.median_house_value(房屋中位价)与 total_rooms(总房间数):

这两个变量之间也显示出一点正相关性。房屋中位价随着总房间数的增加而增加,这可能意味着更大的房屋通常价格更高。

3.median_income(收入中位数)与 total_rooms(总房间数):

这两个变量之间的关系不太明显,但似乎存在轻微的正相关性。收入较高的地区可能拥有更多的房间。

4.housing_median_age(房屋中位年龄)与其他变量:

房屋中位年龄与其他变量(median_house_value, median_income, total_rooms)之间的关系不明显,散点图没有显示出强烈的线性关系。

5.median_house_value(房屋中位价)与 housing_median_age(房屋中位年龄):

这两个变量之间的关系较为复杂,可能存在轻微的负相关性,表明较新的房屋可能价格更高,但这种关系不如与其他变量的关系强烈。

6.total_rooms(总房间数)与 housing_median_age(房屋中位年龄):

这两个变量之间没有明显的线性关系,散点图显示房间数与房屋年龄之间的分布较为随机。

7.变量的分布:

median_house_value 和 total_rooms 显示出右偏分布,意味着大多数值集中在较低的范围内,但有一些高值的异常点。

median_income 和 housing_median_age 的分布较为均匀,但 median_income 也显示出轻微的右偏。

对角线上的图如果也画散点图的话,其实都是一条直线,没有任何意义,这里对角线上的图画的是直方图。

因为对角线上是自己和自己的比较,是明显的线性关系,并且斜线斜率为1

从上面几个图我们可以看到,最有潜力预测房价的属性是收入,因为目标特征是median_house_value

复制代码
#` `验证猜想`
`train_set.plot(kind="scatter", x="median_income", y="median_house_value", alpha=0.3)`
`#` `定义x、y轴的坐标尺度`
`plt.axis([0,` `16,` `0,` `550000])`
`

这张图说明了几点。首先,相关性非常高;可以清晰地看到向上的趋势,并且数据点不是非常分散。第二,我们之前看到的最高价,清晰地呈现为一条位于 500000 美元的水平线。

4.3特征提取

在把数据给机器学习算法之前,还有一件事可以做,就是尝试多种属性组合。

例如,总的房间数可能并不重要,我们真正关心的是每户的房间数。

构建三个新的特征:每户的房间数,每户的人口数,卧室在总房间数中的比例

复制代码
train_set["rooms_per_household"]` `= train_set["total_rooms"]/train_set["households"]`
`train_set["population_per_household"]=train_set["population"]/train_set["households"]`
`train_set["bedrooms_per_room"]` `= train_set["total_bedrooms"]/train_set["total_rooms"]`
`

再次观察其它数据与目标数据间的关联度

复制代码
corr_matrix = train_set.corr()`
`corr_matrix["median_house_value"].sort_values(ascending=False)`
`

与总房间数或总卧室数相比,卧室占比与房价中位数的关联更强。显然,卧室数/总房间数的比例越低,房价就越高(负相关)。除此之外,每户的房间数相比于总房间数和户数,也更合适预测房价。

这里我们根据相似度排名,去掉几个与房价关系不大的特征。

复制代码
train_set.drop(['households','population_per_household','population','longitude'],axis=1)`
`
相关推荐
风象南24 分钟前
Claude Code这个隐藏技能,让我告别PPT焦虑
人工智能·后端
曲幽1 小时前
FastAPI压力测试实战:Locust模拟真实用户并发及优化建议
python·fastapi·web·locust·asyncio·test·uvicorn·workers
Mintopia1 小时前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮2 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬2 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia2 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区2 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两5 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
敏编程5 小时前
一天一个Python库:jsonschema - JSON 数据验证利器
python