大数据预处理学习笔记01——二手车数据集

《大数据预处理:基于Python的应用》一书的学习笔记,本书用四个数据集作为案例详细讲解了大数据预处理,由于保险数据集并不是公开数据集,所以只跟着做了其他三个,整理在这里。

一、数据集描述 

此数据集可以在Kaggle上获得,在实际操作中感觉我自己下载的数据集和书中用的有差距,但影响不大。

需要导入的包
 

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
import missingno as msno

缺失值

不同系统环境中缺失值表现形式不同。pandas和numpy中为‘NaN’,空字符串“\Delta”不是缺失值。

缺失值概览 

可以先看一下整体的缺失值

msno.matrix(car_data)

空白越多缺失值越严重

因为county这一列是全部缺失,所以直接drop掉这一列。

car_data.drop(labels=['county'],axis=1,inplace=True)

缺失值数量概览

car_data_raw=car_data.copy()#建立副本保存数据集原始状态
#二手车数据集缺失值概览
missing_car=car_data.isna().sum()
print("变量缺失值计数:\n%s" % missing_car)

可以看到缺失值数量和缺失值矩阵展现的一致

缺失值填补 

 根据变量数据类型(整数,浮点)选择不同的填补方式

#根据情况选择不同指标进行填补
#以i为循环变量,missing_car的索引(变量名)为循环变量序列进行for循环,每个循坏使用if语句判断。若当前i所对应的变量包含缺失值,即missing_car[i]>0成立,则:
#使用try:首先尝试将当前变量的数据类型转换为长整数(int64),并使用中位数填补,若成功则进入下一个循环周期,若不成功则进入expect语句
#使用expect语句,在转换长整数型不成功时,检测是否为浮点型,如果是浮点型则用均值填补缺失值,若不成功则使用众数填补缺失值
for i in missing_car.index:
    if missing_car[i]>0:
        try:
            car_data[i]=car_data[i].astype("Int64")
            car_data[i]=car_data[i].fillna(car_data[i].median()) 
        except:
            if is_float_dtype(car_data[i]):
                car_data[i]=car_data[i].fillna(car_data[i].mean()) 
            else:
                car_data[i]=car_data[i].fillna(car_data[i].mode()[0])
print("缺失值计数:\n%s" % car_data.isna().sum())

可以看到已经填补了所有列的缺失值了。 

缺失值信息的提取

因为很多时候缺失值本身也包含了一定的信息,所以书中介绍了将缺失值的模式量化表示出来的方法。在提取缺失值信息时的两个思路:

1.为每个包含缺失值的变量建立一个哑变量形式的新变量,用于将该变量的缺失信息标识出来

2.仅建立一个新变量,将每一个样本在所有变量上的缺失值情况标识出来。

书中的介绍:

步骤一:标记每个变量的缺失值。

#缺失值信息提取方法
#将缺失值用0-1的形式进行标记
mismark_car=pd.DataFrame()#先建一个数据框mismark_car
car_data=car_data_raw.copy()#复制原始数据集
missing_car=car_data.isna().sum()#是一个包含每列缺失值数量的 Series,其中索引是列名,值是对应列的缺失值数量。
#遍历 missing_car 中的索引(列名),如果某列存在缺失值,则在 mismark_car 数据框中创建一个新的列,列名为 "missing_" + 列名,该列的值为对应列中缺失值的标记,用 0 表示非缺失,1 表示缺失。
for i in missing_car.index:
    if missing_car[i]>0:
        mismark_car["missing_%s" % i]=car_data[i].isna().astype(int)
#transpose() 是 DataFrame 的一个方法,用于将数据框的行和列进行转置,即行变成列,列变成行。它返回一个新的 DataFrame,该 DataFrame 的行索引变为原始 DataFrame 的列索引,列索引变为原始 DataFrame 的行索引。
print("二手车数据集缺失值标记:\n%s" % mismark_car[50:60].transpose())

因为这个数据集的数据质量很不好,前几十行基本全是缺失值,所以打印了50-60行用于展示, 0表示非缺失,1表示缺失。

