Task 2 數據挖掘實戰-二手車交易價格預測-數據探索(EDA)

整體流程

數據探索,Exploratory Data Analysis, EDA

  1. 統計層面分析
  2. 缺失值處理
  3. 異常值處理
  4. Label分析
  5. 特徵分析

詳細步驟

1 載入庫

  1. warnings包,忽視警告
  2. 使用missingno包進行缺失值可視化處理,需安裝pip install missingno
import warnings
warnings.filterwarnings('ignore')
import missingno as msno  # 缺失值可視化處理

2 載入數據

  1. 數據中使用空格作爲各列數據間的分隔符,載入數據時加上sep參數
  2. pandas的append用法,可將數據拼接到一起
Train_data = pd.read_csv('used_car_train_20200313.csv', sep=' ')

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

3 總覽數據

  1. describe()函數直觀的看到部分變量的樣本值有缺失
  2. 通過info()函數了解數據每列的type,發現notRepairedDamage特徵的類型爲object,此變量表示汽車有尚未修復的損壞,取值爲0或1,但是在數據集中有部分樣的值爲-
Train_data.describe()

Train_data.info()

4 判斷數據缺失值

  1. 通過isnull().sum()查看每列nan的情況
  2. 對於nan的處理
    • 個數較少,使用填充,如果使用lgb等樹模型可以直接空缺,讓樹自己去優化
    • 個數較多,考慮刪除這個特徵
Train_data.isnull().sum()

Test_data.isnull().sum()
  1. 查看訓練集中有nan值的特徵的nan值分佈情況
missing = Train_data.isnull().sum()
missing = missing[missing > 0]
missing.sort_values(inplace=True)  # inpalce表示是否用排序後的數據集替換原數據
missing.plot.bar()
  1. 使用missingno庫 ,可視化缺省值
import missingno as msno
msno.matrix(Train_data.sample(250))

上圖中,左邊的縱軸從上至下表示第1-250個樣本,當一列中出現白色橫線時,表示這個樣本的這個特徵的值有缺失,然後在右側的縱軸上便會有一個波動,波動越大,說明此條樣本缺失的特徵越多。在右側的縱軸上會標出完整性最大的點和最小的點,上圖中的28應該表示第28個樣本的完整性最小,即缺失的特徵最多。

msno.bar(Train_data.sample(1000))
`msno.bar` 簡單的展示數據的完整度,右側縱軸表示第0-1000個樣本,左側縱軸表示樣本數據的完整度。

5 判斷數據異常值

  1. 處理notRepairedDamage:

通過顯示其中不同的值來觀察數據

Train_data['notRepairedDamage'].value_counts()

發現-爲缺失值,先換成nan處理

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

value_counts()函數不會統計nan的數量,使用 isnull().sum()函數來查看

Train_data.isnull().sum()

對測試集執行相同的操作

  1. 刪除訓練集和測試集中嚴重傾斜的特徵

使用value_counts()函數統計每個特徵的取值分佈情況,如:【參考 7 查看數據特徵 第2點】

Train_data['seller'].value_counts()

輸出:

    0    149999
    1         1
    Name: seller, dtype: int64

刪除特徵seller, offerType

del Train_data['seller']
del Train_data['offerType']
del Test_data['seller']
del Test_data['offerType']

6 瞭解預測值分佈

  1. 查看預測值基本信息
Train_data['price']
Train_data['price'].value_counts()
  1. 查看預測值總體分佈情況
import scipy.stats as st

y = Train_data['price']
plt.figure(1);plt.title('Johnson SU')
sns.distplot(y, kde=False, fit=st.johnsonsu)  # distplot繪製觀測值的單變量分佈。
plt.figure(2);plt.title('Normal')
sns.distplot(y, kde=False, fit=st.norm)  # kde參數:是否繪製高斯核密度估計。
plt.figure(3);plt.title('Log Normal')
sns.distplot(y, kde=False, fit=st.lognorm)

從上圖可知,橫軸表示樣本的各個價格,縱軸表示這一價格對應的樣本數量與樣本總數的比值。第一個圖中的曲線擬合情況最好,所以預測值服從無界約翰遜分佈,而不是服從正太分佈。關於無界約翰遜分佈(JohnsonSUDistribution)一些信息。

  1. 查看預測值頻數情況
plt.hist(Train_data['price'], orientation='vertical', histtype='bar', color='red')
plt.show()
從上圖可知:大於20000的值比較少,可以把這些當作異常值直接用填充,或者刪除掉。
plt.hist(np.log(Train_data['price']), orientation='vertical', histtype='bar', color='red')
plt.show()

