大数据预处理学习笔记02——房价数据集

一、数据集描述

《大数据预处理:基于python的应用》一书的学习笔记,书中用的是sklearn中自带的波士顿房价数据集,但是目前这个数据集已经从sklearn中删除了,所以在学习过程中,我用了加利福尼亚房间数据集。

数据集导入和需要的包:

import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
import time
import copy
from pandas.api.types import is_float_dtype
from sklearn.cluster import KMeans
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error,roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
from scipy import stats
from sklearn.preprocessing import scale
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import minmax_scale
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import maxabs_scale
from sklearn.preprocessing import MaxAbsScaler
from sklearn.preprocessing import robust_scale
from sklearn.preprocessing import RobustScaler
from sklearn.decomposition import PCA

from sklearn.datasets import fetch_california_housing
housing = pd.DataFrame(fetch_california_housing().data,columns=fetch_california_housing().feature_names)
housing["target"]=fetch_california_housing().target

浅看一下数据集情况 

二、缺失值 

本身这个数据集是没有缺失值的,书中为了演示给一列数据加了十个缺失值。在本数据集中,给AveRooms这一列数据加了10个缺失值。

sample=random.sample(range(housing.shape[0]),10)#从数据集的行索引中随机选择 10 个样本的索引。这样可以获取 10 行数据的子集。
true_value = housing.loc[sample,"AveRooms"]#保存真值用于验证
housing.loc[sample,"AveRooms"]=np.nan#将所选的 10 行数据中的 "AveRooms" 列的值设置为 NaN,模拟缺失值的情况。
housing_raw=housing.copy()#复制原始数据集,以便稍后与处理后的数据进行比较。
true_pd=pd.DataFrame(data={"True_AveRooms":true_value})#创建一个包含真实 "AveRooms" 列值的 DataFrame
true_pd=housing.iloc[true_value.index,-8:-1].merge(true_pd,how="left",left_index=True,right_index=True)#选择原始数据集中与 "AveRooms" 列相对应的其他列,然后与包含真实值的 DataFrame 进行合并。
print("缺失值情况:\n%s"% round(true_pd,2))#\n表示换行,%s表示这里要插入一个字符串

缺失值概览

#缺失值预览
missing_housing=housing.isna().sum()
print("变量缺失值计数:\n%s"% missing_housing)

可以看到现在AveRooms中有10个缺失值 

缺失值填补 

1.均值填补

使用均值填补,使用Series.fillna()方法进行填补,使用AveRooms这一列的均值进行填补

#使用均值填补
housing=housing_raw.copy()
housing_fill=housing["AveRooms"].fillna(housing["AveRooms"].mean())
print("均值填补的效果:\n%s"% pd.DataFrame(data={"True":true_value,"Fill":housing_fill[true_value.index]}))

可以看到使用了5.42924这个数据进行填补,下面来看填补之后的MSE 

print("均值填补的MSE:\n%s"% mean_squared_error(y_true=true_value,y_pred=housing_fill[true_value.index]))

注意在真实的实践中,我们是无法得知缺失值的真实值的,所以没法求MSE。本书是为了展示不同填补方法的MSE来体现不同填补方法的差距。 

2.聚类填补 

 使用K_means聚类进行填补,使用K-means聚类将全部样本分为若干组,使用该变量所在组的非缺失部分的均值填补该缺失值

#聚类填补
housing=housing_raw.copy()
k_means_model=KMeans(n_clusters=5)#初始化K-Means模型,设置类别树为5
cluster=k_means_model.fit_predict(housing.drop("AveRooms",axis=1))#去掉包含缺失值的变量再进行模型拟合,并计算每条样本所属类别
cluster_fill=housing["AveRooms"].groupby(by=cluster).apply(lambda x:x.fillna(x.mean()))#针对每组,分别用平均值填补缺失值
#lambda表达式是一个匿名函数,可以看作是一个‘一次性’函数,用完即抛
print("聚类填补的效果:\n%s"% pd.DataFrame(data={"True":true_value,"Fill":cluster_fill[true_value.index]}))
print("聚类填补的MSE:\n%s"% mean_squared_error(y_true=true_value,y_pred=cluster_fill[true_value.index]))

