天天看點

愛數課實驗 | 第七期-基于随機森林的金融危機分析

愛數課實驗 | 第七期-基于随機森林的金融危機分析

愛數課:idatacourse.cn

領域:其他

簡介:上世紀60年代之後,非洲掀起了擺脫殖民主義的獨立浪潮。由于幾百年的曆史原因,非洲大陸多數國家經濟發展較為落後,經濟體系較脆弱,各種危機時常發生。該案例對近百年來非洲13個國家的金融危機進行了探索性分析,并建構了随機森林模型進行預測。

資料:

./dataset/african_crises.csv

./dataset/SimHei.ttf

目錄

愛數課實驗 | 第七期-基于随機森林的金融危機分析

資料共計1059條,各資料字段含義如下表所示:

字段 含義
case 國家編号,代表特定國家的數字
cc3 國家代碼,三個字母的國家/地區代碼
country 國家名稱
year 觀測年份
systemic_crisis 系統性危機,“ 0”表示當年未發生系統性危機,“ 1”表示當年有發生系統性危機
exch_usd 該國貨币兌美元的匯率
domestic_debt_in_default 國内債務違約,“0”表示當年未發生國内債務違約,“1”表示當年有發生國内債務違約
sovereign_external_debt_default 主權外債違約,“0”表示當年未發生主權外債違約,“1”表示當年有發生主權外債違約
gdp_weighted_default 違約債務總額與GDP之比
inflation_annual_cpi 年度CPI通貨膨脹率
independence 獨立性,“ 0”表示“無獨立性”,“ 1”表示“獨立性”
currency_crises 貨币危機,“ 0”表示當年未發生“貨币危機”,“ 1”表示當年有發生“貨币危機”
inflation_crises 通脹危機,“ 0”表示當年未發生“通脹危機”,“ 1”表示當年有發生“通脹危機”
banking_crisis 銀行業危機,“ no_crisis”表示當年沒有發生銀行業危機,而“ crisis”表示當年有發生銀行業危機

1. 資料讀取與預處理

1.1 讀取資料

# 導入相應子產品
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
# 設定字型
font = FontProperties(fname = "./dataset/SimHei.ttf", size=14)

import seaborn as sns
import random

# 設定繪圖風格
%matplotlib inline
sns.set(style='whitegrid')

# 忽略所有警告
import warnings
warnings.filterwarnings('ignore')

# 讀取資料
data = pd.read_csv('./dataset/african_crises.csv')
data.sample(5)      

1.3 檢視資料基本資訊

首先我們檢視一共有哪些國家。

unique_countries = data.country.unique()
unique_countries      

可以看到資料中一共包含13個非洲國家,按順序分别為阿爾及利亞,安哥拉,中非共和國,象牙海岸,埃及,肯亞,模裡西斯,摩洛哥, 奈及利亞,南非,突尼西亞,尚比亞和辛巴維(威)。接下來我們使用描述性統計函數檢視資料的基本情況以及資料中是否存在缺失和異常。

# 資料集的基本資訊
data.______()      

可以看到除了國家代碼​

​cc3​

​​、國家名稱​

​country​

​​和銀行危機​

​banking_crisis​

​這三個字段為字元型之外,其餘均為數值類型,且資料中不存在缺失值。

# 檢視資料統計性名額
data.____________(include = 'all')      

通過觀察統計性名額,我們看到年份​

​year​

​​的最大最小值分别為2014年和1860年,埃及的統計記錄最多,有155條資料。同時我們也發現一個異常,貨币危機​

​currency_crises​

​的取值範圍為0、1,但資料中出現了取值2,我們需要單獨進行處理,且其餘名額無明顯異常。

1.3 資料預處理

#檢視貨币危機currency_crises的取值為2的資料
data[data['currency_crises'] == 2]       

可以看到存在異常的資料隻有4條,我們直接進行删除。

data = data[data['currency_crises'] != 2]# 得到生成删除貨币危機currency_crises的取值為2的資料集
data.______ # 檢視新生成的資料集大小      

2. 經濟名額探索性分析

在二戰之後,當今世界的格局初步形成,在60年代之後,非洲掀起了擺脫殖民主義的獨立浪潮。由于幾百年的曆史原因,非洲大陸是地球上發展最落後的地區,多數國家經濟政治發展較為落後,人口素質較低,經濟體系較為脆弱,各種危機時常發生。接下來我們通過該資料集分析一下在獨立前後的各個國家的經濟發展以及面臨經濟危機的情況。首先我們繪制13個國家的貨币兌美元的匯率變化情況的折線圖。

