二手车价格预测实战(一)——数据探索

1.理解赛题:

赛题:零基础入门数据挖掘 - 二手车交易价格预测
链接:https://tianchi.aliyun.com/competition/entrance/231784/introduction?spm=5176.12281957.1004.1.38b02448ausjSX

本次赛题的问题看起来很简单——预测二手车交易价格,赛题中没有过多的背景介绍。但是了解背景知识,对后续的预测工作是非常有帮助的。由于本人之前也未接触过二手车行业数据挖掘,因此,查阅相关资料了解二手车定价的背景。

背景

二手车市场越来越繁荣,交易量越来越大。二手车交易市场本身的特点
是买者卖者处于不对称信息结构中,传统的定价方法基于资产评估来进行二手车价格评定,受主观因素影响大且不够精确,尚未形成一套公认的可靠的二手车价格评估体系。因此迫切需要一种更科学、更准确的估价模型。
研究二手车定价有助于:
(1)帮助二手车商规范收车定价业务
(2)帮助汽车制造商推进二手车业务
(3)帮助金融机构开展二手车金融

基础知识

从二手车本身性质来看,由于其属于非标品,具有“一车一价”的特点。二手车的价格会受到车辆使用强度、养护情况、使用区域、品牌溢价、消费心理等多方面的影响,交易价格也会有很大的波动。二手车具有“一车一况”的特点,二手车价格则具有“一车一价”的特点。影响二手车价格的因素,又可从宏观及微观两个层面考虑。从宏观层面上考虑,区域经济情况、人均可支配收入、区域保有量情况等因素影响着供需关系及购买力情况,而区域政策因素、用车环境因素、养车价格等则影响着购买意愿。从微观层面考虑,二手车辆的使用情况、磨损情况、保养情况、是否出过事故等因素。车况可通过车型、行驶里程、车龄、车系、车型配置、车身颜色、车辆用途、所属地区、使用年限、新车价等可搜集的信息来体现。

2.数据探索

比赛数据来自某交易平台的二手车交易记录,总数据量超过40w,包含31列变量信息,其中15列为匿名变量。为了保证比赛的公平性,将会从中抽取15万条作为训练集,5万条作为测试集A,5万条作为测试集B。

数据概况

字段信息

字段名称中文解释字段名称中文解释
name汽车编码regDate汽车注册时间
model车型编码brand品牌
bodyType车身类型fuelType燃油类型
gearbox变速箱power汽车功率范围 [0,600]
kilometer汽车行驶公里notRepairedDamage汽车有尚未修复的损坏
regionCode看车地区编码seller销售方
offerType报价类型creatDate广告发布时间
price汽车价格

v0-v14信息未作进一步说明

# 读取数据
train_data=pd.read_csv('./used_car_data/used_car_train_20200313.csv',sep=' ')
test_data=pd.read_csv('./used_car_data/used_car_testA_20200313.csv',sep=' ')
# 输出数据大小
print('train_data.shape:',train_data.shape)
print('test_data.shape:',test_data.shape)

在这里插入图片描述

stats = []
for col in train_data.columns:
	stats.append((col, train_data[col].nunique(), train_data[col].isnull().sum() * 100 / train_data.shape[0],train_data[col].value_counts(normalize=True, dropna=False).values[0] * 100, train_data[col].dtype))
stats_df = pd.DataFrame(stats, columns=['Feature', 'Unique_values', 'Percentage of missing values','Percentage of values in the biggest category', 'type'])
stats_df.sort_values('Percentage of missing values', ascending=False, inplace=True)
stats_df

在这里插入图片描述从整体的统计信息中,发现价格、功率、行驶里程有0值,在实际中是很不现实的,后续需分析是异常还是脱敏后的正常值。
另外,关注notRepairedDamage、offerType、 seller字段信息。notRepairedDamage在赛题中说明只有0和1两类,统计却出现3类。offerType在赛题中说明有0和1两类,统计只有1类。 seller2类中有1大类占据极大部分。

# 查看object格式的数据字段
train_data['notRepairedDamage'].value_counts()

在这里插入图片描述

test_data['notRepairedDamage'].value_counts()

在这里插入图片描述

# 空替换'-'
train_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
test_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
train_data['seller'].value_counts().sort_values(ascending=False)

在这里插入图片描述
从结果中可以看出offerType,seller两个字段对价格影响特别小,可删除。

train_data.drop(['seller','offerType'],axis=0, inplace=True)

探索性数据分析

在前面的数据信息统计表中发现有几个字段存在缺失值,接下来分析下缺失值情况。

缺失值可视化

# 缺失值可视化显示
import missingno as msno
t_data=train_data.copy()
msno.matrix(df=t_data.iloc[:,1:69], figsize=(20, 14), color=(0.42, 0.1, 0.05),labels=True)

在这里插入图片描述
将缺失值可视化后可以发现bodyType、fuelType、gearbox缺失值比较多,后续需要对这些字段缺失值进行处理。

查看变量分布