步骤二:生成每个样本的缺失值模式

二、数据离散化

消除异常值,相当于比赛中去掉一个最高分去掉一个最低分,因为这些异常值会影响数据处理的结果。以数据集中的price为例。

#消除异常值
#计算变量price1%和99%分位点
qt=stats.scoreatpercentile(car_data["price"],[1,99])
#去掉最大和最小的1%数据,从而消除异常值
price1=car_data["price"]
price=car_data["price"][(price1>qt[0])&(price1<qt[1])]

 等宽分箱

等宽法适用于对数据分布较为均匀的连续型变量进行离散化,根据变量的取值范围,建立若干个宽度相等且首尾相连的区间。将变量的每个值映射到相应的区间,并以区间名称作为新的离散型变量的值。 区间个数需要事先确定,一般不宜过多不宜过少。过多会将原变量的分布形式保留的比较完整,从而降低原变量在离散化过程中的信息损失,但会失去数据分组带来的信息整合效果,使离散化失去意义。区间过少会破坏原变量的数据分布形似,从而在离散化过程中损失过多的信息。本书以price为例。使用pd.cut()函数。

#等宽分箱
bin_1=pd.cut(price,bins=5)#设定组数为5
#将原变量和等宽分箱结果合并进一个数据框
d1={"price":car_data["price"],"bin":bin_1}
p1=pd.DataFrame(data=d1)
print("等宽分箱结果:\n%s" % p1[0:20])
print("等宽分箱频数分布:\n%s" % bin_1.value_counts())

从结果来看,等宽分箱后,在每个分组内的频数是不平衡的。 

等频分箱

适用于对数据分布不均匀的连续型变量进行离散化。

#等频分箱
bin_2=pd.qcut(price,q=5)
d1={"price":car_data["price"],"bin":bin_2}
p1=pd.DataFrame(data=d1)
print("等频分箱结果:\n%s" % p1[0:20])
print("等频分箱频数分布:\n%s" % bin_2.value_counts())

可以看到等频分组虽然产生了宽度差异较大的区间,但保证了各分组包含的变量的个数基本相等。 

主观法 

前两个等宽和等频都是客观法,下面介绍主观法

(1)离散化为二分类型(0-1型)变量

二分类变量是指仅有两个类别的定性变量的类型。以‘odometer’车辆行驶总里程为例进行离散化。以“变量odometer值是否为0”为条件对其进行离散化。eq(0)用来检查odometer中是否有变量等于0.等于0,则返回True,否则返回False。

#离散化为0-1型变量(条件:变量odometer值为0)
bin_3=car_data["odometer"].eq(0).astype(int)#离散化为0-1形式
#将原变量和0-1型变量合并进一个数据框
d1={"odometer":car_data["odometer"],"bin":bin_3}
p1=pd.DataFrame(data=d1)
print("二分类离散结果:\n%s" % p1[20:40])
print("二分类离散各类聘书:\n%s" % bin_3.value_counts())

结果显示仅有1965个数据被标记为1(1965个数据数值为0),如果使用该数据集建模分析的话可能会出现数据不平衡的现象或低频分类数据现象。

下面展示将NaN(缺失值)和0都标记为1.用|实现,是逻辑运算中的“或”的关系。 

#离散化为0-1型变量(条件:变量odometer值为0或NaN)
bin_3=car_data["odometer"].eq(0)|car_data["odometer"].isna()
#找出odometer为0或缺失值的值
bin_3=bin_3.astype(int)#转换为0-1形式
d1={"odometer":car_data["odometer"],"bin":bin_3}
p1=pd.DataFrame(data=d1)
print("二分类离散结果:\n%s" % p1[20:40])
print("二分类离散各类频数:\n%s" % bin_3.value_counts())

 结果显示,有6365个数据被标记为1了。 

将行驶里程数在合理区间30000到100000之间的标记为1