可以看到聚类填补的MSE小于均值填补的MSE,聚类填补的效果更好。 

3.线性回归模型填补 

#使用线性回归模型进行填补
housing=housing_raw.copy()#建立副本数据集
#调用数据框的方法DataFrame.dropna(),设定参数subet为包含缺失值变量的AveRooms,从而获得不包含缺失值的数据集作为模型的训练集train
train=housing.dropna(subset=["AveRooms"])
reg=LinearRegression()#初始化线性回归模型
reg.fit(X=train.drop("AveRooms",axis=1),y=train["AveRooms"])#另含有缺失值的变量AveRooms作为因变量,其余变量作为自变量拟合模型
#使用模型预测变量AveRoom的全部数据
predict=pd.Series(reg.predict(housing.drop("AveRooms",axis=1)),index=housing.index)
reg_fill=housing["AveRooms"].fillna(predict)#使用预测量进行填补
print("线性回归填补的效果:\n%s"% pd.DataFrame(data={"True":true_value,"Fill":reg_fill[true_value.index]}))
print("线性回归填补的MSE:\n%s"% mean_squared_error(y_true=true_value,y_pred=reg_fill[true_value.index]))

 可以看到使用线性回归填补时每个填补值都不一样,并且mse相较于均值填补和聚类填补又小的多,证明线性填补的效果更好 

4.GBDT模型填补 

梯度提升树模型,以决策树为基础,对数据进行多轮迭代式的学习。

#使用GBDT模型进行填补
#与线性回归模型填补的步骤是一样的,不再赘述
housing=housing_raw.copy()
train=housing.dropna(subset=["AveRooms"])#使用无缺失的数据作为训练数据
GBDT_model=GradientBoostingRegressor()
GBDT_model.fit(X=train.drop("AveRooms",axis=1),y=train["AveRooms"])
predict=pd.Series(GBDT_model.predict(housing.drop("AveRooms",axis=1)),index=housing.index)
GBDT_fill=housing["AveRooms"].fillna(predict)
print("GBDT填补的效果:\n%s"% pd.DataFrame(data={"True":true_value,"Fill":GBDT_fill[true_value.index]}))
print("GBDT填补的MSE:\n%s"% mean_squared_error(y_true=true_value,y_pred=GBDT_fill[true_value.index]))

可以看到使用GBDT模型填补的mse更小,效果更好,注意这仅仅是在本例中,具体实践中哪种更好不一定。

三、箱形图的概念和使用

使用plot.box()函数画箱形图,画了HouseAge这一列的箱形图 

box_plot=housing["HouseAge"].plot.box()
plt.show()

#观察箱形图的相关指标
HouseAge_q1=housing["HouseAge"].quantile(.25)
HouseAge_q3=housing["HouseAge"].quantile(.75)
HouseAge_iqr=HouseAge_q3-HouseAge_q1
print("中位数(Median):%f" % housing["HouseAge"].quantile(.5))
print("下四分位数(Q1):%f" % HouseAge_q1)
print("上四分位数(Q3):%f" % HouseAge_q3)
print("四分位差(IQR):%f" % HouseAge_iqr)
print("最小值(Min):%f" % housing["HouseAge"].min())
print("最大值(Max):%f" % housing["HouseAge"].max())
print("下限值:%f" % (HouseAge_q1-1.5*HouseAge_iqr))
print("上限值:%f" % (HouseAge_q3+1.5*HouseAge_iqr))

 四、异常值

可以利用箱形图来观察一下一列数据中是否有异常值。

观察因变量“target”的异常值和偏度

box_plot=housing["target"].plot.box()
plt.show()
print("因变量的偏度为:%f" % housing["target"].skew())

 观察自变量“Latitude”的异常值和偏度

box_plot=housing["Latitude"].plot.box()
plt.show()
print("变量的偏度为:%f" % housing["Latitude"].skew())

偏度都大于0代表是右偏。

