本文目的是为记录学习阿里天池《二手车价格预测》EDA数据探索分析的过程,不仅仅是copy源代码,而是将所有可参考的参赛者分享和自己查阅的内容结合在一起,其中的细节值得探索!
EDA理解
探索性数据分析EDA是针对原始数据在尽量少的先验假定下进行探索,通过作图、制表、计算特征量、方程拟合等手段探索数据结构和规律的一种方法。
包括对数据的清洗、描述、查看数据分布、比较数据之间的关系、对数据进行总结。分为探索阶段和验证阶段,探索阶段侧重于发现数据中包含的模型,验证阶段侧重于评估这些模型,类似于机器学习算法训练模型和验证模型。
与传统分析的区别在于:传统分析先假设数据符合某分布,再讲数据套入模型当中,得到的结果不尽人意。而EDA则强调数据的真实分布,通过可视化得到启发帮助找到合适的模型。
EDA技术手段
汇总统计
- 频率,众数,百分位了解数据大小分布
- 位置度量:均值,中位数
- 离散情况:极差和方差对异常值敏感,IQR四分位差
- 多属性数据间:相关系数在[-1,1]之间,负值代表负相关,正值代表正相关,越接近1相关性越大。协方差越接近0表明两个数据之间不具有相关性
EDA一般步骤
1. 读取数据,清洗数据
目的:保证数据可供接下来的机器学习使用
缺失值,异常值,重复值,变量是否需要转换,是否需要抽样,是否需要增加新特征等。
缺失值处理:
(1)删除:当缺失值占比较大时,对后期贡献较小,直接删除即可(慎用)
(2)填补:缺失值占比较小且对后期贡献较大
- 可以用当前统计量的均值,中位数进行填充
- 可以用分组后的统计量均值,中位数进行填充
(3)删除具有缺失值的行,但是会删掉未缺失的其他列(不推荐使用)
异常值处理:
2. 探索数据特征,使用描述统计量和图表对数据进行描述
单变量分析
直方图一般解决对称or分散,异常值;
箱型图一般解决对称,异常值,分类数据的统计量;
双变量分析
一般结合者任务目标:比如哪个分组的生还率较高?
因变量Y离散-自变量X离散
方法一:分组-聚合-计算df[['x2','x2]].groupby('x3').mean()
方法二:pd.pivot(index,columns,values)
sns.barplot条形图可视化
因变量Y离散-自变量X连续
连续变量离散化分组,pd.cut等距,pd.qcut等频
再使用分组聚合方法
sns.barplot或plt.hist可视化
多变量分析
因变量Y离散-自变量X1,X2...Xn离散
分组聚合方法,这里的index是多变量
sns.barplot(关键是hue进行再分组)
sns.pointplot(关键是hue进行再分组)折线图
数据分为定量数据和定性数据,定量数据有包括连续和离散数据
(1)连续数据
常见描述统计量:均值,中位数,众数,最大值,最小值,四分位数,标准差等
图表:频数分布表(需要分箱),直方图,箱线图(看分布)
(2)离散数据
A. 无序性离散数据
常见描述统计量:各变量出现的频数及占比
图表:频数分布表(绝对频数,相对频数=频数/总数,百分数频数),柱状图,条形图,饼图,茎叶图
B. 有序性离散数据
常见描述统计量:各变量出现的频数及占比
图表:频数分布表,堆积柱状图,堆积条形图
3. 探索各特征之间的关系
目的:了解变量之间的相互关系以及变量与预测值之间的关系
(1)连续变量与连续变量之间的关系
散点图,散点矩阵,相关系数矩阵,热图
指标:皮尔逊相关系数
(2)离散变量与离散变量之间的关系
交叉分组表,堆积柱形图,复合柱形图
指标:卡方检验
(3)连续变量与离散变量之间的关系,饼图等
直方图,箱型图,小提琴图等
EDA项目实战:阿里天池《二手车交易价格预测》
项目目录:
1. 引入包,载入数据
2. 数据概览
3. 判断数据异常及缺失值
4. 了解预测值分布情况
5. 对数字特征进行分析 ***
- 相关性分析
- 查看几个特征得 偏度和峰值
- 每个数字特征得分布可视化
- 数字特征相互之间的关系可视化
- 预测值price分别于各特征之间的回归关系可视化
6. 对分类特征进行分析 *** - unique分布
- 类别特征箱形图可视化
- 类别特征的小提琴图可视化
- 类别特征的柱形图可视化类别
- 特征的每个类别频数可视化(count_plot)
7. 用pandas_profiling生成数据报告
1. 引入包,载入数据
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
import os
os.chdir('/Users/xy/Desktop/专业知识/阿里天池/天池-二手车交易价格预测')
train = pd.read_csv('used_car_train_20200313.csv',sep = ' ')#sep参数指定分隔符,导入训练集
test = pd.read_csv('used_car_testA_20200313.csv',sep = ' ')#导入测试集
sample = pd.read_csv('used_car_sample_submit.csv',sep = ' ')
首先介绍下各字段的含义:
2. 数据概览
数据形状,数据各字段类型
print(train.shape)#(150000, 31)
print(test.shape)#(50000, 30)
train.info()
test.info()
可以看出字段model车型编码、bodytype车身类型、fueltype燃油类型、gearbox变速箱均有缺失值,所有字段都是数值型,除了notRepairedDamage汽车尚有未修复的损坏,需要注意。v_0至v_14均为15个匿名特征。
训练集部分具体数据
pd.set_option('display.max_columns', None)#显示所有列
train.head().append(train.tail())#显示前5+后5列
train.describe()#对数值数据进行描述性统计
可以看出power功率和price交易价格的std标准差较大,price的最小值只有11元,说明离散程度较严重。seller和offtertype标准差极小,属于异常数据,后续处理。
3. 判断数据缺失值
print(train.isnull().sum())#查看缺失值数量
print(test.isnull().sum())#查看缺失值数量
#缺失值可视化
plt.rcParams['font.sans-serif'] = ['SimHei']#避免中文乱码
plt.rcParams['axes.unicode_minus'] = False#避免中文乱码
train_miss = train.isnull().sum()
train_miss = train_miss[train_miss>0]
train_miss.sort_values(inplace=True)#对值进行排序
fig = plt.figure(figsize = (10,4))
axes1 = fig.add_subplot(1,2,1)
train_miss.plot.bar()#柱状图
plt.title('train缺失值可视化')
test_miss = test.isnull().sum()
test_miss = test_miss[test_miss>0]
test_miss.sort_values(inplace=True)
axes2 = fig.add_subplot(1,2,2)
test_miss.plot.bar()
plt.title('test缺失值可视化')
用missingno库专门可视化数据缺失值
import missingno as msno
msno.matrix(train.sample(250))
msno.bar(train.sample(250))
(test也做同样操作,这里不再展示)
结论:通过上图可以直观的看到哪些字段存在缺失值,缺失数量是否庞大,如果较少可以填充,一般lgb等树模型可以空缺让模型自己处理,缺失值量较多则选择删除该字段。
3. 判断数据异常值
上面有数据概览时提及过notRepairedDamage、power功率和price字段可能存在异常。
train['notRepairedDamage'].value_counts()
train['notRepairedDamage'].replace('-',np.nan,inplace=True)
(test也做同样操作,这里不再展示)
可以看出notRepairedDamage字段中有2.4w个-缺失,由于很多模型对缺失值都有处理,因此将-替换为nan。
print(train['offerType'].value_counts())
print(train['seller'].value_counts())
del train['offerType']
del train['seller']
(test也做同样操作,这里不再展示)
offerType和seller数据存在严重倾斜,对预测不会有什么帮助,因此删掉。
(train['power'].value_counts()).sort_values(ascending=False)
(train['price'].value_counts()).sort_index()
power=0的数量较多,price=十位数的数量较少,暂时不作处理。
4. 了解预测值分布情况
(1)首先看下预测值price的总体分布情况
import seaborn as sns
import scipy.stats as st
y = train['price']
plt.figure(1);plt.title('johnsonsu')
sns.distplot(y,kde=True,fit = st.johnsonsu)
plt.figure(2);plt.title('norm')
sns.distplot(y,kde=True,fit = st.norm)
plt.figure(3);plt.title('lognorm')
sns.distplot(y,kde=False,fit = st.lognorm)
如分布图所示,price不符合正态分布在回归之前要做转换。虽然对数拟合做得很好但是最佳拟合还是johnsonsu。
Tips:sns.distplot可以做单变量的直方图,参数kde=True直接绘制密度曲线图,参数fit=传入scipy.stats中的分布类型,用于在观察变量上抽取相关统计特征来强行拟合指定的分布。既不定义kde也不定义fit,返回图表为直方图纵坐标表示的是频数。
(2)了解预测值price 的峰度和偏度
print('price的偏度为%.2f'%train['price'].skew())#3.35
print('price的峰度为%.2f'%train['price'].kurt())#19
#train中峰度和偏度的可视化
plt.figure(1),plt.title('train的偏度skew')
sns.distplot(train.skew(),color='g')
plt.figure(2),plt.title('train的偏度kurt')
sns.distplot(train.kurt(),color='b')
图上可以看出train中某些字段的偏度为-80和60+,峰度为6000,7000+,分别是power和creatdate。
正态分布的峰度和偏度均为0,price的偏度为3.35说明是右偏,尾部在右侧,右侧有极端值,偏度越大离群程度越高。峰度为19说明比正态分布更加陡峭属于尖峰,峰度越大数据中极端值越多。
(3)查看预测值描述统计量及分布
train['price'].describe()
可以看出price的均值为5923,标准差为7501,最大值有99999,回归最怕离群点。
plt.figure(1),plt.title('train的频数分布')
sns.distplot(train['price'],kde=False)
plt.figure(2),plt.title('train的箱型图')
sns.boxplot(train['price'],orient='v')
plt.figure(3),plt.title('train用log转换后的频数分布')
sns.distplot(np.log(train['price']),kde=False)
price与正态分布相差甚远,远处离群点数量较多,训练出的误差较大,无法准确预测,正常来说可以去掉。
可以看出大于20000价格的数量很少,可以当做异常值填充。
将price进行log变换后趋近于正态分布,可以用来预测。
5. 对数字特征进行分析 ***
(1)区分开分类特征和数字特征
y_train = train['price']
numeric_feature = ['power','kilometer', 'v_0', 'v_1', 'v_2', 'v_3', 'v_4', 'v_5', 'v_6', 'v_7', 'v_8', 'v_9', 'v_10', 'v_11', 'v_12', 'v_13','v_14']
categorical_features = ['name','model','brand','bodyType','fuelType','gearbox','notRepairedDamage','regionCode']
categorical_features为分类特征,numeric_feature为数字特征
(2)各个分类特征和数字特征包含的唯一值个数
for cat_fea in categorical_features:
print('train中'+cat_fea+'特征分布如下:')
print('train中'+cat_fea+'有%i个不同值'%(train[cat_fea].nunique()))
#print(train[cat_fea].value_counts())
(3)相关性分析 ***
用热力图表示price与哪些数字特征强相关
numeric_feature.append('price')#将预测值price添加至数字特征中
corr = train[numeric_feature].corr()
fig = plt.figure(figsize = (15,8))
sns.heatmap(corr,annot=True)
图中最右侧是颜色代表的相关系数值,price与v_0、v_8、v_12相关性较高,v_11和v_2、v_7,v_12和v_8,v_13和v_9相关系数都很高。
(4)特征的偏度与峰度
for col in numeric_feature:
print(col+'的峰度为%.2f, 偏度为%.2f'%(train[col].skew(),train[col].kurt()))
可见power,v_2,v_5,v_7,v_11,price的峰度和偏度都有异常。
(5)各个数字特征的分布可视化
f = pd.melt(train, value_vars=numeric_feature)# 将数字变为简洁型
g = sns.FacetGrid(f,col='variable',col_wrap = 2,sharex=False,sharey=False)
g = g.map(sns.distplot,'value')
得到数字特征各自的概率密度分布,可以看出匿名特征相对分布均匀。
- 解读:
首先介绍下pd.melt的用法:
如果说pd.pivot()是将长数据变为宽数据,那么pd.melt()就是pivot的逆转函数,将宽数据变为长数据。pd.melt(df数据集,id_vars不需要被转换的列名,value_vars需要被转换的现有列名)了解这几个参数就够了。将value_vars所包含的值列在同一列当中名为variable,右侧列名为value即是value_vars所对应的值。举例说明:
df10 = pd.DataFrame({'name1':['a','a','a'],
'name2':['b','b','b']})
print(df10)
print(pd.melt(df10,value_vars=['name1','name2']))
把name1和name2都作为被转换的列,得到的结果中名为variable那列为name1和name2的遍历,名为value那列为name1和name2所对应的值遍历。如果不想转化name1,那么将name1作为id_vars即可。
接着介绍sns.FacetGrid()的用法:
FacetGrid()在数据集的不同子集上绘制多个示例图,要求简洁性,也就是每列都是一个变量每行就是一个值,这里要用到上面提及的pd.melt实现。FacetGrid()可以表示多个变量之间的关系。步骤1:要g=sns.FacetGrid()构建初始化对象;步骤2:g.map(sns.displot(),'值')绘制相应的图表.
介绍下参数的使用:
sns.FacetGrid(df数据集,row横向,col纵向,hue代表对值进行分类,col_warp=2表示最多有2列行数不限制)
sns.FacetGrid(df,row='smoker',col='time')
(6)数字特征之间的关系可视化
pairplot主要是表示变量两两之间的对应关系
columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
sns.pairplot(train[columns])
可以明显看出,v_1和v_6呈明显线性关系。
(7)预测值price分别于各特征之间的回归关系可视化
利用线型回归模型对数据进行拟合
#多变量互相回归关系可视化
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6), (ax7, ax8), (ax9, ax10)) = plt.subplots(nrows=5, ncols=2, figsize=(24, 20))#定义多个图像
#columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
v12_scatter_plot = pd.concat([y_train,train['v_12']],axis = 1)#将price和各特征值结合
sns.regplot(x = 'v_12',y = 'price',data = v12_scatter_plot ,ax = ax1)
v8_scatter_plot = pd.concat([y_train,train['v_8']],axis = 1)
sns.regplot(x = 'v_8',y = 'price',data = v8_scatter_plot ,ax = ax2)
v0_scatter_plot = pd.concat([y_train,train['v_0']],axis = 1)
sns.regplot(x = 'v_0',y = 'price',data = v0_scatter_plot ,ax = ax3)
power_scatter_plot = pd.concat([y_train,train['power']],axis = 1)
sns.regplot(x = 'power',y = 'price',data = power_scatter_plot ,ax = ax4)
v5_scatter_plot = pd.concat([y_train,train['v_5']],axis = 1)
sns.regplot(x = 'v_5',y = 'price',data = v5_scatter_plot ,ax = ax5)
v2_scatter_plot = pd.concat([y_train,train['v_2']],axis = 1)
sns.regplot(x = 'v_2',y = 'price',data = v2_scatter_plot ,ax = ax6)
v6_scatter_plot = pd.concat([y_train,train['v_6']],axis = 1)
sns.regplot(x = 'v_6',y = 'price',data = v6_scatter_plot ,ax = ax7)
v1_scatter_plot = pd.concat([y_train,train['v_1']],axis = 1)
sns.regplot(x = 'v_1',y = 'price',data = v1_scatter_plot ,ax = ax8)
v14_scatter_plot = pd.concat([y_train,train['v_14']],axis = 1)
sns.regplot(x = 'v_14',y = 'price',data = v14_scatter_plot ,ax = ax9)
v13_scatter_plot = pd.concat([y_train,train['v_13']],axis = 1)
sns.regplot(x = 'v_13',y = 'price',data = v13_scatter_plot ,ax = ax10)
6. 对分类特征进行分析 ***
(1)unique分布
for cat_fea in categorical_features:
print('train中'+cat_fea+'特征分布如下:')
print('train中'+cat_fea+'有%i个特征'%(train[cat_fea].nunique()))
#print(train[cat_fea].value_counts().sort_values(ascending=False))
(2)类别特征箱形图可视化
先将分类特征变为category类别,category是具有顺序的分类静态变量但是不能排序。这里由于name和regioncode离散程度较大太稀疏,不展示。
train[categorical_features].describe()
categorical_features = ['model','brand','bodyType','fuelType','gearbox','notRepairedDamage']
for c in categorical_features:
train[c] = train[c].astype('category')#把分类标签全部变成分类类型
if train[c].isnull().any():#找出空值
train[c] = train[c].cat.add_categories(['MISSING'])##增加missing分类标签
train[c] = train[c].fillna('MISSING')#将空值赋值成missing分类标签
train['model'].value_counts()#这时,分类特征均为分类类型且其中的空值都是MISSING值
进行分类特征的箱型图可视化
f = pd.melt(train,id_vars='price',value_vars=categorical_features)
g = sns.FacetGrid(f,col='variable',sharex=False,sharey=False,col_wrap=2,size=8)
g.map(sns.boxplot,'value','price')
可以看出brand=24和37的车型价格区间较高离散程度较大,价位较低的离散程度较小;bodytype=6商务车价位稍高;混合动力和柴油的车价位稍高;自动挡汽车价位稍高。这些都可以作为特征在特征工程中使用。
(3)类别特征的小提琴图可视化
f = pd.melt(train,id_vars='price',value_vars=categorical_features)
g = sns.FacetGrid(f,col='variable',sharex=False,sharey=False,col_wrap=2,size = 8)
g.map(sns.violinplot,'value','price')
小提琴图是箱型图与核密度估计的组合图,箱型图展示了分位数的位置,小提琴图展示的是任意位置的密度。
(4)类别特征的柱形图可视化类别
def bar_plot(x, y, **kwargs):#定义barpolt的格式
sns.barplot(x=x, y=y)
x=plt.xticks(rotation=90)
f = pd.melt(train,id_vars='price',value_vars=categorical_features)
g = sns.FacetGrid(f,col = 'variable',sharex=False,sharey=False,col_wrap=2,size = 8)
g.map(bar_plot,'value','price')
这里的barplot默认计算平均值,因此能看出brand=24的车型均值在3w元,brand=34车型均值价位最低;商务车价位最高微型车价位最低;电动、混合动、柴油车价位偏高,液化石油气价位最低;
(5)特征的每个类别频数可视化(countplot)
def count_plot(x,**kwargs):
sns.countplot(x = x)
plt.xticks(rotation=90)
f = pd.melt(train,value_vars=categorical_features)
g = sns.FacetGrid(f,col='variable',sharex=False,sharey=False,col_wrap=2,size=8)
g.map(count_plot,'value')
countplot是一个分类直方图,纵坐标是频数/数量。
7. 用pandas_profiling生成数据报告
import pandas_profiling
pfr = pandas_profiling.ProfileReport(train)
pfr.to_file("example.html")
会很卡...
关于EDA的套路总结:
其实EDA还是对数据集进行异常值缺失值处理,通过可视化图表了解各特征的表现。
1. 引入包,载入数据
2. 数据概览
- 数据形状,数据各字段类型
3. 判断数据异常及缺失值 - missingno库专门可视化缺失值
- 一些模型可以自动处理缺失值,如lgb
- 异常值较严重对分析无帮助可以删除
4. 了解预测值分布情况 - 先看预测值总体分布,符合哪种分布
- 了解预测值峰度和偏度
- 预测值的频数分布
5. 对数字特征进行分析 *** - 区分开数字特征和分类特征
- 了解特征所包含的唯一值个数
- 相关性分析heatmap***
- 查看几个特征得偏度和峰值skew kurt
- 每个数字特征得分布可视化distplot
- 数字特征相互之间的关系可视化pairplot
- 预测值price分别于各特征之间的回归关系可视化regplot
6. 对分类特征进行分析 *** - unique分布
- 类别特征箱形图可视化boxplot
- 类别特征的小提琴图可视化violinplot
- 类别特征的柱形图可视化类别barplot均值
- 特征的每个类别频数可视化countplot
7. 用pandas_profiling生成数据报告
以上就是结合项目对EDA知识点的整理,虽说是跟着做项目但是里面很多套路和函数方法相关知识点还是需要仔细查看,了解细节才能把这些变成自己的干货。因此参考链接值得一看!实战项目(2)就是对特征工程的研究。
码字不易,求赞,求建议,求指导,求拍砖~感谢阅读
Peace❤️Love
参考:
https://blog.csdn.net/huguozhiengr/article/details/85321521探索性数据分析
https://www.cnblogs.com/yongestcat/p/11458057.htmlmissingno库
https://www.cnblogs.com/feffery/p/11128113.htmlseaborn库
https://www.jianshu.com/p/abefd2d01684scipy.stats库
https://www.cnblogs.com/wyy1480/p/10474046.html峰度和偏度
https://blog.csdn.net/weixin_42398658/article/details/82960379FacetGrid方法
https://blog.csdn.net/mingkoukou/article/details/82867218melt的用法
https://www.jianshu.com/p/6e18d21a4cadpairplot方法
https://cloud.tencent.com/developer/article/1517210regplot方法
https://blog.csdn.net/weixin_41580067/article/details/88639872小提琴图
https://www.jianshu.com/p/8bb06d3fd21bbarplot和countplot
https://blog.csdn.net/FrankieHello/article/details/97272990df.groupby()详解