这个条件可以分为两个条件,一个是大于等于30000另一个是小于等于10000,使用Series.ge() 检查变量的值是否大于等于30000,使用Series.le() 检查值是否小于等于10000. 两者之间使用逻辑运算符&“与”链接。

#离散化为0-1型变量(条件:变量odometer值在30000到100000之间)
bin_3=car_data["odometer"].ge(30000)&car_data["odometer"].le(100000)
#找出odometer在30000到100000之间的值
bin_3=bin_3.astype(int)
d1={"odometer":car_data["odometer"],"bin":bin_3}
p1=pd.DataFrame(data=d1)
print("二分类离散结果:\n%s" % p1[20:40])
print("二分类离散各类频数:\n%s" % bin_3.value_counts())

可以看到符合条件的二手车有162722辆(被标记为1)。 

离散化为顺序变量

将连续变量分组赋值为顺序变量,以odometer为例,里程程度可以在一定程度上代表新旧程度,里程越长越旧。

#离散化为顺序变量
bin_4=pd.cut(car_data["odometer"],
            bins=[0,10000,100000,200000,np.inf],#np.inf 是 NumPy 库中的一个特殊常量,表示正无穷大。在这里,它被用作 cut 函数的 bins 参数的最后一个边界,表示将 "odometer" 列的数值分箱时,最后一个箱的上边界是正无穷大。
            labels=["new","used","old","worn"],#分箱后的标签
            include_lowest = True)#include_lowest=True: 是否包含最低边界
d1={"odometer":car_data["odometer"],"bin":bin_4}#是在创建包含原始数据和分箱结果的 DataFrame 时,给 DataFrame 添加了一个名为 "bin" 的新列,该列的值是 bin_4,即对 "odometer" 列进行离散化后的结果。
p1=pd.DataFrame(data=d1)
print("二分类离散结果:\n%s" % p1[20:40])
print("二分类离散各类聘书:\n%s" % bin_4.value_counts())

从结果来看old和used的车是最多的 

定性变量的数据转换 

以数据集中的fuel,车辆燃料类型为例

car_data["fuel"].value_counts()

可以看到有五类,以及每类的数量。

定性变量转换为哑变量

使用get_dummies()函数

#将变量fuel转换为哑变量(以某一类别为全0项,包含缺失值)
#pd.get_dummies(...): 使用 Pandas 的 get_dummies 函数,将分类变量转换为哑变量。
#car_data["fuel"]: 要进行哑变量编码的列。
#prefix="f": 哑变量列的名称前缀,这里设置为 "f"。
#dummy_na=True: 是否为原始数据中的缺失值创建哑变量列。在本例中有很多缺失值,可以将缺失值也视作一个类别并建立哑变量
#drop_first=True: 是否删除第一个哑变量列,以避免共线性问题。K个类别的定性变量转换为k-1个哑变量
#创建包含原始数据和哑变量结果的 DataFrame,并使用 join 将它们合并。
#d1: 包含原始 "fuel" 列的部分数据的 DataFrame。
#dummy_fuel: 包含 "fuel" 列哑变量结果的 DataFrame。
dummy_fuel=pd.get_dummies(car_data["fuel"],prefix="f",dummy_na=True,drop_first=True)
d1={"fuel":car_data["fuel"][20:40]}
p1=pd.DataFrame(data=d1).join(dummy_fuel[20:40])
print("建立哑变量结果:\n%s" % p1)

#将变量fuel转换为哑变量(不以某一类别为全0项,不包含缺失值)
dummy_fuel=pd.get_dummies(car_data["fuel"],prefix="f",dummy_na=False,drop_first=False)
d1={"fuel":car_data["fuel"]}
p1=pd.DataFrame(data=d1).join(dummy_fuel)
print("建立哑变量结果:\n%s" % p1[20:40])

f_nan 不再是单独的一列

定性变量转换为one-hot码