数据偏度的纠正 

书中介绍了BOX-COX变换对数据进行纠偏的方法。详细介绍请参照原书。

使用scipy.stats库中的boxcox函数进行变换,boxcox函数中包括三个参数:

(1)x:需要进行BOX-COX变换的原始数据

(2)lmbda:变换参数lmbda,其默认值None,当为默认值时,其值将由极大似然法确定

(3)alpha:显著性水平值,其默认值为None,会以第三个输出结果形式返回lmbda的置信度为(1—alpha)的置信区间。

对因变量进行变换

housing_1=copy.deepcopy(housing)
#对目标变量进行 Box-Cox 变换
housing_1["target"],lam_tar = stats.boxcox(housing_1["target"])
box_plot=housing_1["target"].plot.box()#绘制 Box-Cox 变换后的目标变量的箱线图。
plt.show()
print("纠偏后因变量偏度:%f" % housing_1["target"].skew())
print("对因变量进行BOX-COX变换的lambda为:%f" % lam_tar)

对Latitude进行变换 

housing_1=copy.deepcopy(housing)
housing_1["Latitude"],lam_Latar = stats.boxcox(housing_1["Latitude"])
box_plot=housing_1["Latitude"].plot.box()
plt.show()
print("纠偏后因变量偏度:%f" % housing_1["Latitude"].skew())
print("对因变量进行BOX-COX变换的lambda为:%f" % lam_Latar)

数据纠偏对于模型预测效果的影响

为观察纠偏对模型预测的影响,建立了三个不同的模型。

(1)model1 无论自变量还是因变量使用的均为未经过BOX-COX变换的有偏数据

(2)model2的因变量使用的是经过BOX-COX变换的数据,自变量仍然为未经过BOX- C0X变换的有偏数据

(3)model3的因变量和自变量中的Latitude使用的是经过BOX- COX变换的数据

#用高偏度因变量建立简单线性回归模型
model_1=LinearRegression()
model_1.fit(X=housing.drop("target",axis=1),y=housing["target"])
#使用无偏的因变量建立简单线性回归mox
model_2=LinearRegression()
model_2.fit(X=housing.drop("target",axis=1),y=housing_1["target"])
#同时对因变量和变量Latitude做纠偏处理
model_3=LinearRegression()
model_3.fit(X=housing_1.drop("target",axis=1),y=housing_1["target"])

 看一下三个模型的MSE

#计算因变量未纠偏时回归MSE
print("因变量未纠偏时回归MSE:%f" % mean_squared_error(y_true=housing["target"],y_pred=(model_1.predict(housing.drop("target",axis=1)))))
print("因变量纠偏后回归MSE:%f" % mean_squared_error(y_true=housing["target"],y_pred=(model_2.predict(housing.drop("target",axis=1))*lam_tar+1)**(1/lam_tar)))#然后反向变换还原为原始空间。
print("因变量和自变量纠偏后回归MSE:%f" % mean_squared_error(y_true=housing["target"],y_pred=(model_3.predict(housing_1.drop("target",axis=1))*lam_tar+1)**(1/lam_tar)))

 在本例中纠偏未能提高预测效果,在书中给出的例子中三个模型的MSE依次下降,能够显著改善线性回归模型的预测效果。

五、数据特征缩放(data feature scaling)

数据特征缩放的方法包括数据标准化(中心化和标准化),数据的归一化(Min-Max缩放和Max- ABS缩放)以及包含异常值数据的标准化方法。scale()提供了对单一序列进行标准化的实现方式,模块StandardScale提供了建立数据标准化模型的方法。

主要以数据集中的AveBedrms 列为例,首先观察这一列数据的基本情况。

#观察原始数据
BR=housing["AveBedrms"]
print("变量BR的分布\n平均值:%f\n标准差:%f\n最大值:%f\n最小值:%f" % (BR.mean(),BR.std(),BR.max(),BR.min()))
plt.plot(BR)
plt.xlabel("Index")
plt.ylabel("Values of BR")
plt.show()

