欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二手车交易价格预测-Task2 数据分析

程序员文章站 2022-05-16 20:59:59
...

本阶段我们主要是通过EDA等方式来了解数据,探索数据的特征。
关于资料有挺多疑问的地方,均在文中用删除线加以注释,并总结放在了最后
一、载入各种库和数据

使用库pandas来便捷的载入数据,其中path标识了我们训练集和测试集所在的路径,为了之后的读取更为便捷。

# 导入warnings包,利用过滤器来实现忽略警告语句。
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno

path = 'E:/学习资料/car/'
Train_data = pd.read_csv(path + 'used_car_train.csv', sep=' ')    # 文件以空字符分隔数据,若以','分隔数据,则sep=','或省略(源文件都将数据存到了一列里面)
Test_data = pd.read_csv(path + 'used_car_testA.csv', sep=' ')

代码详解:我们通过观察源文件.csv可以知,所有特征均存储在同一列中,且特征之间以空字符分隔,因此我们在使用pandas.read_csv读入的时候需要使用关键字sep=' '实现。sep默认的是以逗号','分隔,以后我们读取源文件时必须显示的使用sep=' '

下面我们来简略的观察一下源数据。

print(Train_data.head().append(Train_data.tail()))

代码详解:其中Train_data.head()是获取训练集的前几行特征(默认前5行,如果想调用前10行可用Train_data.head(10)来实现),Train_data.tail()是或许训练集的最后几行特征,默认值同样也是后5行。因此我们可以使用上面代码观察原数据集前5行和后5行的数据。其结果如下:

        SaleID    name   regDate  model  ...      v_11      v_12      v_13      v_14
0            0     736  20040402   30.0  ...  2.804097 -2.420821  0.795292  0.914762
1            1    2262  20030301   40.0  ...  2.096338 -1.030483 -1.722674  0.245522
2            2   14874  20040403  115.0  ...  1.803559  1.565330 -0.832687 -0.229963
3            3   71865  19960908  109.0  ...  1.285940 -0.501868 -2.438353 -0.478699
4            4  111080  20120103  110.0  ...  0.910783  0.931110  2.834518  1.923482
149995  149995  163978  20000607  121.0  ... -2.983973  0.589167 -1.304370 -0.302592
149996  149996  184535  20091102  116.0  ... -2.774615  2.553994  0.924196 -0.272160
149997  149997  147587  20101003   60.0  ... -1.630677  2.290197  1.891922  0.414931
149998  149998   45907  20060312   34.0  ... -2.633719  1.414937  0.431981 -1.659014
149999  149999  177672  19990204   19.0  ... -3.179913  0.031724 -1.483350 -0.342674

[10 rows x 31 columns]

我们在用shape函数观察一下整个训练集数据的“形状”:

print(Train_data.shape)

结果如下所示:

(150000, 31)

综上我们可知,训练集*有150,000条数据,每条数据31个特征值。

我们用同样的方法看一下测试集数据的“形状”,结果如下:

(50000, 30)

因此测试集共有50,000条数据,每条数据30个特征值。细心的同学可能发现了测试集的特征数比训练集少了一个,原因是训练集中多的那个特征就是该数据的标签price

二、总览数据概况

1. 熟悉数据的相关统计量
通过describe()来熟悉数据的相关统计量,看max是否有异常值,如99,999等。

print(Train_data.describe())

结果如下所示:

              SaleID           name  ...           v_13           v_14
count  150000.000000  150000.000000  ...  150000.000000  150000.000000
mean    74999.500000   68349.172873  ...       0.000313      -0.000688
std     43301.414527   61103.875095  ...       1.288988       1.038685
min         0.000000       0.000000  ...      -4.153899      -6.546556
25%     37499.750000   11156.000000  ...      -1.057789      -0.437034
50%     74999.500000   51638.000000  ...      -0.036245       0.141246
75%    112499.250000  118841.250000  ...       0.942813       0.680378
max    149999.000000  196812.000000  ...      11.147669       8.658418

[8 rows x 30 columns]

很显然,这不是我们想要的结果(我用的是pycharm),好多特征都被折叠了,于是我们需要想办法展开被折叠的内容,我们只需要在之前加入以下设置即可:

pd.set_option('display.max_columns',1000)
pd.set_option('display.width', 1000)
print(Train_data.describe())

于是我们得到了全部特征的相关统计量。

              SaleID           name       regDate          model          brand       bodyType       fuelType        gearbox          power      kilometer     regionCode         seller  offerType     creatDate          price            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
count  150000.000000  150000.000000  1.500000e+05  149999.000000  150000.000000  145494.000000  141320.000000  144019.000000  150000.000000  150000.000000  150000.000000  150000.000000   150000.0  1.500000e+05  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000  150000.000000
mean    74999.500000   68349.172873  2.003417e+07      47.129021       8.052733       1.792369       0.375842       0.224943     119.316547      12.597160    2583.077267       0.000007        0.0  2.016033e+07    5923.327333      44.406268      -0.044809       0.080765       0.078833       0.017875       0.248204       0.044923       0.124692       0.058144       0.061996      -0.001000       0.009035       0.004813       0.000313      -0.000688
std     43301.414527   61103.875095  5.364988e+04      49.536040       7.864956       1.760640       0.548677       0.417546     177.168419       3.919576    1885.363218       0.002582        0.0  1.067328e+02    7501.998477       2.457548       3.641893       2.929618       2.026514       1.193661       0.045804       0.051743       0.201410       0.029186       0.035692       3.772386       3.286071       2.517478       1.288988       1.038685
min         0.000000       0.000000  1.991000e+07       0.000000       0.000000       0.000000       0.000000       0.000000       0.000000       0.500000       0.000000       0.000000        0.0  2.015062e+07      11.000000      30.451976      -4.295589      -4.470671      -7.275037      -4.364565       0.000000       0.000000       0.000000       0.000000       0.000000      -9.168192      -5.558207      -9.639552      -4.153899      -6.546556
25%     37499.750000   11156.000000  1.999091e+07      10.000000       1.000000       0.000000       0.000000       0.000000      75.000000      12.500000    1018.000000       0.000000        0.0  2.016031e+07    1300.000000      43.135799      -3.192349      -0.970671      -1.462580      -0.921191       0.243615       0.000038       0.062474       0.035334       0.033930      -3.722303      -1.951543      -1.871846      -1.057789      -0.437034
50%     74999.500000   51638.000000  2.003091e+07      30.000000       6.000000       1.000000       0.000000       0.000000     110.000000      15.000000    2196.000000       0.000000        0.0  2.016032e+07    3250.000000      44.610266      -3.052671      -0.382947       0.099722      -0.075910       0.257798       0.000812       0.095866       0.057014       0.058484       1.624076      -0.358053      -0.130753      -0.036245       0.141246
75%    112499.250000  118841.250000  2.007111e+07      66.000000      13.000000       3.000000       1.000000       0.000000     150.000000      15.000000    3843.000000       0.000000        0.0  2.016033e+07    7700.000000      46.004721       4.000670       0.241335       1.565838       0.868758       0.265297       0.102009       0.125243       0.079382       0.087491       2.844357       1.255022       1.776933       0.942813       0.680378
max    149999.000000  196812.000000  2.015121e+07     247.000000      39.000000       7.000000       6.000000       1.000000   19312.000000      15.000000    8120.000000       1.000000        0.0  2.016041e+07   99999.000000      52.304178       7.320308      19.035496       9.854702       6.829352       0.291838       0.151420       1.404936       0.160791       0.222787      12.357011      18.819042      13.847792      11.147669       8.658418

2. 观察是否有缺省值

需要注意的是除了Nan以外,还有其他的异常值表达,比如999,9999-1 等值其实都是Nan的另外一种表达方式。
我们首先看一下训练集每个特征Nan的情况:

print(Train_data.isnull().sum())

其结果如下:

SaleID                  0
name                    0
regDate                 0
model                   1
brand                   0
bodyType             4506
fuelType             8680
gearbox              5981
power                   0
kilometer               0
notRepairedDamage       0
regionCode              0
seller                  0
offerType               0
creatDate               0
price                   0
v_0                     0
v_1                     0
v_2                     0
v_3                     0
v_4                     0
v_5                     0
v_6                     0
v_7                     0
v_8                     0
v_9                     0
v_10                    0
v_11                    0
v_12                    0
v_13                    0
v_14                    0
dtype: int64