查看预测值价格的分布
## 1) 价格总体分布概况
import scipy.stats as st
y = train_data['price']
plt.figure(figsize=[8,5])
plt.subplot(1,3,1)
plt.title('Johnson SU')
sns.distplot(y, kde=False, fit=st.johnsonsu)
plt.subplot(1,3,2)
plt.title('Normal')
sns.distplot(y, kde=False, fit=st.norm)
plt.subplot(1,3,3)
plt.title('Log Normal')
sns.distplot(y, kde=False, fit=st.lognorm)

在这里插入图片描述
将价格的总体分布画出来后,发现Johnson SU拟合效果较好,价格数据分布存在右偏,说明存在过大的极端值。需要对数据中的过大的价格值进行处理。

查看数据的偏度和峰度
偏度和峰度定义可参考 https://www.cnblogs.com/wyy1480/p/10474046.html

## 2) 查看skewness and kurtosis
sns.distplot(train_data['price']);
print("Skewness: %f" % train_data['price'].skew())
print("Kurtosis: %f" % train_data['price'].kurt())

在这里插入图片描述
从偏度值大于0也可得知数据右偏。从价格分布中可以看出价格大于40000后的二手车数量极少。再利用箱型图查看具体的分布划分,从箱型图中可以看出,价格大于20000则为异常值。

train_data['price'].plot(kind='box')

在这里插入图片描述

train_data['price'].hist()

在这里插入图片描述
将价格大于20000的数据剔除,再重新画图,并对价格进行log处理后,价格的分布相对集中。说明后续在特征工程中需要对价格的数据进行log处理。

train_data[train_data['price']<=20000]['price'].hist()

在这里插入图片描述

np.log(train_data[train_data['price']<=20000]['price']).hist()

在这里插入图片描述

查看变量间的关系

特征数据分为定类数据、定序数据、定距数据、定比数据四类。在本次赛题中提供的数据类型有定类数据和定距数据。因此,需要分别分析。

查看定距数据的相关性