plt.boxplot(BR)
plt.xlabel("BR")
plt.ylabel("Values of BR")
plt.show()

plt.boxplot(housing.values,labels=housing.columns,vert=False)
plt.xlabel("Values of housing")
plt.show()


数据特征缩放方法

书中主要介绍了五种常用的数据特征缩放方法,包括数据中心化,数据标准化,Min-Max缩放,Max-ABS缩放和Robust标准化。

数据中心化

数据中心化,用变量中的每个值减去该变量的均值即可得到。

#直接计算
centralize = BR-BR.mean()#直接计算中心化后的数据。这里假设 BR 是包含特征 "AveBedrms" 的数据。
#使用scale()函数
centralize_s=scale(BR,with_std=False)
#使用 scale() 函数进行中心化。with_std=False 表示不进行标准化,只进行中心化。
scaler=StandardScaler(with_std=False)
#创建 StandardScaler 的实例,同样设置 with_std=False 表示只进行中心化。
centralize_scaler=scaler.fit_transform(housing[["AveBedrms"]])
#使用 fit_transform() 方法进行中心化。这里选择了 "AveBedrms" 特征,并使用 fit_transform() 对其进行中心化。
print("中心化后BR的分布\n平均值:%f\n标准差:%f\n最大值:%f\n最小值:%f" % (centralize_scaler.mean(),centralize_scaler.std(),centralize_scaler.max(),centralize_scaler.min()))
plt.plot(centralize_scaler)
plt.xlabel("Index")
plt.ylabel("Values of centralize_scaler")
plt.show()
plt.boxplot(centralize_scaler)
plt.xlabel("BR")
plt.ylabel("Values of centralize_scaler")
plt.show()

可以看到中心化后的变量平均值为0,最大值和最小值也发生了变化。本例中的标准差相差很小,书中这个例子的标准差是相同的,说明中心化后的变量尺度没有发生变化。观察箱形图也能发现中心化对数据分布的几乎没有影响。

对整个数据集中心化

scaler=StandardScaler(with_std=False)
housing_centralize=scaler.fit_transform(housing)#使用 fit_transform() 方法对整个数据集 housing 进行中心化。
print(pd.DataFrame({"Scale":scaler.scale_,"Mean":scaler.mean_},index=housing.columns))
#创建一个 Pandas DataFrame,包含中心化后的数据集的均值和缩放因子(标准差)。这里使用 scaler.scale_ 获取标准差,scaler.mean_ 获取均值。
plt.boxplot(housing_centralize,labels=housing.columns,vert=False)
plt.xlabel("Values of housing_centralize")
plt.show()

可以看到由于对变量BR进行了中心化缩放,并未使用数据的尺度属性,所以Scale那一列的值都是None。 数据集的每列数据分布都没有变化。

数据标准化 

数据标准化(Z-score 标准化),是在数据中心化的基础上,再除以该数据的标准差,缩放后的变量均值为0,标准差为1。

normalize = (BR-BR.mean())/BR.std()
normalize_s=scale(BR)
scaler=StandardScaler()
normalize_scaler=scaler.fit_transform(housing[["AveBedrms"]])
print("标准化后BR的分布\n平均值:%f\n标准差:%f\n最大值:%f\n最小值:%f" % (normalize_scaler.mean(),normalize_scaler.std(),normalize_scaler.max(),normalize_scaler.min()))
plt.plot(normalize_scaler)
plt.xlabel("Index")
plt.ylabel("Values of normalize_scaler")
plt.show()
plt.boxplot(normalize_scaler)
plt.xlabel("BR")
plt.ylabel("Values of normalize_scaler")
plt.show()


可以看到标准化后的变量变成了均值为0,标准差为1的分布。 

对整个数据集进行标准化

scaler=StandardScaler()
housing_normalize=scaler.fit_transform(housing)
print(pd.DataFrame({"Scale":scaler.scale_,"Mean":scaler.mean_},index=housing.columns))
plt.boxplot(housing_normalize,labels=housing.columns,vert=False)
plt.xlabel("Values of housing_normalize")
plt.show()