#将变量fuel转换为one-hot码
ohe=OneHotEncoder(drop="first")#建立one-hot编码器ohe,drop=“first”表示以某一类别为全0项
#将变量fuel转变为数据框形式,并用unknown替换缺失值
fuel=pd.DataFrame(car_data["fuel"]).fillna("unknown")
#使用ohe建立one-hot编码
onehot_fuel = ohe.fit_transform(fuel).toarray()
#将原变量和one-hot编码合并进一个数据框
onehot_fuel = pd.DataFrame(onehot_fuel)#转换为数据框形式
d1={"fuel":car_data["fuel"]}
p1=pd.DataFrame(data=d1).join(onehot_fuel)
print("建立one-hot编码结果:\n%s" % p1[20:40])
#将变量fuel转换为one-hot码
#建立one-hot编码器ohe,drop=“None”表示不以某一类别为全0项
ohe=OneHotEncoder(drop=None)
fuel=pd.DataFrame(car_data["fuel"]).fillna("unknown")
onehot_fuel = ohe.fit_transform(fuel).toarray()
onehot_fuel = pd.DataFrame(onehot_fuel)
d1={"fuel":car_data["fuel"]}
p1=pd.DataFrame(data=d1).join(onehot_fuel)
print("建立one-hot编码结果:\n%s" % p1[20:40])

顺序变量转换为得分 

将之前创建的那个新旧程度的序列转换为得分

# 顺序变量的赋值
bin_4_order=bin_4.map({"new":4,"used":3,"old":2,"worn":1})
d1={"odometer":car_data["odometer"],"bin":bin_4,"bin_order":bin_4_order}
p1=pd.DataFrame(data=d1)
print("顺序变量赋值结果:\n%s" % p1[20:40])

定性变量的平滑化

#消除数据集中价格异常的所有行
qt=stats.scoreatpercentile(car_data["price"],[1,99])## 计算 "price" 列的 1% 和 99% 分位数
car_data1=car_data[(price1>qt[0])&(price1<qt[1])]#: 结合上述两个条件,选取 "price" 列在 1% 到 99% 分位数之间的行,将结果赋给 car_data1
#定性变量平滑化
smooth_value=car_data1[["price","manufacturer"]].groupby(by="manufacturer",).mean()#计算每个品牌的平均价格
smooth_dict=smooth_value["price"].to_dict()#转化为字典
smooth_manufacturer=car_data1["manufacturer"].map(smooth_dict)#将品牌名替换为各自平均价格
d1={"manufacturer":car_data1["manufacturer"],"smooth_manufacturer":smooth_manufacturer}#将原变量和平滑变量合并进一个数据框
p1=pd.DataFrame(data=d1)
print("定性变量平滑化结果:\n%s" % round(p1[20:40],2))

反映了汽车品牌内涵的价格信息。

三、异常值处理 

通过箱形图观察低频分类数据的现象,以汽车厂商manufacturer列为例

#通过箱形图观察低频分类现象
box_plot=car_data["manufacturer"].value_counts().plot.box()
plt.show()

计算每个分类的频数

make_count=car_data["manufacturer"].value_counts()
print(make_count)

计算每个频数的类别的个数 

print(make_count.value_counts())

 低频分类的处理方法

将频数低于100的合并为一类

#将频数低于100的类合为一类
start=time.time()#用于计算算法耗时
make_count1=car_data["manufacturer"].value_counts()
car_data["manufacturer1"]=car_data["manufacturer"].map(
    lambda x:"category_under100"
    if make_count1[x]<100 else x,na_action="ignore")
print("算法耗时%f秒" % (time.time()-start))
make_count1=car_data["manufacturer1"].value_counts()
print(make_count1)

可以看到现在我们合并出来的category——under100 有206个变量

将频数低于100的类按其频数分类

#将频数低于100的类按其频数分类
start=time.time()
make_count1=car_data["manufacturer"].value_counts()
car_data["manufacturer1"]=car_data["manufacturer"].map(
    lambda x:"category_%d" % make_count1[x]
    if make_count1[x]<100 else x,na_action="ignore")
print("算法耗时%f秒" % (time.time()-start))
make_count1=car_data["manufacturer1"].value_counts()
print(make_count1)

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值