從上圖可知:log變換預測值之後的分佈較均勻,可以用log變換進行預測(預測問題常用trick

  1. 查看偏度(Skewness)和峯度(Kurtosis)

預測值的偏度和峯度:

sns.distplot(Train_data['price']);
print('Skewness: %f' % Train_data['price'].skew())
print('Kurtosis: %f' % Train_data['price'].kurt())
Train_data.skew(), Train_data.kurt()

從上圖可知:預測值的分佈,呈現出右偏,尖頂峯

知識補充: 詳細見參考網站

  1. 用skewness和kurtosis來看數據的分佈形態,一般會和正態分佈比較,所以把正態分佈的skewness和kurtosis都看作0.
  2. 偏度:描述的是某總體取值分佈的對稱性
    (1)Skewness = 0 ,分佈形態與正態分佈偏度相同。
    (2)Skewness > 0 ,正偏差數值較大,爲正偏或右偏。長尾巴拖在右邊,數據右端有較多的極端值。
    (3)Skewness < 0 ,負偏差數值較大,爲負偏或左偏。長尾巴拖在左邊,數據左端有較多的極端值。
    (4)數值的絕對值越大,表明數據分佈越不對稱,偏斜程度大。
  3. 峯度:是數據分佈頂的尖銳程度
    (1)Kurtosis=0 與正態分佈的陡緩程度相同。
    (2)Kurtosis>0 比正態分佈的高峯更加陡峭——尖頂峯
    (3)Kurtosis<0 比正態分佈的高峯來得平臺——平頂峯

訓練集的偏度和峯度:

sns.distplot(Train_data.skew(), color='blue', axlabel='Skewness')
sns.distplot(Train_data.kurt(), color='orange', axlabel='Kurtness')

上圖中,以偏度圖爲例,橫軸表示數據集中所有特徵分佈的偏度值,縱軸表示擁有此分佈特徵分佈的偏度值的特徵數與總特徵數的比值。

7 查看數據特徵

  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']
  1. 查看各個特徵的unique分佈(特徵的各個值出現的次數)
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())

分別查看訓練集和測試集

數字特徵(連續)

  1. 數字特徵總覽
numeric_features.append('price')
numeric_features
  1. 相關性分析

計算相關係數

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)  # square:單元格爲方格 vmax:右側色條可見的最大數值
從上圖可知:方格顏色最淺或者最深,則此方格對應的兩個特徵之間相關性越大。
  1. 查看特徵的偏度和峯值
del price_numeric['price']

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())\
         )
  1. 每個數字特徵分佈
f = pd.melt(Train_data, value_vars=numeric_features)  # 將DataFrame從寬格式轉爲長格式
g = sns.FacetGrid(f, col="variable",  col_wrap=2, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")

這裏的圖的含義,和前述都差不多,橫軸表示這個特徵所有的取值,縱軸表示特徵值等這個取值的樣本數與總樣本數的比值。

  1. 數字特徵之間的關係分析
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()

從上圖可知:當兩個特徵之間存在較強的關係,會出現共線性,需要考慮去除共線性。

  1. 多變量互相迴歸關係可視化

可以根據前述的相關係數或者特徵關係矩陣,來挑選部分特徵再次進行迴歸分析。

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_13']
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)

從上圖可知:部分解釋變量與被解釋變量之間沒有太大關係,可以考慮刪除。

類別特徵(離散)

  1. 查看類別特徵的unique分佈
for fea in categorical_features:
    print(Train_data[fea].nunique())

categorical_features
  1. 箱型圖可視化特徵
# 因爲 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():  # 發現空值時,添加MISSING類,並且用”MISSING“來作爲nan填充
        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')

所以,由上圖可知,橫軸中會出現MISSING這一類。

  1. 類別特徵的小提琴圖可視化
catg_list = categorical_features
target = 'price'
for catg in catg_list:
    sns.violinplot(x=catg,y=target, data=Train_data)
    plt.show()
可以看到各個特徵的分佈情況。
  1. 類別特徵的柱形圖可視化
categorical_features = ['model',
 'brand',
 'bodyType',
 'fuelType',
 'gearbox',
 'notRepairedDamage']
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')

研究預測值與各個類別特徵的關係,給出某個特徵的每一個類別取值對應的預測值的平均數,以及”置信區間“(黑色的豎線)。

  1. 類別特徵的每個類別頻數可視化
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')

某個特徵的每一個類別出現的頻數。

8 生成數據報告

需要安裝pandas_profiling庫,可能出現安裝失敗的情況。有很多種解決辦法,詳見官網
我是用pip安裝失敗,用conda install -c conda-forge pandas-profiling 就可以了。

import pandas_profiling

pfr = pandas_profiling.ProfileReport(Train_data)
pfr.to_file('./report.html')

導出的效果不錯!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章