Min-Max缩放 

Min-Max缩放也被称为离差标准化,可以将数据缩放至指定的区间,通常情况下这一制定区间为【0,1】。

#直接计算,将变量缩放到区间【0,1】
BR_01=(BR-BR.min())/(BR.max()-BR.min())
#使用minmax_scale()函数将变量缩放到区间【0,1】
BR_01=minmax_scale(BR)
#使用MinMaxScale类,将变量缩放到区间【0,1】
minmaxs_scaler=MinMaxScaler()
BR_01_scaler=minmaxs_scaler.fit_transform(housing[["AveBedrms"]])
print("缩放后BR的分布\n平均值:%f\n标准差:%f\n最大值:%f\n最小值:%f" % (BR_01_scaler.mean(),BR_01_scaler.std(),BR_01_scaler.max(),BR_01_scaler.min()))
plt.plot(BR_01_scaler)
plt.xlabel("Index")
plt.ylabel("Values of BR_01_scaler")
plt.show()
plt.boxplot(BR_01_scaler)
plt.xlabel("BR")
plt.ylabel("Values of BR_01_scaler")
plt.show()


mm_scaler=MinMaxScaler()
housing_01=mm_scaler.fit_transform(housing)
print(pd.DataFrame({"Scale":mm_scaler.scale_,"Min":mm_scaler.data_min_,"Max":mm_scaler.data_max_},index=housing.columns))
plt.boxplot(housing_01,labels=housing.columns,vert=False)
plt.xlabel("Values of housing_01")
plt.show()

Max-ABS缩放 

