二手车价格预测1:数据集探索
该系列是用于记录跟随Datawhale入门数据挖掘的笔记,感谢Datawhale与天池联合发起的赛事——二手车交易价格预测。
一、赛题背景
赛题以预测二手车的交易价格为任务,数据集报名后可见并可下载,该数据来自某交易平台的二手车交易记录,总数据量超过40w,包含31列变量信息,其中15列为匿名变量。为了保证比赛的公平性,将会从中抽取15万条作为训练集,5万条作为测试集A,5万条作为测试集B,同时会对name、model、brand和regionCode等信息进行脱敏。
image.png二、数据集探索
1.什么是数据集探索(EDA)?
Exploratory Data Analysis refers to the critical process of performing initial investigations on data so as to discover patterns,to spot anomalies,to test hypothesis and to check assumptions with the help of summary statistics and graphical representations(Prasad Patil).
相关资料:
wikipedia:Exploratory data analysis
cmu:Exploratory Data Analysis
What is Exploratory Data Analysis?
2.载入函数
# 数据科学库
import pandas as pd
import numpy as np
from scipy.special import jn
#数据可视化库
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno #缺失值可视化
#忽略警告语句
import warnings
warnings.filterwarnings('ignore')
3.载入数据
train_data = pd.read_csv('C:/Users/Van/Desktop/Datawhale数据挖掘/used_car_train_20200313.csv', engine='python', sep = ' ')
test_data = pd.read_csv('C:/Users/Van/Desktop/Datawhale数据挖掘/used_car_testA_20200313.csv', engine='python', sep = ' ')
4.简略观察数据
4.1 首末数据
#训练集
train_data.head().append(train_data.tail())
#测试集
test_data.head().append(test_data.tail())
4.2 数据形状
#训练集
train_data.shape
#测试集
test_data.shape
如果对自己的pandas等操作不放心,建议执行一步看一下head, tail, shape,这样有助于理解函数并进行操作。同时,也能避免连串的错误。
5.数据总览
5.1基本统计量
describe函数会输出每列的统计量:count、mean、std、min、25%、 50%、 75% 和最大值,这些信息可以帮助我们快速分析出数据的大概范围以及异常值。这里需要注意有时我们会看到999 9999 -1 等值,这些其实都是nan的另外一种表达方式。
#训练集
train_data.describe()
#测试集
test_data.describe()
5.2数据类型
info函数能够帮助我们了解每列数据的type,有助于了解是否存在除了nan以外的异常。
#训练集
train_data.info()
#测试集
test_data.info()
6.数据检测
6.1缺失值检测
6.1.1查看每列缺失值情况
# 训练集
train_data.isnull().sum()
# 测试集
test_data.isnull().sum()
6.1.2缺失值可视化
通过以上代码,我们可以很直观的了解哪些列存在缺失值。如果缺失值较少,我们可以选择填充,但如果缺失值过多,则需要考虑删除。
#训练集
missing = train_data.isnull().sum()
missing = missing[missing > 0]
missing.sort_values(inplace = True)
missing.plot.bar()
image.png
#测试集
missing = test_data.isnull().sum()
missing = missing[missing > 0]
missing.sort_values(inplace = True)
missing.plot.bar()
image.png
以下加载missingno包,它能够很直观地显示出数据缺失情况。
#训练集
#取了250个样本作为观察值
msno.matrix(train_data.sample(250))
image.png
#训练集
#取了1000个样本作为观察值
msno.bar(train_data.sample(1000))
image.png
#测试集
#取了250个样本作为观察值
msno.matrix(test_data.sample(250))
image.png
#测试集
#取了1000个样本作为观察值
msno.bar(test_data.sample(1000))
image.png
6.2异常值检测
6.2.1数据类型
#训练集
train_data.info()
由变量的类型可知,除了notRepairedDamage 为object类型其他都为数值型
#查看notRepairedDamage信息
train_data['notRepairedDamage'].value_counts()
#将notRepairedDamage中“-”替换成nan
train_data['notRepairedDamage'].replace('-', np.nan, inplace = True)
#查看notRepairedDamage信息
train_data['notRepairedDamage'].value_counts()
#查看缺失值情况
test_data.isnull().sum()
#测试集
test_data.info()
#查看notRepairedDamage信息
test_data['notRepairedDamage'].value_counts()
#将notRepairedDamage中“-”替换成nan
test_data['notRepairedDamage'].replace('-', np.nan, inplace = True)
#查看notRepairedDamage信息
test_data['notRepairedDamage'].value_counts()
test_data.isnull().sum()
6.2.1数据分布
通过对其他变量的观察,可以发现seller、offerType严重倾斜
#训练集
train_data["seller"].value_counts()
train_data["offerType"].value_counts()
del train_data["seller"]
del train_data["offerType"]
#测试集
test_data["seller"].value_counts()
test_data["offerType"].value_counts()
del test_data["seller"]
del test_data["offerType"]
7.预测值分布
train_data['price']
train_data['price'].value_counts()
import scipy.stats as st
y = train_data['price']
plt.figure(1); plt.title('Johnson SU')
sns.distplot(y, kde=False, fit=st.johnsonsu)
plt.figure(2); plt.title('Normal')
sns.distplot(y, kde=False, fit=st.norm)
plt.figure(3); plt.title('Log Normal')
sns.distplot(y, kde=False, fit=st.lognorm)
image.png
从上图可以看出,价格不服从正态分布,在进行回归前需要转换,其中无界约翰逊分布是最佳拟合。
7.2偏度和峰度
偏度(skewness)是用来衡量数据的不对称程度,峰度(Kurtosis)是用来衡量数据分布的尖锐程度。
sns.distplot(train_data['price']);
print("Skewness: %f" % train_data['price'].skew())
print("Kurtosis: %f" % train_data['price'].kurt())
image.png
train_data.skew(), train_data.kurt()
sns.distplot(train_data.skew(),color='blue',axlabel ='Skewness')
sns.distplot(Train_data.kurt(),color='orange',axlabel ='Kurtness')
image.png
image.png
上图显示了训练集各属性的偏度与峰度的分布,可以看到大部分属性基本都符合正态分布,结合train_data.skew(), train_data.kurt()
显示的具体数值可知,createdata和power不满足。
7.3预测值分布
plt.hist(Train_data['price'], orientation = 'vertical',histtype = 'bar', color ='red')
plt.show()
image.png
可以看出大于20000得值极少
#log变换
plt.hist(np.log(train_data['price']), orientation = 'vertical',histtype = 'bar', color ='red')
plt.show()
image.png
这里有一个技巧:对price取对数后,这样会使得分布更加均匀。
8.特征分析
8.1前期准备
#分离预测值
Y_train = train_data['price']
#手工方式区分变量
numeric_features = ['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',]
8.2类别特征
8.2.1唯一值
#训练集
for cat_fea in categorical_features:
print(cat_fea + "的特征分布如下:")
print("{}特征有个{}不同的值".format(cat_fea, train_data[cat_fea].nunique()))
print(train_data[cat_fea].value_counts())
# 测试集
for cat_fea in categorical_features:
print(cat_fea + "的特征分布如下:")
print("{}特征有个{}不同的值".format(cat_fea, test_data[cat_fea].nunique()))
print(test_data[cat_fea].value_counts())
8.2.2箱型图
# 因为 name和 regionCode的类别太稀疏了,这里我们把不稀疏的几类画一下
categorical_features = ['model',
'brand',
'bodyType',
'fuelType',
'gearbox',
'notRepairedDamage']
for c in categorical_features:
train_data[c] = train_data[c].astype('category')
if train_data[c].isnull().any():
train_data[c] = train_data[c].cat.add_categories(['MISSING'])
train_data[c] = train_data[c].fillna('MISSING')
def boxplot(x, y, **kwargs):
sns.boxplot(x=x, y=y)
x=plt.xticks(rotation=90)
f = pd.melt(train_data, id_vars=['price'], value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(boxplot, "value", "price")
image.png
8.2.3小提琴图
catg_list = categorical_features
target = 'price'
for catg in catg_list :
sns.violinplot(x=catg, y=target, data=train_data)
plt.show()
image.png
8.2.4柱形图
def bar_plot(x, y, **kwargs):
sns.barplot(x=x, y=y)
x=plt.xticks(rotation=90)
f = pd.melt(train_data, id_vars=['price'], value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(bar_plot, "value", "price")
image.png
8.2.5条形图
def count_plot(x, **kwargs):
sns.countplot(x=x)
x=plt.xticks(rotation=90)
f = pd.melt(train_data, value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(count_plot, "value")
image.png
8.3数字特征
8.3.1前期准备
price_numeric = train_data[numeric_features]
correlation = price_numeric.corr()
print(correlation['price'].sort_values(ascending = False),'\n')
8.3.2相关性分析
price_numeric = train_data[numeric_features]
correlation = price_numeric.corr()
print(correlation['price'].sort_values(ascending = False),'\n')
f , ax = plt.subplots(figsize = (7, 7))
plt.title('Correlation of Numeric Features with Price',y=1,size=16)
sns.heatmap(correlation,square = True, vmax=0.8)
image.png
8.3.3偏度和峰度
for col in numeric_features:
print('{:15}'.format(col),
'Skewness: {:05.2f}'.format(train_data[col].skew()) ,
' ' ,
'Kurtosis: {:06.2f}'.format(train_data[col].kurt())
)
image.png
8.3.4数字特征的分布
f = pd.melt(Train_data, value_vars=numeric_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")
image.png
8.3.5数字特征的相互关系
sns.set()
columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
sns.pairplot(train_data[columns],size = 2 ,kind ='scatter',diag_kind='kde')
plt.show()
image.png
8.3.6多变量相互回归关系
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6), (ax7, ax8), (ax9, ax10)) = plt.subplots(nrows=5, ncols=2, figsize=(24, 20))
# ['v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
v_12_scatter_plot = pd.concat([Y_train,train_data['v_12']],axis = 1)
sns.regplot(x='v_12',y = 'price', data = v_12_scatter_plot,scatter= True, fit_reg=True, ax=ax1)
v_8_scatter_plot = pd.concat([Y_train,train_data['v_8']],axis = 1)
sns.regplot(x='v_8',y = 'price',data = v_8_scatter_plot,scatter= True, fit_reg=True, ax=ax2)
v_0_scatter_plot = pd.concat([Y_train,train_data['v_0']],axis = 1)
sns.regplot(x='v_0',y = 'price',data = v_0_scatter_plot,scatter= True, fit_reg=True, ax=ax3)
power_scatter_plot = pd.concat([Y_train,train_data['power']],axis = 1)
sns.regplot(x='power',y = 'price',data = power_scatter_plot,scatter= True, fit_reg=True, ax=ax4)
v_5_scatter_plot = pd.concat([Y_train,train_data['v_5']],axis = 1)
sns.regplot(x='v_5',y = 'price',data = v_5_scatter_plot,scatter= True, fit_reg=True, ax=ax5)
v_2_scatter_plot = pd.concat([Y_train,train_data['v_2']],axis = 1)
sns.regplot(x='v_2',y = 'price',data = v_2_scatter_plot,scatter= True, fit_reg=True, ax=ax6)
v_6_scatter_plot = pd.concat([Y_train,train_data['v_6']],axis = 1)
sns.regplot(x='v_6',y = 'price',data = v_6_scatter_plot,scatter= True, fit_reg=True, ax=ax7)
v_1_scatter_plot = pd.concat([Y_train,train_data['v_1']],axis = 1)
sns.regplot(x='v_1',y = 'price',data = v_1_scatter_plot,scatter= True, fit_reg=True, ax=ax8)
v_14_scatter_plot = pd.concat([Y_train,train_data['v_14']],axis = 1)
sns.regplot(x='v_14',y = 'price',data = v_14_scatter_plot,scatter= True, fit_reg=True, ax=ax9)
v_13_scatter_plot = pd.concat([Y_train,train_data['v_13']],axis = 1)
sns.regplot(x='v_13',y = 'price',data = v_13_scatter_plot,scatter= True, fit_reg=True, ax=ax10)
image.png
9.生成报告
import pandas_profiling
pfr = pandas_profiling.ProfileReport(train_data)
pfr.to_file("C:/Users/Van/Desktop/Datawhale数据挖掘/example.html")
数据集探索.png
参考资料:
What is Exploratory Data Analysis?
wikipedia:Exploratory data analysis
cmu:Exploratory Data Analysis
数据的偏度和峰度
Seaborn-05-Pairplot多变量图