第二章的再次思考
目录
一、数据的预处理
二、对于数据的思考
三、对于数据的再次思考
四、选择模型训练数据
附录
处理问题的整体思路
一、数据的预处理
导入需要使用的库
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import pandas as pd
运行此下代码
housing_data=pd.read_csv('housing.csv')
housing_data.info()
# 我们需要获得整体的信息
输出如下
>>
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 longitude 20640 non-null float64
1 latitude 20640 non-null float64
2 housing_median_age 20640 non-null float64
3 total_rooms 20640 non-null float64
4 total_bedrooms 20433 non-null float64
5 population 20640 non-null float64
6 households 20640 non-null float64
7 median_income 20640 non-null float64
8 median_house_value 20640 non-null float64
9 ocean_proximity 20640 non-null object
dtypes: float64(9), object(1)
memory usage: 1.6+ MB
我们可以看到对于total_bedrooms项是存在缺失值的,且ocean_proximity项的类型是object
,
我们需要在后续注意这些问题。
目前需要我们解决的问题有两个:缺失值问题、类型转化问题。我们逐一进行解决。
1.缺失值问题
我们面对缺失值主要有三个处理方法:
(1)进行缺失值的填充
(2)放弃有缺失值的测试案例
(3)放弃有缺失值的特征
这里我们选择缺失值的填充。当然这里我们缺失的数据很少,我们如果选择放弃也是没有问题的。
使用SimpleImputer
来完成插值的工作
from sklearn.impute import SimpleImputer
imputer=SimpleImputer(strategy='median') # 使用中位数对数据进行填补
housing_data['total_bedrooms']=imputer.fit_transform(housing_data['total_bedrooms'].values.reshape(-1,1))
housing_data.info()
输出如下
>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20640 entries, 0 to 20639
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 longitude 20640 non-null float64
1 latitude 20640 non-null float64
2 housing_median_age 20640 non-null float64
3 total_rooms 20640 non-null float64
4 total_bedrooms 20640 non-null float64
5 population 20640 non-null float64
6 households 20640 non-null float64
7 median_income 20640 non-null float64
8 median_house_value 20640 non-null float64
9 ocean_proximity 20640 non-null object
dtypes: category(1), float64(9), object(1)
memory usage: 1.6+ MB
我们可以看到数据完成了填充。
2.处理object类型问题
对于数据来说我们机器更容易去处理数字的问题,同时我们观察到ocean_proximity中的数据输出是有限的,这个为我们的转化提供了条件。
我们使用OrdinalEncoder
来进行处理
from sklearn.preprocessing import OrdinalEncoder
ordinal_encoder=OrdinalEncoder()
ocean_encoded=ordinal_encoder.fit_transform(housing_data['ocean_proximity'].values.reshape(-1,1))
ordinal_encoder.categories_
# 标记了类的类型
输出如下
>>
[array(['<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY', 'NEAR OCEAN'],
dtype=object)]
这样我们就知道了转化成数字后的数字对应着什么意思。
至此我们就解决了我们看得到的数据问题。
二、对数据进行思考
1.我们直接通过直方图来看看数据分布的情况
mpl.rcParams['font.sans-serif']=['FangSong']
mpl.rcParams['axes.unicode_minus']=False # 设置字体,防止后续输入中文出错。
fig,ax=plt.subplots(3,3)
fig.set_dpi(200)
for i in range(3):
for j in range(3):
ax[i,j].hist(housing_data.iloc[:,3*i+j],
bins=np.linspace(np.min(housing_data.iloc[:,3*i+j]),np.max(housing_data.iloc[:,3*i+j]),50))
ax[i,j].set_title(housing_data.columns[3*i+j],fontsize=9)
plt.subplots_adjust(wspace=0.3,hspace=0.5)
plt.show()
#我们首先使用可视化的方法查看一下各个数据的分布情况。
输出如下
>>
根据图片我们就可以得到许多的信息。对于housing_median_age和median_house_value存在着一个阈值,超过阈值的都被放在一类了。
同时几乎对于每个特征都存在着极端值。
####2.使用相关矩阵来和散点图来查看可能存在的关系
# 使用相关性矩阵来查看相关性问题,不过这个就只能看出一一次的相关性,有些相关性会被埋没。
corr_matrix=housing_data.corr()
corr_matrix['median_house_value'].sort_values(ascending=False)
输出如下
>>
median_house_value,1.000000
median_income,0.688075
total_rooms,0.134153
housing_median_age,0.105623
households,0.065843
total_bedrooms,0.049686
population,-0.024650
longitude,-0.045967
latitude,-0.144160
我们发现这些数据中和median_house_value相关性较高的也只有三个,且median_income占主要的成分。
为了探索是否存在一次函数之外的关系,我们使用散点图直接查看。(当数据量过大时不宜使用)
# 数据集的大小不大,我们可以先观察各个之间是否存在相关性
from pandas.plotting import scatter_matrix
attributes=['median_house_value','median_income','total_rooms','housing_median_age']
scatter_matrix(housing_data[attributes],figsize=(12,8))
# 我们没有研究所有的变量,我们仅考察那些对房价的影响潜力较大的数据。
输出如下
>>
确实除了median_income之外其余各个数据之间的关系几乎是不存在的感觉。我们尝试其他方法进行尝试。
三、对数据再次进行处理
既然一次之间看不出关系,不妨我们看看是否和各个数据的平方和其他二次项是否存在着关系呢。
我们使用Polynomialfeatures
进行实现,该类可以实现根据现有数据添加二次和高次的特征项。
# 现在来尝试来创造一些新的属性吧
from sklearn.preprocessing import PolynomialFeatures
poly=PolynomialFeatures()
housing_temporary=housing_data.drop(['latitude','longitude','ocean_proximity','income_cat','median_house_value'],axis=1)
housing_num=housing_temporary.values
housing_add=poly.fit_transform(housing_num)
接着我们用相关性矩阵来看看相关性
housing_ploy=pd.concat([housing_add,housing_data[['median_house_value','latitude','longitude']]],axis=1)
corr_matrix=housing_ploy.corr()
corr_matrix['median_house_value'].sort_values(ascending=False)
# 查看相关性的问题,我们新出现的一个数据似乎有着很好的相关性。
输出如下
>>
median_house_value,1.000000
median_income,0.688075
median_income^2,0.624514
housing_median_age median_income,0.589142
total_rooms median_income,0.376997
households median_income,0.352662
total_bedrooms median_income,0.338424
population median_income,0.276269
housing_median_age total_rooms,0.267332
housing_median_age households,0.158517
housing_median_age total_bedrooms,0.143108
total_rooms,0.134153
housing_median_age^2,0.119955
housing_median_age,0.105623
households,0.065843
total_rooms^2,0.064408
total_rooms households,0.052046
total_bedrooms,0.049457
total_rooms total_bedrooms,0.045810
households^2,0.037986
total_bedrooms households,0.034166
total_rooms population,0.028736
total_bedrooms^2,0.028708
housing_median_age population,0.021064
population households,0.010536
total_bedrooms population,0.007658
population^2,-0.010089
population,-0.024650
longitude,-0.045967
latitude,-0.144160
1,NAN
我们看到经过我们将特征扩展到二次的特征之后,我们的特征数量激增。但是同时我们也创造除了一些更加有用的特征。
(我们也可以通过两个特征做除法来进行添加特征,但是我就不再尝试了,这样也有可能获得很好的关系。)
四、选择模型处理数据
1.分割我们的数据集
对于数据集的分割我们可以使用train_test_split
进行分割,代码如下。
# 下面我们分割数据集
y=housing_data['median_house_value']
X=housing_data.drop(['median_house_value'],axis=1)
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=42)
# 注意将随机数确定,保证每次都有着相同的数据集
但是对于train_test_split
方法所有的抽取都是随机的。但是根据我们上面的分析发现median_income占据着影响
房价的主要方面,所以我们希望根据不同的收入区间来进行分层抽样。下面我们来实现根据房价分层抽样。
先看看median_income的分布情况。
# 我们决定将平均收入作为分层抽样的依据,那么我们再次绘制来看看
fig=plt.figure()
fig.set_dpi(150)
ax=fig.add_subplot(111)
ax.hist(housing_data.iloc[:,7],
bins=np.linspace(np.min(housing_data.iloc[:,7]),np.max(housing_data.iloc[:,7]),20))
ax.set_title(housing_data.columns[7],fontsize=12)
# 在观察数据后我们为数据添加分层的标签
输出如下
>>
我们根据根据这个收入来为数据集添加一个新的新的特征,作为分层抽样的标准
# 进行数据的分层标签添加
housing_data['income_cat']=pd.cut(housing_data['median_income'],bins=[0,1.5,3.0,4,5,6.0,np.inf])
income_cat_count=housing_data['income_cat'].value_counts(sort=False)
plt.figure(dpi=150)
plt.bar(range(6),income_cat_count)
plt.xticks(range(6),income_cat_count.index)
plt.title('分层后的数据分布情况')
# 整体来说分层还是理想的,保持了正态分布的特点。
输出如下
>>
数据保持正态分布的感觉,说明我们选择的分层还不错。
根据新加入的income_cat来分层。
split=StratifiedShuffleSplit(n_splits=2,test_size=0.2,random_state=42)
start_train=[]
start_test=[]
for train_index, test_index in split.split(housing_ploy,housing_ploy['income_cat']):
start_train=housing_ploy.loc[train_index]
start_test=housing_ploy.loc[test_index]
# 这个分层抽样的原理就是临时添加一个train-index和test_index来实现抽样。
X_train = start_train.drop(['median_house_value','income_cat'],axis=1)
y_train = start_train['median_house_value']
X_test = start_test.drop(['median_house_value','income_cat'],axis=1)
y_test = start_test['median_house_value']
至此我们完成了数据集的分割。
2.选择模型来训练我们的数据集
先看看线性回归模型的效果
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
lin_pipeline=Pipeline([
('std', StandardScaler()),
('lin_reg',LinearRegression())
])
lin_pipeline.fit(X_train,y_train)
注意在采取回归模型时,一定要进行数据的归一化处理。
我们现在要考察这模型的效果,我们一般使用交叉验证的方法进行验证。
from sklearn.model_selection import cross_val_score
score=cross_val_score(lin_pipeline,X_train,y_train,
scoring='neg_mean_squared_error',cv=10)
np.sqrt(-score.mean())
# 我们的训练模型效果挺一般的,可以看出会有很大的偏差了。
输出如下
>>
71134.26227024043
这个结果只能说是差强人意了。
看看随机森林的效果
# 采用随机森林进行测试
from sklearn.ensemble import RandomForestRegressor
forest_reg=RandomForestRegressor()
forest_reg.fit(X_train,y_train)
score=cross_val_score(forest_reg,X_train,y_train,
scoring='neg_mean_squared_error',cv=10)
np.sqrt(-score.mean())
# 看来随机森林的效果好了不少的
输出如下
>>
51301.609169123614
我们就仅仅试验这两个方法吧,显然随机森林更加好,我们对随机森林的模型进行一些超参数的调整,看看能不能
找到一个最好值,我们采用GridSearchCV
的方法实现。
# 随机森林还好一点,看看使用网格搜索是否可以得到最好值
from sklearn.model_selection import GridSearchCV
param_grid=[
{'n_estimators':np.arange(250,350,15),
'max_depth':np.arange(14,20),
'max_features':[8,9,10,11]}
]
grid_search=GridSearchCV(
forest_reg,param_grid=param_grid,cv=5,
scoring='neg_mean_squared_error',return_train_score=True
)
grid_search.fit(X_train,y_train)
grid_search.best_estimator
想法很好,但是最后输出的结果还不如原来训练结果(寻找的时间也是很长的,如果拥有足够的时间,去慢慢搜索也是可以的),所以我们使用原来的结果。并且使用joblib
库进行我们模型的保存,方便之后的调用等。
forest_reg=RandomForestRegressor()
forest_reg.fit(X_train,y_train)
import joblib
joblib.dump(forest_reg,'forest.pkl')
至此我们对于房价问题的研究就结束了。
附录
这篇笔记中没有提及但是也十分重要的问题:
1.关于模型的泛化问题,有些模型对于数据有着过拟合的倾向。对于这种问题我们可以通过调节模型中的超参数,或者对我们的数据进行降维减少特征量。
2.数据的特征不是越多就越好,过于多的数据特征会减慢我们的模型速度,而且不一定会得到好的效果,增加维度要谨慎的选择。
3.我们在选择模型时应当多选择几个模型来试验,我这里仅选了两个还是比较少的。
4.对于数据的思考是十分重要的,在训练之前的思考可以为我们创造很好的效果。
5.遇到问题一定要去查询官方文档,在百度上搜索经常会造成大量的时间浪费,官方文档可以解决大部分问题。
6.本文的数据、代码以及PDF版链接
链接:https://pan.baidu.com/s/1uRmh73yBDRv_MiYNT6fWjg
提取码:0k5e