最近博主在作個 kaggle 競賽,有個 Kernel 的數據探索分析很是值得借鑑,博主也學習了一波操做,搬運過來借鑑,原連接以下:程序員
https://www.kaggle.com/willkoehrsen/start-here-a-gentle-introduction安全
數據由Home Credit提供,該服務致力於向無銀行帳戶的人羣提供信貸(貸款)。預測客戶是否償還貸款或遇到困難是一項重要的業務需求,Home Credit將在Kaggle上舉辦此類競賽,以瞭解機器學習社區能夠開展哪些模式以幫助他們完成此任務。app
有7種不一樣的數據來源:機器學習
application_train / application_test:函數
主要的培訓和測試數據以及關於Home Credit每一個貸款申請的信息。每筆貸款都有本身的行,並由功能SK_ID_CURR標識。培訓申請數據附帶TARGET表示0:貸款已還清或1:貸款未還清。學習
bureau:測試
有關客戶以前來自其餘金融機構的信貸的數據。之前的每一筆信貸都有本身的分行,但申請數據中的一筆貸款可能有多筆先前信貸。編碼
bureau_balance:spa
關於主管局之前信用的月度數據。每一行都是上一個信用的一個月,而且一個先前的信用能夠有多個行,每一個信用額度的每月有一個行。code
previous_application:
以前在申請數據中擁有貸款的客戶的Home Credit貸款申請。申請數據中的每一個當前貸款均可以有多個之前的貸款。每一個之前的應用程序都有一行,並由功能SK_ID_PREV標識。
POS_CASH_BALANCE:關於客戶之前的銷售點或現金貸款與住房貸款有關的每個月數據。每一行都是前一個銷售點或現金貸款的一個月,之前的一筆貸款能夠有多行。
credit_card_balance:
有關以前的信用卡客戶與Home Credit有關的每個月數據。每行都是信用卡餘額的一個月,一張信用卡能夠有多行。
installments_payment:
Home Credit之前貸款的付款記錄。每筆付款都有一行,每筆未付款都有一行。
下圖顯示了全部數據是如何相關的:
# numpy and pandas for data manipulation
import numpy as np
import pandas as pd
# sklearn preprocessing for dealing with categorical variables
from sklearn.preprocessing import LabelEncoder
# File system manangement
import os
# Suppress warnings
import warnings
warnings.filterwarnings('ignore')
# matplotlib and seaborn for plotting
import matplotlib.pyplot as plt
import seaborn as sns
app_train = pd.read_csv('../input/application_train.csv')
app_test = pd.read_csv('../input/application_test.csv')
app_train['TARGET'].value_counts()
app_train['TARGET'].astype(int).plot.hist();
根據這些信息,咱們發現這是一個不平衡的課堂問題。 按時還款的貸款遠遠多於未還款的貸款。 一旦咱們進入更復雜的機器學習模型,咱們能夠經過它們在數據中的表示來對類進行加權以反映這種不平衡。
# Function to calculate missing values by column# Funct
def missing_values_table(df):
# Total missing values
mis_val = df.isnull().sum()
# Percentage of missing values
mis_val_percent = 100 * df.isnull().sum() / len(df)
# Make a table with the results
mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)
# Rename the columns
mis_val_table_ren_columns = mis_val_table.rename(
columns = {0 : 'Missing Values', 1 : '% of Total Values'})
# Sort the table by percentage of missing descending
mis_val_table_ren_columns = mis_val_table_ren_columns[
mis_val_table_ren_columns.iloc[:,1] != 0].sort_values(
'% of Total Values', ascending=False).round(1)
# Print some summary information
print ("Your selected dataframe has " + str(df.shape[1]) + " columns.\n"
"There are " + str(mis_val_table_ren_columns.shape[0]) +
" columns that have missing values.")
# Return the dataframe with missing information
return mis_val_table_ren_columns
# Missing values statistics
missing_values = missing_values_table(app_train)
missing_values.head(20)
當須要創建咱們的機器學習模型時,咱們將不得不填寫這些缺失值(稱爲插補)。 在之後的工做中,咱們將使用XGBoost等模型,能夠處理缺失值而不須要插補。 另外一種選擇是刪除缺失值比例較高的列,但若是這些列對咱們的模型有幫助,則不可能提早知道。 所以,咱們如今將保留全部列。
# Number of each type of column
app_train.dtypes.value_counts()
如今咱們來看看每一個對象(分類)列中惟一條目的數量。
app_train.select_dtypes('object').apply(pd.Series.nunique, axis = 0)
大多數分類變量的惟一條目數量相對較少。 咱們須要找到一種方法來處理這些分類變量!
在咱們繼續前進以前,咱們須要處理討厭的分類變量。 不幸的是,機器學習模型不能處理分類變量(除了LightGBM等一些模型)。 所以,咱們必須找到一種將這些變量編碼(表示)爲數字的方式,而後再將它們交給模型。 有兩種主要的方法來執行這個過程:
標籤編碼的問題在於它給了類別一個任意的順序。 分配給每一個類別的值是隨機的,並不反映該類別的任何固有方面。 在上面的例子中,程序員收到一個4和數據科學家a 1,但若是咱們再次執行相同的過程,標籤可能會顛倒或徹底不一樣。 整數的實際賦值是任意的。 所以,當咱們執行標籤編碼時,模型可能會使用特徵的相對值(例如程序員= 4和數據科學家= 1)來分配不是咱們想要的權重。 若是咱們對於分類變量(例如男性/女性)只有兩個惟一值,那麼標籤編碼很好,但對於超過2個獨特類別,單熱編碼是安全的選擇。
關於這些方法的相對優勢存在一些爭議,而且一些模型能夠處理標籤編碼的分類變量而沒有問題。 這是一個很好的Stack Overflow討論。 我認爲(這只是一種我的觀點)對於有不少類的分類變量,單熱編碼是最安全的方法,由於它不會對類別強加任意值。 單熱編碼惟一的缺點是特徵的數量(數據的維數)會隨着分類變量的分類而爆炸。 爲了解決這個問題,咱們能夠執行一個熱門的編碼,而後是 PCA 或其餘降維方法,以減小維數(同時仍試圖保留信息)。
咱們將使用標籤編碼來處理任何僅有2個類別的分類變量,對於超過2個類別的任何分類變量使用單向編碼。
讓咱們實現上面描述的策略:對於具備2個惟一類別的任何分類變量(dtype ==對象),咱們將使用標籤編碼,對於具備多於2個惟一類別的任何分類變量,咱們將使用單熱編碼。
對於標籤編碼,咱們使用Scikit-Learn LabelEncoder和一個熱門編碼,即熊貓get_dummies(df)函數。
# Create a label encoder object
le = LabelEncoder()
le_count = 0
# Iterate through the columns
for col in app_train:
if app_train[col].dtype == 'object':
# If 2 or fewer unique categories
if len(list(app_train[col].unique())) <= 2:
# Train on the training data
le.fit(app_train[col])
# Transform both training and testing data
app_train[col] = le.transform(app_train[col])
app_test[col] = le.transform(app_test[col])
# Keep track of how many columns were label encoded
le_count += 1
print('%d columns were label encoded.' % le_count)
app_train = pd.get_dummies(app_train)
app_test = pd.get_dummies(app_test)
print('Training Features shape: ', app_train.shape)
print('Testing Features shape: ', app_test.shape)
訓練和測試數據中都須要具備相同的特徵(列)。 一次性編碼在訓練數據中建立了更多列,由於有一些分類變量的類別未在測試數據中表示。 要刪除訓練數據中不在測試數據中的列,咱們須要對齊數據框。 首先,咱們從訓練數據中提取目標列(由於這不在測試數據中,但咱們須要保留這些信息)。 當咱們進行對齊時,咱們必須確保將axis = 1設置爲基於列而不是在行上對齊數據框!
train_labels = app_train['TARGET']
# Align the training and testing data, keep only columns present in both dataframes
app_train, app_test = app_train.align(app_test, join = 'inner', axis = 1)
# Add the target back in
app_train['TARGET'] = train_labels
print('Training Features shape: ', app_train.shape)
print('Testing Features shape: ', app_test.shape)
訓練和測試數據集如今具備機器學習所需的相同特徵。 因爲單熱編碼,功能數量顯着增長。 在某些狀況下,咱們可能會嘗試降維(刪除不相關的功能)以減少數據集的大小。
在探索數據的時候須要注意的一個問題是數據中的異常狀況。 這多是因爲錯誤的數字,測量設備的錯誤,或者它們多是有效的,但極端的測量。 定量支持異常的一種方法是使用描述方法查看列的統計數據。 DAYS_BIRTH列中的數字是負數,由於它們是相對於當前貸款申請記錄的。 要查看這些年份的統計數據,咱們能夠用-1來多項式除以一年中的天數:
(app_train['DAYS_BIRTH'] / -365).describe()
那些年齡看起來合理。 高端或低端的年齡沒有異常值。 就業日期如何?
app_train['DAYS_EMPLOYED'].describe()
這看起來不正確! 最大值(除了正值)大約是1萬年!
app_train['DAYS_EMPLOYED'].plot.hist(title = 'Days Employment Histogram');
plt.xlabel('Days Employment');
出於好奇,讓咱們對異常客戶進行分類,看看他們是否傾向於比其餘客戶的違約率更高或更低。
anom = app_train[app_train['DAYS_EMPLOYED'] == 365243]
non_anom = app_train[app_train['DAYS_EMPLOYED'] != 365243]
print('The non-anomalies default on %0.2f%% of loans' % (100 * non_anom['TARGET'].mean()))
print('The anomalies default on %0.2f%% of loans' % (100 * anom['TARGET'].mean()))
print('There are %d anomalous days of employment' % len(anom))
很是有趣! 事實證實,異常狀況的違約率較低。
處理異常狀況取決於具體狀況,沒有設置規則。 最安全的方法之一就是將異常設置爲缺失值,而後在機器學習以前填充(使用插補)。 在這種狀況下,因爲全部的異常都具備徹底相同的價值,咱們但願用相同的價值填充它們以防全部這些貸款共享相同的東西。 異常值彷佛有一些重要性,因此咱們想告訴機器學習模型,若是咱們確實填寫了這些值。 做爲一個解決方案,咱們將用不是一個數字(np.nan)填充異常值,而後建立一個新的布爾列來指示值是否異常。
# Create an anomalous flag column
app_train['DAYS_EMPLOYED_ANOM'] = app_train["DAYS_EMPLOYED"] == 365243
# Replace the anomalous values with nan
app_train['DAYS_EMPLOYED'].replace({365243: np.nan}, inplace = True)
app_train['DAYS_EMPLOYED'].plot.hist(title = 'Days Employment Histogram');
plt.xlabel('Days Employment');
這個分佈看起來更符合咱們的預期,並且咱們還建立了一個新的列,告訴模型這些值最初是異常的(由於咱們將不得不用一些值填補nans,多是中值的列)。 數據框中DAYS的其餘列看起來與咱們所指望的沒有明顯的異常值有關。
做爲一個很是重要的筆記,咱們對訓練數據所作的任何事情,咱們也必須對測試數據進行處理。 讓咱們確保建立新列,並在測試數據中用np.nan填充現有列。
app_test['DAYS_EMPLOYED_ANOM'] = app_test["DAYS_EMPLOYED"] == 365243
app_test["DAYS_EMPLOYED"].replace({365243: np.nan}, inplace = True)
print('There are %d anomalies in the test data out of %d entries' % (app_test["DAYS_EMPLOYED_ANOM"].sum(), len(app_test)))
如今咱們已經處理了分類變量和異常值, 嘗試和理解數據的一種方法是經過查找特徵和目標之間的相關性。 咱們可使用.corr數據框方法計算每一個變量和目標之間的 Pearson 相關係數。
相關係數並非表示某個要素「相關性」的最佳方法,但它確實給了咱們一個關於數據內可能關係的想法。 對相關係數絕對值的一些通常解釋是:
# Find correlations with the target and sort
correlations = app_train.corr()['TARGET'].sort_values()
# Display correlations
print('Most Positive Correlations:\n', correlations.tail(15))
print('\nMost Negative Correlations:\n', correlations.head(15))
咱們來看看更重要的一些相關性:DAYS_BIRTH是最正相關的。 (TARGET除外,由於變量與其自己的相關性始終爲1!)查看文檔,DAYS_BIRTH是負很多天期內客戶在貸款時間的天數(不管出於何種緣由!)。 這種相關性是正向的,可是這個特徵的價值其實是負的,這意味着隨着客戶年齡的增加,他們不太可能違約(即目標== 0)。 這有點使人困惑,所以咱們將採用該特徵的絕對值,而後相關性將爲負值。
# Find the correlation of the positive days since birth and target
app_train['DAYS_BIRTH'] = abs(app_train['DAYS_BIRTH'])
app_train['DAYS_BIRTH'].corr(app_train['TARGET'])
隨着客戶年齡的增加,與目標意義呈負相關關係,即隨着客戶年齡的增加,他們每每會更常常地按時償還貸款。咱們開始看這個變量。 首先,咱們能夠製做年齡的直方圖。 咱們將把x軸放在幾年,使情節更容易理解。
# Set the style of plots
plt.style.use('fivethirtyeight')
# Plot the distribution of ages in years
plt.hist(app_train['DAYS_BIRTH'] / 365, edgecolor = 'k', bins = 25)
plt.title('Age of Client'); plt.xlabel('Age (years)'); plt.ylabel('Count');
就其自己而言,年齡的分佈並無告訴咱們不少,除了全部年齡都是合理的,沒有異常值。 爲了觀察年齡對目標的影響,咱們接下來將製做一個核心密度估計圖(KDE),該圖由目標值着色。 核密度估計圖顯示單個變量的分佈,能夠認爲是平滑直方圖(它是經過在每一個數據點上計算內核(一般爲高斯)建立的,而後對全部單個內核進行平均以造成單個平滑 曲線)。 咱們將爲此圖使用 seaborn kdeplot。
plt.figure(figsize = (10, 8))
# KDE plot of loans that were repaid on time
sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, 'DAYS_BIRTH'] / 365, label = 'target == 0')
# KDE plot of loans which were not repaid on time
sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, 'DAYS_BIRTH'] / 365, label = 'target == 1')
# Labeling of plot
plt.xlabel('Age (years)'); plt.ylabel('Density'); plt.title('Distribution of Ages');
目標== 1曲線朝向範圍的年輕端傾斜。 雖然這不是顯著的相關性(-0.07相關係數),但這個變量在機器學習模型中極可能會有用,由於它確實會影響目標。 讓咱們從另外一個角度來看待這種關係:平均未按年齡段償還貸款。
爲了製做這個圖表,咱們首先將年齡類別分爲5年的垃圾箱。 而後,對於每一個垃圾箱,咱們計算目標的平均值,它告訴咱們在每一個年齡段中沒有償還的貸款的比例。
# Age information into a separate dataframe
age_data = app_train[['TARGET', 'DAYS_BIRTH']]
age_data['YEARS_BIRTH'] = age_data['DAYS_BIRTH'] / 365
# Bin the age data
age_data['YEARS_BINNED'] = pd.cut(age_data['YEARS_BIRTH'], bins = np.linspace(20, 70, num = 11))
age_data.head(10)
age_groups = age_data.groupby('YEARS_BINNED').mean()
age_groups
plt.figure(figsize = (8, 8))
# Graph the age bins and the average of the target as a bar plot
plt.bar(age_groups.index.astype(str), 100 * age_groups['TARGET'])
# Plot labeling
plt.xticks(rotation = 75); plt.xlabel('Age Group (years)'); plt.ylabel('Failure to Repay (%)')
plt.title('Failure to Repay by Age Group');
有一個明顯的趨勢:年輕的申請人更有可能不償還貸款! 年齡最小的三個年齡組的失敗率在10%以上,最老的年齡組爲5%。這是銀行能夠直接使用的信息:由於年輕的客戶不太可能償還貸款,可能應該爲他們提供更多的指導或財務規劃提示。 這並不意味着銀行應該歧視年輕的客戶,但採起預防措施幫助年輕客戶按時付款是明智之舉。
咱們來看看這些變量。
首先,咱們能夠顯示「EXT_SOURCE」特徵與目標和相互之間的相關性。
# Extract the EXT_SOURCE variables and show correlations
ext_data = app_train[['TARGET', 'EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]
ext_data_corrs = ext_data.corr()
ext_data_corrs
# Extract the EXT_SOURCE variables and show correlations
ext_data = app_train[['TARGET', 'EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]
ext_data_corrs = ext_data.corr()
ext_data_corrs
全部三個EXT_SOURCE特徵與目標都有負相關,代表隨着EXT_SOURCE的價值增長,客戶更有可能償還貸款。 咱們還能夠看到,DAYS_BIRTH與EXT_SOURCE_1正相關,代表多是此分數中的一個因素是客戶年齡。
接下來咱們能夠看一下這些目標值的顏色的分佈。 這將讓咱們可視化這個變量對目標的影響。
plt.figure(figsize = (10, 12))
# iterate through the sources
for i, source in enumerate(['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3']):
# create a new subplot for each source
plt.subplot(3, 1, i + 1)
# plot repaid loans
sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, source], label = 'target == 0')
# plot loans that were not repaid
sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, source], label = 'target == 1')
# Label the plots
plt.title('Distribution of %s by Target Value' % source)
plt.xlabel('%s' % source); plt.ylabel('Density');
plt.tight_layout(h_pad = 2.5)
EXT_SOURCE_3顯示目標值之間的最大差別。 咱們能夠清楚地看到,這一特徵與申請人償還貸款的可能性有必定關係。 這種關係不是很強(事實上它們都被認爲是很是弱的,可是這些變量對於機器學習模型來講仍然是有用的,能夠預測申請人是否按時償還貸款。