2.1 貨币兌美元的匯率變化情況

plt.figure(figsize=(12,20))

for i in range(13):
    
    plt.subplot(7,2,i+1)
    country = unique_countries[i]
    
    # 随機生成一種顔色 random.choice():從一個序列中随機的抽取一個元素,抽取6次組成6位代表随機顔色
    col="#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
    
    # 繪制折線圖
    sns.____________(data[data.country == country]['year'],data[data.country == country]['exch_usd'],label = country,color = col)
    
    # np.logical_and()邏輯與 兩個條件均成立時傳回True
    plt.plot([np.min(data[np.logical_and(data.country == country,data.independence == 1)]['year']),
              np.min(data[np.logical_and(data.country == country,data.independence == 1)]['year'])],
             [0,np.max(data[data.country == country]['exch_usd'])],color = 'black',linestyle = 'dotted',alpha = 0.8)
    

    plt.______(country) # 添加圖像标題
    
plt.tight_layout() # 自動調整子圖參數以提供指定的填充
plt.show() # 輸出13個國家的貨币兌美元的匯率變化情況的折線圖      

可以看到,大部分國家在在獨立前和獨立之後的短期階段并沒有自己的貨币體系,仍沿用殖民統治國家的貨币,如法郎或英鎊。安哥拉(Angola)、辛巴維(威)(Zimbabwe)、尚比亞(Zambia)、奈及利亞(Nigeria)等國匯率長期維持在0,在21世紀左右開始有了本國貨币。突尼西亞(Tunisia)在獨立後匯率驟然下降,長期穩定在1:1左右。另外還能發現大部分非洲國家随着時間的發展,以美元為基準,貨币呈現逐漸貶值的狀态。

2.2 通貨膨脹率變化情況

通貨膨脹率也稱為物價變化率,是貨币超發部分與實際需要的貨币量之比,用以反映通貨膨脹、貨币貶值的程度。通過價格指數的增長率來計算通貨膨脹率,在本資料中采用的是消費者價格指數(CPI)來進行表示的。

按照價格上升的速度加以區分:

  • 溫和的通貨膨脹(每年物價上升比例在1%~6%之内)
  • 嚴重的通貨膨脹(每年物價上升比例在6%~9%)
  • 飛奔的通貨膨脹(每年物價上升比例在10%~50%以下)
  • 惡性的通貨膨脹(每年物價上升比例在50%以上)

接下來我們分析一下各國的通貨膨脹率變化情況。

plt.figure(figsize=(12,20))

for i in range(13):
    
    plt.subplot(7,2,i+1)
    country = unique_countries[i]
    
    # 随機生成一種顔色
    col="#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
    
    # 繪制折線圖
    sns.lineplot(data[data.country == country]['year'],data[data.country == country]['inflation_annual_cpi'],label = country,color = col)
    
    # 加入散點圖
    plt.______(data[data.country == country]['year'],data[data.country == country]['inflation_annual_cpi'],color = col,s = 28) # s指散點的面積
    
    plt.plot([np.min(data[np.logical_and(data.country == country,data.independence==1)]['year']),
              np.min(data[np.logical_and(data.country == country,data.independence==1)]['year'])],
             [np.min(data[data.country == country]['inflation_annual_cpi']),np.max(data[data.country == country]['inflation_annual_cpi'])],
             color = 'black',linestyle = 'dotted',alpha = 0.8) # alpha指顔色透明度

    plt.title(country)
    
plt.tight_layout() # 自動調整子圖參數以提供指定的填充
plt.show() # 輸出13個國家的通貨膨脹率變化情況      

可以看到這些非洲國家中大部分均出現了不同程度的通貨膨脹,如南非(South Africa)在1970-1990之間由于種族隔離政策不斷被西方經濟體制裁導緻經濟受到大幅度影響;安哥拉(Angola)在20世紀90年代發生多次内戰,戰争導緻通貨膨脹率瘋狂飙升,在最高一年達到4000以上。也有部分國家經濟較為穩定,如突尼西亞(Tunisa)獨立後,通貨膨脹率經過短暫上升又逐漸下降至較低水準并保持穩定。

2.3 其他危機分布情況

接下來我們分析一下資料的其他字段:不同國家的系統性危機​

​systemic_crisis​

​​、國内債務違約​

​domestic_debt_in_default​

​​、主權外債違約​

​sovereign_external_debt_default​

​​、貨币危機​

​currency_crises​

​​、通脹危機​

​inflation_crises​

​​、銀行危機​

​banking_crisis​

​等的分布規律。