测试集的特征Nan情况也与训练集类似

SaleID                  0
name                    0
regDate                 0
model                   0
brand                   0
bodyType             1413
fuelType             2893
gearbox              1910
power                   0
...
v_14                    0
dtype: int64

由此我们可以很清晰的观察到特征bodyType、fuelTypegearbox存在大量的Nan值。Nan存在的个数如果很小一般选择填充,如使用lgb等树模型可以直接空缺,让树自己去优化;但如果Nan存在的过多、可以考虑删掉。

3.查看是否有异常值

查看特征值的数据类型。

print(Train_data.info())

结果如下:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 31 columns):
 #   Column             Non-Null Count   Dtype  
---  ------             --------------   -----  
 0   SaleID             150000 non-null  int64  
 1   name               150000 non-null  int64  
 2   regDate            150000 non-null  int64  
 3   model              149999 non-null  float64
 4   brand              150000 non-null  int64  
 5   bodyType           145494 non-null  float64
 6   fuelType           141320 non-null  float64
 7   gearbox            144019 non-null  float64
 8   power              150000 non-null  int64  
 9   kilometer          150000 non-null  float64
 10  notRepairedDamage  150000 non-null  object 
 11  regionCode         150000 non-null  int64  
 12  seller             150000 non-null  int64  
 13  offerType          150000 non-null  int64  
 14  creatDate          150000 non-null  int64  
 15  price              150000 non-null  int64  
 16  v_0                150000 non-null  float64
 17  v_1                150000 non-null  float64
 18  v_2                150000 non-null  float64
 19  v_3                150000 non-null  float64
 20  v_4                150000 non-null  float64
 21  v_5                150000 non-null  float64
 22  v_6                150000 non-null  float64
 23  v_7                150000 non-null  float64
 24  v_8                150000 non-null  float64
 25  v_9                150000 non-null  float64
 26  v_10               150000 non-null  float64
 27  v_11               150000 non-null  float64
 28  v_12               150000 non-null  float64
 29  v_13               150000 non-null  float64
 30  v_14               150000 non-null  float64
dtypes: float64(20), int64(10), object(1)
memory usage: 35.5+ MB

可以发现除了notRepairedDamageobject类型其他都为数字(int或者float)。这里不难会产生疑问,notRepairedDamage是车是否损坏,只有0和1两种情况,为什么不是int类型呢?
我们将notRepairedDamage具体的值和数量显示一下看看:

print(Train_data['notRepairedDamage'].value_counts())

其结果如下所示:

0.0    111361
-       24324
1.0     14315
Name: notRepairedDamage, dtype: int64

我们可以看到,除了和我们想的有0和1之外,还有一个'-'值,我们可以理解为不知道该车是否损坏,因此我们可以将其当做Nan值处理。
下面我们'-'替换为Nan,并显示处理后的情况:

Train_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
print(Train_data['notRepairedDamage'].value_counts())

其结果如下:

0.0    111361
1.0     14315
Name: notRepairedDamage, dtype: int64

为了再次确认,我们再次查看训练集中缺省值的数目:

print(Train_data.isnull().sum())

结果如下:

SaleID                   0
name                     0
regDate                  0
model                    1
brand                    0
bodyType              4506
fuelType              8680
gearbox               5981
power                    0
kilometer                0
notRepairedDamage    24324
regionCode               0
...
v_14                     0
dtype: int64

由此可见,notRepairedDamage的缺省值已经从0变成了24324,我们已经成功替换为了缺省值nan
我们用相同的操作处理测试集中的notRepairedDamage,以下不再赘述。

4.删除对预测结果无帮助的特征

二、1中的统计量我们观察到有两个特征值可能出现了严重的倾斜。(部分截图如下)
二手车交易价格预测-Task2 数据分析
因此我们需要进一步验证一下这两个特征是否真的是无效的,代码如下所示:

print(Train_data['seller'].value_counts())
print()
print(Train_data['offerType'].value_counts())

结果如下:

0    149999
1         1
Name: seller, dtype: int64

0    150000
Name: offerType, dtype: int64