num_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','price' ]
cat_features = ['name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'notRepairedDamage', 'regionCode']
num_feature_price=train_data[num_features]
colormap = plt.cm.magma
plt.figure(figsize=(16,12))
plt.title('Pearson correlation of continuous features', y=1.05, size=15)
sns.heatmap(num_feature_price.corr(),linewidths=0.1,vmax=1.0, square=True, 
            cmap=colormap, linecolor='white', annot=True)

在这里插入图片描述
从相关性图表中,可以看到不同定距数据之间的相关性大小。从中可挑选出与价格相关性较大的特征,剔除相关性为0的特征。此外回归预测中需要解决共线性特征。v_6与v_1相关性为1,需判断是否为重复列。此外v0-v14的大部分特征的相关性系数比较大,需要进行降维处理。

查看定距特征的偏度和峰度,观察是否存在极值需要进行处理或者是需要进行转换。

for col in num_features:
    print('{:15}'.format(col), 
          '特征偏度: {:05.2f}'.format(num_feature_price[col].skew()) , 
          '   ' ,
          '特征峰度: {:06.2f}'.format(num_feature_price[col].kurt())  
         )
f = pd.melt(train_data, value_vars=num_features)
g = sns.FacetGrid(f, col="variable",  col_wrap=3, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")

在这里插入图片描述
在这里插入图片描述
上述的数据表中可以看到,v0-v4的特征分布相对均匀。而power的偏度和峰度特别大,右偏且特别峰顶尖锐。因此,需具体查看power的数据分布。

plt.figure(figsize=[16,4])
plt.subplot(1,2,1)
train_data['power'].plot(kind='box')
plt.subplot(1,2,2)
train_data['power'].hist()

在这里插入图片描述
从power的箱型图和直方图中可以看到,power大于2500的二手车数量非常少,将power大于2500的数据剔除继续画图,发现仍然存在异常的值,结合赛题的字段说明中,power的范围为[0,600],因此,将power大于600的剔除,继续画图观察。并将power进行log转换,发现数据有2部分。左边为0的同样是异常值,汽车功率不可能为0。因此,后续将对power大于600及为0的值进行异常值处理,并对power进行log转换。

plt.figure(figsize=[16,4])
plt.subplot(1,2,1)
train_data[train_data['power']<=2500]['power'].plot(kind='box')
plt.subplot(1,2,2)
train_data[train_data['power']<=2500]['power'].hist()

在这里插入图片描述

plt.figure(figsize=[16,4])
plt.subplot(1,3,1)
train_data[train_data['power']<=600]['power'].plot(kind='box')
plt.subplot(1,3,2)
train_data[train_data['power']<=600]['power'].hist()
plt.subplot(1,3,3)
np.log(train_data[train_data['power']<=600]['power']+1).hist()

在这里插入图片描述
查看多变量之间的关系

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()
target=train_data['price']
## 5) 多变量互相回归关系可视化
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6)) = plt.subplots(nrows=3, ncols=2, figsize=(24, 20))
v_12_scatter_plot = pd.concat([target,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([target,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([target,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([target,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([target,train_data['kilometer']],axis = 1)
sns.regplot(x='kilometer',y = 'price',data = v_5_scatter_plot,scatter= True, fit_reg=True, ax=ax5)
v_2_scatter_plot = pd.concat([target,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)

查看定类数据相关系

# 将空值填充为MISSING
for c in cat_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')
# 分析不同定类变量与价格之间的关系
cat_features = ['name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'notRepairedDamage', 'regionCode']
train_data_copy=train_data[train_data['price']<=20000]
plt.figure(figsize=[16,10])
plt.subplot(2,2,1)
ax = sns.boxplot(x="bodyType", y="price", data=train_data_copy)
plt.subplot(2,2,2)
ax = sns.boxplot(x="fuelType", y="price", data=train_data_copy)
plt.subplot(2,2,3)
ax = sns.boxplot(x="gearbox", y="price", data=train_data_copy)
plt.subplot(2,2,4)
ax = sns.boxplot(x="notRepairedDamage", y="price", data=train_data_copy)

在这里插入图片描述
查看bodyType不同类别的价格分布,可以发现不同类型的车型,其价格跨度会有所不同。不同燃油类型价格跨度也不同。自动挡均价比手动挡的高一些。汽车有尚未修复的损伤比无损伤的均价高?(是字段说明有问题?)

plt.figure(figsize=[16,10])
plt.subplot(2,2,1)
ax = sns.violinplot(x="bodyType", y="price", data=train_data_copy)
plt.subplot(2,2,2)
ax = sns.violinplot(x="fuelType", y="price", data=train_data_copy)
plt.subplot(2,2,3)
ax = sns.violinplot(x="gearbox", y="price", data=train_data_copy)
plt.subplot(2,2,4)
ax = sns.violinplot(x="notRepairedDamage", y="price", data=train_data_copy)

在这里插入图片描述

分析日期与价格关系

赛题数据中有汽车注册日期regDate和汽车上线日期creatDate。先将日期格式转化为字符串,并区分年、月。

df_train=train_data.loc[:,['regDate','creatDate','price']]
#转换日期格式
df_train['regDate']=df_train['regDate'].astype(str)
df_train['creatDate']=df_train['creatDate'].astype(str)
df_train['regyear']=df_train['regDate'].str[0:4]
df_train['creatyear']=df_train['creatDate'].str[0:4]
df_train['regmonth']=df_train['regDate'].str[4:6]
df_train['creatmonth']=df_train['creatDate'].str[4:6]
df_train['creatyear'].value_counts()

在这里插入图片描述

df_train['regyear'].value_counts()

在这里插入图片描述

df_train['regmonth'].value_counts()

在这里插入图片描述
此处发现汽车注册月份有登记错误,为00月份,后续若有用到月份字段,则需要进行处理。分析不同汽车注册年份的价格分布箱型图,发现注册年份越往后,价格的跨度越来越大,推测二手车的车型越来越多,且价格有上升的趋势。汽车在不同月份注册的箱型图分布基本一致,说明注册月份对二手汽车价格的影响极小。因此,不需要处理注册月份为00的情况。

df_train_copy=df_train[df_train['price']<=20000]
plt.figure(figsize=[16,10])
plt.subplot(2,1,1)
ax = sns.boxplot(x="regyear", y="price", data=df_train_copy)
plt.subplot(2,1,2)
ax = sns.boxplot(x="regmonth", y="price", data=df_train_copy)

在这里插入图片描述

df_train.groupby('regyear')['price'].mean().plot(kind='line')

在这里插入图片描述

df_train_copy=df_train[df_train['price']<=20000]
plt.figure(figsize=[16,4])
plt.subplot(1,2,1)
ax = sns.boxplot(x="creatyear", y="price", data=df_train_copy)
plt.subplot(1,2,2)
ax = sns.boxplot(x="creatmonth", y="price", data=df_train_copy)

在这里插入图片描述上线年份2015和2016,极大部分的二手车上线实际为2016年,上线月份的价格跨度不一样,其中3,4,9,12月份的价格较低,1,6,11月份价格较高。上线月份可作为预测特征。

3.总结

1、通过分析赛题提供的数据,了解了数据的内容及需要处理的地方,为后续的特征工程做好准备。
2、本次的实践中梳理EDA通用的python语句查看数据,其实,数据挖掘的流程基本上是一样的,不同的地方在于挖掘数据背后的含义,前期需提前了解研究问题的基础背景知识。
3、在EDA过程中了解了其他的一些更简便的工具,如pandas_profiling、easyeda,虽然这些工具能快速出报告结果,但若深入挖掘数据背后的信息,还需要自己动手实践才能体会数据背后隐含的意思。

参考资料:
[1] 谢杨,温华等,基于机器学习的二手车价格评估方法[J].企业技术开发第34卷第11期
[2] 周瑞凯, 基于XGBoost 算法分析影响二手车拍卖价的因素[J].信息与电脑 2019年第6期
[3] 张远森,基于神经网络的二手车价格评估模型[D].天津大学,2018
[4] https://www.jianshu.com/p/9325c9f88ee6
[5] https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12281978.0.0.6802593a1rLGbn&postId=95457

  • 10
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值