sns.set(style='darkgrid')
columns = ['systemic_crisis','domestic_debt_in_default','sovereign_external_debt_default','currency_crises','inflation_crises','banking_crisis']

# 繪制其他特征的分布規律圖
plt.figure(figsize=(16,16))

for i in range(6):
    plt.subplot('32'+str(i+1))
    sns.countplot(y = data.country,hue = data[columns[i]],palette = 'rocket') # palette為調色闆
    plt.______(loc = 0) # 選擇最優的圖例位置
    plt.title(columns[i])
    
plt.tight_layout()
plt.show()      
  • 主權外債違約​

    ​sovereign_external_debt_default​

    ​是指一國政府無法按時對其向外擔保借來的債務還本付息的情況,觀察上圖可以發現中非共和國(Central African Republic)、辛巴維(威)(Zimbabwe)和象牙海岸(Ivory Coast)出現大量的主權外債違約,這導緻主權信用評級極低。
  • 同時我們還能發現,除了安哥拉(Angola)、辛巴維(威)(Zimbabwe),其餘大部分國家均沒有國内債務違約​

    ​domestic_debt_in_default​

    ​。這是因為當一國政府掌握有貨币的發行權時,政府可以通過發行新的貨币,以投放過量的貨币來償還本币債務(即内債),此時政府不會發生對國内債務的主權違約,這也是采用本币标記的主權債券在其國内享有最高信用評級的原因。但現實中由于政府超發貨币會帶來通貨膨脹、本币币值波動等現象,是以其面向全部債權人債務總額是有上限的。
  • 系統性金融危機可以稱為“全面金融危機”,是指主要的金融領域都出現嚴重混亂,如貨币危機、銀行業危機、外債危機的同時或相繼發生。它往往發生在金融經濟、金融系統、金融資産比較繁榮的市場化國家和地區以及赤字和外債較為嚴重的國家,對世界經濟的發展具有巨大的破壞作用。

發生系統性危機​

​systemic_crisis​

​​最多的國家是中非共和國(Central African Republic),其次是辛巴維(威)(Zimbabwe)和肯亞(Kenya)。按照系統性危機的定義,系統性危機與銀行業危機之間應存在聯系。讓我們檢查這些國家在發生系統性危機的時候是否同時發生銀行危機​

​banking_crisis​

​。

2.4 系統性危機與銀行危機間的相關性

# 建立包含年份,國家,系統性危機,銀行危機的資料集
systemic = data[['year','country', 'systemic_crisis', 'banking_crisis']]

# 繪制觀察系統性危機與銀行危機發生的重疊性
systemic = systemic[(systemic['country'] == 'Central African Republic') | (systemic['country']=='Kenya') | (systemic['country']=='Zimbabwe') ]
plt.figure(figsize=(12,12))
count = 1

for country in systemic.country.unique():
    plt.subplot(len(systemic.country.unique()),1,count)
    subset = systemic[(systemic['country'] == country)]
    sns.lineplot(subset['year'],subset['systemic_crisis'],ci=None) # ci參數可用于指定線段區間的大小
    plt.scatter(subset['year'],subset["banking_crisis"], color='coral', label='Banking Crisis')
    plt.subplots_adjust(hspace=0.6) # hspace用來設定子圖上下間的距離
    plt.______('Years') # 給x軸命名
    plt.______('Systemic Crisis/Banking Crisis') # 給y軸命名
    plt.title(country)
    count+=1      

藍色折線的取值代表系統性危機是否發生,紅色散點代表銀行危機是否發生,上圖表明了危機是如何重疊的,進而證明了我們關于系統性危機對銀行業危機有影響的假設。

計算所有特征間的相關性

# 将銀行危機banking_crisis列進行特征編碼
# 将銀行危機banking_crisis中未發生危機的資料标為0,發生危機的資料标為1
data['banking_crisis'] = data['banking_crisis'].map({"no_crisis":0,"crisis":1})

# 選出所有特征
selected_features = ['systemic_crisis', 'exch_usd', 'domestic_debt_in_default','sovereign_external_debt_default', 'gdp_weighted_default',
       'inflation_annual_cpi', 'independence', 'currency_crises','inflation_crises','banking_crisis']

corr = data[selected_features].______() # 得到各特征間的相關性大小生成相關性矩陣

fig = plt.figure(figsize = (12,8))

cmap = sns.diverging_palette(220, 10, as_cmap=True) # 生成藍-白-紅的顔色清單
mask = np.zeros_like(corr, dtype=np.bool) # 傳回與相關性矩陣具有相同形狀和類型的零數組作為掩碼
mask[np.triu_indices_from(mask)] = True # 給相關性矩陣的上三角陣生成掩碼

