此部分為零基礎(chǔ)入門數(shù)據(jù)挖掘的 Task2 EDA-數(shù)據(jù)探索性分析部分。
數(shù)據(jù)探索在機(jī)器學(xué)習(xí)中我們一般稱為EDA(Exploratory Data Analysis):是指對已有的數(shù)據(jù)(特別是調(diào)查或觀察得來的原始數(shù)據(jù))在盡量少的先驗假定下進(jìn)行探索,通過作圖、制表、方程擬合、計算特征量等手段探索數(shù)據(jù)的結(jié)構(gòu)和規(guī)律的一種數(shù)據(jù)分析方法。
EDA過程如下:
1. 載入各種數(shù)據(jù)科學(xué)以及可視化庫
#導(dǎo)入warnings包,利用過濾器來實現(xiàn)忽略警告語句。
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
2. 載入數(shù)據(jù)
## 1) 載入訓(xùn)練集和測試集;
path = './datalab/231784/'
Train_data = pd.read_csv(path+'used_car_train_20200313.csv', sep=' ')
Test_data = pd.read_csv(path+'used_car_testA_20200313.csv', sep=' ')
## 2) 簡略觀察數(shù)據(jù) head()+shape
Train_data.head().append(Train_data.tail())
Train_data.shape
Test_data.head().append(Test_data.tail())
Test_data.shape
3. 數(shù)據(jù)總覽
## 1) 通過describe()來熟悉數(shù)據(jù)的相關(guān)統(tǒng)計量
Train_data.describe()
Test_data.describe()
## 2) 通過info()來熟悉數(shù)據(jù)類型
Train_data.info()
Test_data.info()
說明:describe()函數(shù)可以返回每列的統(tǒng)計量:個數(shù)count、平均值mean、方差std、最小值min、中位數(shù)25% 50% 75% 以及最大值等信息??催@個信息主要是瞬間掌握數(shù)據(jù)的大概的范圍以及每個值的異常值的判斷,比如有的時候會發(fā)現(xiàn)999 9999 -1 等值這些其實都是nan的另外一種表達(dá)方式,有的時候需要注意下。
info()函數(shù)可以了解數(shù)據(jù)每列的type,有助于了解是否存在除了nan以外的特殊符號異常。
4. 判斷數(shù)據(jù)缺失和異常情況
4.1. 查看每列的存在nan情況
## 1) 查看每列的存在nan情況
Train_data.isnull().sum()
Test_data.isnull().sum()
# nan可視化
missing = Train_data.isnull().sum()
missing = missing[missing > 0]
missing.sort_values(inplace=True)
missing.plot.bar()
# 可視化看下缺省值
msno.matrix(Train_data.sample(250))
msno.bar(Train_data.sample(1000))
msno.matrix(Test_data.sample(250))
msno.bar(Test_data.sample(1000))
#結(jié)果:測試集和訓(xùn)練集相同, 可視化有四列有缺省,notRepairedDamage缺省得最多。
說明:isnull()函數(shù)可以用來判斷缺失值。
4.2. 查看異常值情況
## 2) 查看異常值檢測
Train_data.info()
# 可以發(fā)現(xiàn)除了notRepairedDamage 為object類型其他都為數(shù)字。
Train_data['notRepairedDamage'].value_counts()
# 這里我們把它的幾個不同的值都進(jìn)行顯示,可以看出‘ - ’也為空缺值。
Train_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
# 因為很多模型對nan有直接的處理,這里我們先不做處理,先替換成nan。
#對Test數(shù)據(jù)集做同樣處理
Test_data.info()
Test_data['notRepairedDamage'].value_counts()
Test_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
5. 了解預(yù)測值的分布
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)
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)
#結(jié)果:價格不服從正態(tài)分布,所以在進(jìn)行回歸之前,它必須進(jìn)行轉(zhuǎn)換。
#雖然對數(shù)變換做得很好,但最佳擬合是無界約翰遜分布。
說明:
Johnson SU:簡稱約翰遜分布。經(jīng)約翰變換后服從正態(tài)分布的隨機(jī)變量的概率分布。
Normal:正態(tài)分布。
Log Normal:對數(shù)正態(tài)分布。
## 2) 查看skewness and 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()
sns.distplot(Train_data.skew(),color='blue',axlabel ='Skewness')
sns.distplot(Train_data.kurt(),color='orange',axlabel ='Kurtness')
說明:我們一般會拿偏度skew和峰度kurt來看數(shù)據(jù)的分布形態(tài),而且一般會跟正態(tài)分布做比較,我們把正態(tài)分布的偏度和峰度都看做零。
偏度:是描述數(shù)據(jù)分布形態(tài)的統(tǒng)計量,其描述的是某總體取值分布的對稱性,簡單來說就是數(shù)據(jù)的不對稱程度。
峰度:是描述某變量所有取值分布形態(tài)陡緩程度的統(tǒng)計量,簡單來說就是數(shù)據(jù)分布頂?shù)募怃J程度。
## 3) 查看預(yù)測值的具體頻數(shù)
plt.hist(Train_data['price'], orientation = 'vertical',histtype = 'bar', color ='red')
plt.show()
#結(jié)果:查看頻數(shù), 大于20000得值極少。
#其實這里也可以把這些當(dāng)作特殊得值(異常值)直接用填充或者刪掉,在前面進(jìn)行。
# log變換 z之后的分布較均勻,可以進(jìn)行l(wèi)og變換進(jìn)行預(yù)測,這也是預(yù)測問題常用的trick
plt.hist(np.log(Train_data['price']), orientation = 'vertical',histtype = 'bar', color ='red')
plt.show()
說明:hist()列出的是直方圖。
6. 對特征進(jìn)行分類:數(shù)字特征、類別特征
數(shù)據(jù)特征可分為兩類:數(shù)字特征和類別特征。
這里需要進(jìn)行人為分類:
# 數(shù)字特征
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',]
另外一種常用的自動分類方法為:
# 這個區(qū)別方式適用于沒有直接label coding的數(shù)據(jù)
# 這里不適用,需要人為根據(jù)實際含義來區(qū)分
# 數(shù)字特征
numeric_features = Train_data.select_dtypes(include=[np.number])
numeric_features.columns
# 類型特征
categorical_features = Train_data.select_dtypes(include=[np.object])
categorical_features.columns
7. 數(shù)字特征分析
7.1 相關(guān)性分析
numeric_features.append('price')
numeric_features
## 1) 相關(guān)性分析
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)
說明:sns.heatmap()畫出熱力圖來進(jìn)行相關(guān)性分析。熱力圖在實際中常用于展示一組變量的相關(guān)系數(shù)矩陣,在展示列聯(lián)表的數(shù)據(jù)分布上也有較大的用途,通過熱力圖我們可以非常直觀地感受到數(shù)值大小的差異狀況。
7.2 查看幾個特征的偏度和峰值
## 2) 查看幾個特征的偏度和峰值
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())
)
7.3 數(shù)字特征的分布可視化
## 3) 每個數(shù)字特征的分布可視化
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")
說明:pd.melt()用來規(guī)整數(shù)據(jù),F(xiàn)acetGrid()、map()用來構(gòu)建結(jié)構(gòu)化多繪圖網(wǎng)格。
7.4 數(shù)字特征相互之間的關(guān)系可視化
## 4) 數(shù)字特征相互之間的關(guān)系可視化
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()
說明:pairplot中pair是成對的意思,pairplot主要展現(xiàn)的是變量兩兩之間的關(guān)系(線性或非線性,有無較為明顯的相關(guān)關(guān)系)
7.5 多變量互相回歸關(guān)系可視化
## 5) 多變量互相回歸關(guān)系可視化
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)
說明:sns.regplot()可視化線性回歸關(guān)系圖。
8. 類別特征分析
8.1 類別特征的unique分布
## 1) unique分布
for fea in categorical_features:
print(Train_data[fea].nunique())
說明:pandas 的 unique()函數(shù)與nunique()函數(shù)區(qū)別:
unique()函數(shù)是以數(shù)組形式(numpy.ndarray)返回列的所有唯一值(特征的所有唯一值)。
nunique() 函數(shù)Return number of unique elements in the object.即返回的是唯一值的個數(shù)。
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")
說明:箱形圖(Box-plot)又稱為盒須圖、盒式圖或箱線圖,是一種用作顯示一組數(shù)據(jù)分散情況資料的統(tǒng)計圖。它能顯示出一組數(shù)據(jù)的最大值、最小值、中位數(shù)及上下四分位數(shù)。
8.3 類別特征的小提琴圖可視化
## 3) 類別特征的小提琴圖可視化
catg_list = categorical_features
target = 'price'
for catg in catg_list :
sns.violinplot(x=catg, y=target, data=Train_data)
plt.show()
說明:violinplot與boxplot扮演類似的角色,它顯示了定量數(shù)據(jù)在一個(或多個)分類變量的多個層次上的分布,這些分布可以進(jìn)行比較。不像箱形圖中所有繪圖組件都對應(yīng)于實際數(shù)據(jù)點,小提琴繪圖以基礎(chǔ)分布的核密度估計為特征。
8.4 類別特征的柱形圖可視化
## 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")
8.5 類別特征的類別頻數(shù)可視化
## 5) 類別特征的每個類別頻數(shù)可視化(count_plot)
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")
9. 用pandas_profiling生成數(shù)據(jù)報告
import pandas_profiling
pfr = pandas_profiling.ProfileReport(Train_data)
pfr.to_file("./example.html")