通过以上的计数显示,我们确定了我们的猜想,这两个特征值出现了严重的倾斜,对结果无帮助,因此删除。

del Train_data["seller"]
del Train_data["offerType"]
del Test_data["seller"]
del Test_data["offerType"]

print(Train_data.shape)

删除后我们再次看一下训练集的形状:(150000, 29),由此可见,我们已经完成了删除操作。

5.了解预测值的分布

我们使用库seabornscipy来拟合预测值price的分布情况,我们分别尝试了正态分布、*约翰逊分布和对数正态分布。

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)

plt.show()

结果如下图所示:

二手车交易价格预测-Task2 数据分析
由图我们易知,最拟合预测值price的是*约翰逊分布。

为了进一步处理预测值,我们先进一步看一下各个price的频数分布。

y = Train_data['price']
plt.hist(Train_data['price'], orientation = 'vertical',histtype = 'bar', color ='blue')
plt.show()
print('the highest price is:',max(y))

结果如下所示:the highest price is: 99,999
二手车交易价格预测-Task2 数据分析
由结果我们可知,价格的最大值为99,999,但是绝大多数的价格都不超过20,000,因此我们这里也可以把这些当作异常值删掉或者替换为20,000

?关于取对数的小trick还没明白具体怎么用,所以存疑,之后再补充?

6.特征分类和数字特征分析

这里也存疑,这特征不是手工分的吗?之前介绍的根据对象是否是object来输出有何意义?

特征分为类别特征和数字特征,本题中数字特征有: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']
而且我们知道预测值price也属于数字特征,因此将price加入numeric_features中。

numeric_features.append('price')

数字特征的相关性分析

直接调用corr()函数进行相关性分析

price_numeric = Train_data[numeric_features]
correlation = price_numeric.corr()
print(correlation['price'].sort_values(ascending = False),'\n')

结果如下:

price        1.000000
v_12         0.692823
v_8          0.685798
v_0          0.628397
power        0.219834
v_5          0.164317
v_2          0.085322
v_6          0.068970
v_1          0.060914
v_14         0.035911
v_13        -0.013993
v_7         -0.053024
v_4         -0.147085
v_9         -0.206205
v_10        -0.246175
v_11        -0.275320
kilometer   -0.440519
v_3         -0.730946
Name: price, dtype: float64 

相关性的可视化操作:

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)

结果如下所示:

二手车交易价格预测-Task2 数据分析
分析相关性确实是很不错的思路,但是之后呢,是否应该对某些相关性绝对值大于或者小于某些值的特征做处理呢?而且我认为,相关性取绝对值来观察可能会更好一点

数字特征之间的关系可视化:

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()

我们不再一一展示所有的特征之间的关系,但是从结果中我们可以观察到匿名特征'v_1''v_6'之间关系比较特殊,如下图所示:
二手车交易价格预测-Task2 数据分析
由图我们可知匿名特征'v_1''v_6'之间有非常强的线性相关性,因此是否可以将两者之一删掉。(存疑

7.类别特征分析

懂了几种可视图操作,只能说大概了解到各类别取值的频数,并未得到更多知识,期待后续对数据进一步的挖掘和处理。

三、学习总结

1.收获
①.通过观察数据集的形状并观察前几行来大概观测数据特征
②.通过相关统计量来大致了解数据的特征
③.关于缺省值和异常值的查找(通过显示特征的类型和各取值的频数)和处理(改为nan或者填充)
④.删除取值严重倾斜的特征
⑤.使用库seabornscipy来拟合预测值分布情况

2.存疑
①.关于预测值你和那个地方取对数后近似正态分布的小tirck不懂,有什么好处么?
②.关于特征的分类,最开始说的是按照对象是否是object类来分,到最后却是手工分的,前面按照object类是笔误还是有其他什么原因?
③.分析相关性确实是很不错的思路,但是之后呢?是否应该对某些相关性绝对值大于或者小于某些值的特征做处理呢?比起按照相关性,是不是将其取绝对值来观察可能会更好一点?
④.相关性特别强的特征该如何处理,能否删除掉其中一个?比如本题中的v_1v_6.
④.类别特征分析中只是介绍了几种可视图的操作,具体对之后的模型建立有何用处呢,期待后续对数据进一步的挖掘和处理。