# 繪制熱力圖
sns.______(corr, mask=mask, cmap=cmap,vmin=-0.5,vmax=0.7, center=0,annot = True,
            square=True, linewidths=.5,cbar_kws={"shrink": .5});

plt.title("特征間的相關性",fontproperties = font)
plt.show()      

除了看到系統性危機和銀行危機的相關性極高之外,我們還了解到貨币兌美元的匯率及國内債務違約與主權外債違約相關性較大。

接下來我們嘗試建構一個随機森林分類模型,預測影響銀行危機發生的特征有哪些。

3. 建構銀行危機預測模型

  • 特征編碼
  • 資料集劃分與分層采樣
  • 建立随機森林預測模型
  • 模型效果的評估
  • 使用SMOTE進行過采樣優化模型
  • 特征重要性排序

3.1 特征編碼

data.drop(['case','cc3'],axis = 1,inplace = True) # 在原資料集上删掉case列和cc3列
data.head()      
# 對國家country進行labelencoder
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.______(data['country'].values) # 将country的值塞入空字典
data['country']=le.____________(data['country'].values) # 将字典中的country的值轉變為索引值      
print(data['country']) # 檢視特征編碼後的country名稱      
# 繪制未發生銀行危機no_crisis與發生銀行危機crisis的柱狀圖
fig = plt.figure(figsize = (8,6))

data['banking_crisis'].value_counts().plot(kind='______',rot = 360,color = 'lightseagreen')

plt.xticks([0,1],["no_crisis","crisis"])
plt.show()      

可以看到,資料集中發生銀行危機的情況遠少于未發生銀行危機的情況,比例約為1:10,對于此類不平衡資料,應該盡量使得訓練集和測試集中樣本的比例相一緻。需要使用分層采樣的方法來劃分訓練集和測試集。

3.2 資料集劃分與分層采樣

下面我們開始對資料進行訓練集與測試集的劃分。在Sklearn中的​

​model_selection​

​​子產品,存在​

​train_test_split()​

​​函數,用作訓練集和測試集劃分,函數文法為:​

​train_test_split(x,y,test_size = None,random_state = None,stratify = y)​

​,其中:

  • ​x,y​

    ​: 分别為預測所需的所有特征,以及需要預測的特征。
  • ​test_size​

    ​​: 測試集比例,例如​

    ​test_size=0.2​

    ​則表示劃分​

    ​20%​

    ​的資料作為測試集。
  • ​random_state​

    ​​: 随機種子,因為劃分過程是随機的,為了進行可重複的訓練,需要固定一個​

    ​random_state​

    ​,結果重制。
  • ​stratify​

    ​: 使用分層采樣,保證從發生銀行危機樣本和未發生銀行危機樣本中抽取了同樣比例的訓練集和測試集。

函數最終将傳回四個變量,分别為​

​x​

​​的訓練集和測試集,以及​

​y​

​的訓練集和測試集。

# 訓練集與測試集的劃分
from sklearn import model_selection

x = data.drop('banking_crisis',axis = 1) # 将删除banking_crisis列的資料集作為x
y = data['banking_crisis'] # banking_crisis列作為y

x_train,x_test,y_train,y_test = model_selection.__________________(x, y,test_size=0.2,random_state = 33,stratify=y)      

3.3 建立随機森林預測模型

随機森林是一種內建學習方法,通過使用随機的方式從資料中抽取樣本和特征,訓練多個不同的決策樹,形成“森林”。每個樹都給出自己的分類意見,稱“投票”。在分類問題下,森林選擇選票最多的分類;在回歸問題下則使用平均值。在Python中使用​

​sklearn.ensemble​

​​的​

​RandomForestClassifier​

​建構分類模型,其主要參數包括:

  • ​n_estimators​

    ​ : 訓練分類器的數量(預設為100);
  • ​max_depth​

    ​ : 每棵樹的最大深度(預設為3);
  • ​max_features​

    ​: 劃分的最大特征數(預設為 'auto')
  • ​random_state​

    ​ : 随機種子。
from sklearn.ensemble import RandomForestClassifier

# 訓練随機森林分類模型
rf = RandomForestClassifier(n_estimators = 100, max_depth = 20,max_features = 10, random_state = 20)
rf.fit(x_train, y_train) 
y_pred = rf.______(x_test) # 對y進行預測      

3.4 模型評估

在評價模型好壞時,我們分别使用函數​

​classification_report()​

​​、​

​confusion_matrix()​

​​和​

​accuracy_score()​

​,用于輸出模型的預測報告、混淆矩陣和分類正确率。