这种方法可以将变量缩放至区间【-1,1】,但是方法和Min-Max缩放不同,这个方法是将变量的每个值除以变量的绝对值的最大值。这种方法不是将原数据的所有值整体缩放到区间【-1,1】,而是将原值大于0的数据缩放到区间(0,1】,将原值小于0的数据缩放到区间(-1.0】,原值等于0的数据缩放后还为0。

#直接计算最大绝对值缩放后的数据。这里使用了 Pandas 的 .abs() 方法获取数据的绝对值,并除以数据的最大绝对值。
BR_ma=BR/BR.abs().max()
#使用 maxabs_scale 函数对 BR 进行最大绝对值缩放,并存储结果为 Pandas Series。
BR_ma=pd.Series(maxabs_scale(BR))
BR1=copy.deepcopy(BR)
#从 BR1 中随机选择 100 个样本,并记录它们的索引。
index1=BR1.sample(n=100,random_state=0).index
BR1[index1]=-1*BR1[index1]#将随机选择的 100 个样本的值取负数,以引入一些变化。
#使用 maxabs_scale 函数对修改后的 BR1 进行最大绝对值缩放,并存储结果为 Pandas Series。
BR1_ma=pd.Series(maxabs_scale(BR1))
print(round(pd.DataFrame({"缩放后的B1":BR_ma.describe(),"缩放后的BR1":BR1_ma.describe()}),3))
plt.boxplot((BR,BR1),labels=("BR","BR1"))
plt.ylabel("Values of BR & BR1")
plt.show()
plt.boxplot((BR_ma,BR1_ma),labels=("BR_ma","BR1_ma"))
plt.ylabel("Values of BR_ma & BR1_ma")
plt.show()


#使用MaxAbsScaler模块将b变量BR缩放至区间【-1,1】
ma_scaler=MaxAbsScaler()
BR_ma_scaler=ma_scaler.fit_transform(housing[["AveBedrms"]])
plt.plot(BR_ma_scaler)
plt.xlabel("Index")
plt.ylabel("Values of BR_ma_scaler")
plt.show()
plt.boxplot(BR_ma_scaler)
plt.xlabel("BR")
plt.ylabel("Values of BR_ma_scaler")
plt.show()
ma_scaler=MaxAbsScaler()
housing_ma=ma_scaler.fit_transform(housing)
print(pd.DataFrame({"Scale":ma_scaler.scale_,"MaxABS":ma_scaler.max_abs_,},index=housing.columns))
plt.boxplot(housing_ma,labels=housing.columns,vert=False)
plt.xlabel("Values of housing_ma")
plt.show()

 

Min-Max与Max- ABS方法比较

#min_max方法与max_abs方法的比较
#生成BR2
BR2=copy.deepcopy(BR)
index2=BR2.sample(n=1,random_state=0).index
BR1[index2]=-100
#观察BR和BR2原值的分布
plt.boxplot((BR,BR2),labels=("BR","BR2"))
plt.ylabel("Values of BR & BR2")
plt.show()
#分别对变量BR用两种方式进行缩放
Bmm=minmax_scale(BR,(-1,1))
Bma=maxabs_scale(BR)
B2mm=minmax_scale(BR2,(-1,1))
B2ma=maxabs_scale(BR2)
plt.boxplot((Bmm,Bma,B2mm,B2ma),labels=("Bmm","Bma","B2mm","B2ma"))
plt.show()

 Robust缩放

Robust缩放方法与标准化方法的理念相同,都是首先中心化,然后除以尺度。二者的区别是Robust缩放用不易受极端值影响但作用相似的中位数和四分位差替代了均值和标准差。适用于变量的异常值情况比较严重时。

#生成包含异常值的数据BR3
BR3=copy.deepcopy(BR)
index3=BR3.sample(n=10,random_state=0).index
BR3[index3]=5*BR3[index3]
#使用scale函数将变量BR和BR3标准化
BR_std=pd.Series(scale(BR))
BR3_std=pd.Series(scale(BR3))
BR_rob=pd.Series(robust_scale(BR))
BR3_rob=pd.Series(robust_scale(BR3))
print(round(pd.DataFrame({"BR":BR.describe(),"BR3":BR3.describe(),"BR_std":BR_std.describe(),"BR3_std":BR3_std.describe(),"BR_rob":BR_rob.describe(),"BR3_rob":BR3_rob.describe()}),3))
plt.boxplot((BR,BR3),labels=("BR","BR3"))
plt.show()
plt.boxplot((BR_std,BR3_std),labels=("BR_std","BR3_std"))
plt.show()
plt.boxplot((BR_rob,BR3_rob),labels=("BR_rob","BR3_rob"))
plt.show()


#直接计算
BR_rob=(BR-BR.median())/(BR.quantile(0.75)-BR.quantile(0.25))
rob_scaler=RobustScaler()#将变量BR标准化
BR_rob_scaler=rob_scaler.fit_transform(housing[["AveBedrms"]])
#将所有变量标准化
housing_rob=rob_scaler.fit_transform(housing)
print(pd.DataFrame({"Scale":rob_scaler.scale_,"Median":rob_scaler.center_},index=housing.columns))
plt.boxplot(housing_rob,labels=housing.columns,vert=False)
plt.xlabel("Values of housing_rob")
plt.show()

数据特征缩放的效果

本节以数据标准化方法为例,分别使用经过标准化和未经过标准化的数据建立主成分回归模型,展示数据特征缩放的效果。

train_x=housing.drop("target",axis=1)
train_y=housing["target"]
scaler=StandardScaler()
train_x_norm=scaler.fit_transform(train_x)
#使用未标准化的数据,建立主成分回归模型,使用三个主成分
pca=PCA(n_components=3,random_state=0)
linear=LinearRegression()
linear.fit(pca.fit_transform(train_x),train_y)
mse=mean_squared_error(train_y,linear.predict(pca.transform(train_x)))
print("未标准化主成分回归MSE%f" % mse)
#使用标准化的数据,建立主成分回归模型,使用三个主成分。
pca_norm=PCA(n_components=3,random_state=0)
linear_norm=LinearRegression()
linear_norm.fit(pca_norm.fit_transform(train_x_norm),train_y)
mse_norm=mean_squared_error(train_y,linear_norm.predict(pca_norm.transform(train_x_norm)))
print("标准化主成分回归MSE%f" % mse_norm)

​​​​​​​

可以看到标准化后的MSE小于未标准化的,这说明标准化的更优。

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值