from sklearn.metrics import classification_report,confusion_matrix

print(classification_report(y_test, y_pred)) # 輸出模型的預測報告
confusion_matrix = __________________(y_test, y_pred) 
print(confusion_matrix) # 輸出混淆矩陣

# 繪制混淆矩陣熱力圖
fig,ax = plt.subplots(figsize=(8,6)) 
sns._________(confusion_matrix,ax=ax,annot=True,annot_kws={'size':15}, fmt='d',cmap = 'YlGnBu_r')
ax.set_ylabel('真實值',fontproperties = font)
ax.set_xlabel('預測值',fontproperties = font)
ax.set_title('混淆矩陣熱力圖',fontproperties = font)
plt.show() # 輸出混淆矩陣熱力圖      

從模型預測報告中可以看出,對發生銀行危機(少數類)的召回率達到了89%,通過混淆矩陣及混淆矩陣熱力圖可以看出分類正确的占比較高,說明随機森林模型效果較好,接下來我們畫出二分類的ROC_AUC曲線,進行進一步評估。

ROC曲線

from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve

roc_auc = ____________(y_test, rf.predict(x_test)) #計算auc的值
fpr, tpr, thresholds = ____________(y_test, rf.predict_proba(x_test)[:,1]) #計算不同門檻值下的TPR和FPR

# 繪制ROC曲線
plt.figure(figsize = (8,6))

plt.plot(fpr, tpr, label='Random Forest (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1],'r--')# 繪制随機猜測線
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC curve')

plt.legend(loc="lower right") # 圖例位置位于右下方
plt.show()      

可以看到随機森林ROC曲線表現較好,接近左上角,AUC的值達到了0.94。

考慮到訓練集裡面樣本數量較少,樣本類别不平衡,我們對少數類使用SMOTE進行過采樣操作,擴充少數類樣本,對模型進行優化。

3.5 使用SMOTE進行過采樣優化模型

SMOTE算法的基本思想是對少數類樣本進行分析并根據少數類樣本人工合成新樣本添加到資料集中。對于少數類樣本a, 随機選擇一個最近鄰的樣本b, 然後從a與b的連線上随機選取一個點c作為新的少數類樣本。

在對資料集進行劃分,接着對訓練集進行過采樣,将少數類進行擴充。在Python中使用imblearn.over_sampling的SMOTE類建構SMOTE過采樣模型。

# 對x_train,y_train進行SMOTE過采樣
from imblearn.over_sampling import SMOTE
x_train_resampled, y_train_resampled = SMOTE(random_state=4).fit_resample(x_train, y_train)

print(x_train_resampled.shape, y_train_resampled.shape) #檢視采樣後的資料集大小      
# 通過網格搜尋選擇最優參數
from sklearn.model_selection import GridSearchCV
param_grid = [{
    'n_estimators':[10,20,30,40,50],
    'max_depth':[5,8,10,15,20,25]
}]
grid_search = GridSearchCV(rf, param_grid, scoring = 'recall')      
# 輸出最佳參數組合以及分數
grid_search.fit(x_train_resampled, y_train_resampled)

print("best params:", grid_search.best_params_)

print("best score:", grid_search.best_score_)      

可以發現對模型進行SMOTE過采樣以及進行網格搜尋找到最優參數後,得到最優的參數選擇為max_depth: 10, n_estimators: 10;recall得分也最高可達到0.97,與優化模型前的0.89相比也有提升。

最後,我們使用随機森林篩選出影響銀行危機發生的重要的特征,并畫出特征重要性排序圖。

3.6 特征重要性排序

fig = plt.figure(figsize=(16,12))

# 得到随機森林特征重要性評分
rf_importance = rf.__________________
index = data.drop(['banking_crisis'], axis=1).columns # 删掉銀行危機banking_crisis列特征

# 對得到的特征重要性評分進行降序排序
rf_feature_importance = pd.DataFrame(rf_importance.T, index=index,columns=['score']).sort_values(by='score', ascending=True)

# 水準條形圖繪制
rf_feature_importance.plot(kind='______',legend=False,color = 'deepskyblue')

plt.title('随機森林特征重要性',fontproperties = font)

plt.show()      

可以看到,系統危機​

​systemic_crisis​

​​的重要性最高、年度cpi通貨膨脹率​

​inflation_annual_cpi​

​​、年份​

​year​

​​、該國貨币兌美元匯率​

​exch_used​

​重要性也較高,說明這些特征對影響銀行危機是否發生較為重要,這也進一步驗證了我們在之前特征相關性分析的結論。

4. 總結