<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python數(shù)據(jù)挖掘|銀行信用卡客戶流失預(yù)測(kaggle)

          共 14802字,需瀏覽 30分鐘

           ·

          2020-12-23 18:58

          1.背景

          越來越多的客戶不再使用信用卡服務(wù),銀行的經(jīng)理對此感到不安。如果有人能為他們預(yù)測哪些客戶即將流失,他們將不勝感激,因?yàn)檫@樣他們可以主動向客戶提供更好的服務(wù),并挽回這些即將流失的客戶。

          2.數(shù)據(jù)集

          該數(shù)據(jù)集由10,000個客戶組成,其中包含了他們的年齡,工資,婚姻狀況,信用卡限額,信用卡類別等。

          不過,這里面只有16%的客戶是流失的,因此拿來預(yù)測客戶是否會流失有點(diǎn)難度。

          在早起Python后臺回復(fù) 預(yù)測客戶流失 下載這份數(shù)據(jù)和源代碼。

          譯自kaggle并對原文進(jìn)行了修改和補(bǔ)充,感謝原作者:

          https://www.kaggle.com/thomaskonstantin/bank-churn-data-exploration-and-churn-prediction/

          3.代碼與分析

          本文具備流程性,建議使用 VSCode 的 Jupiter Notebook 擴(kuò)展,新建一個名為 test.ipynb 的文件,跟著教程一步步走下去。

          Windows環(huán)境下打開 Cmd (開始-運(yùn)行-CMD),蘋果系統(tǒng)環(huán)境下請打開 Terminal (command+空格輸入Terminal),準(zhǔn)備開始輸入命令安裝依賴。

          所需依賴:

          pip install numpy
          pip install pandas
          pip install plotly
          pip install scikit-learn
          pip install scikit-plot

          # 最后模型預(yù)測需要用到,安裝需要conda
          # 如果只是想探索性分析數(shù)據(jù),可以不導(dǎo)入 imblearn
          conda install -c conda-forge imbalanced-learn

          3.1 導(dǎo)入需要的模塊

          本文比較長,涉及到的模塊比較多,如果只是想探索性分析數(shù)據(jù),可以不導(dǎo)入 imblearn。

          import numpy as np # linear algebra
          import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
          import matplotlib.pyplot as plt
          import seaborn as sns
          import plotly.express as ex
          import plotly.graph_objs as go
          import plotly.figure_factory as ff
          from plotly.subplots import make_subplots
          import plotly.offline as pyo
          pyo.init_notebook_mode()
          sns.set_style('darkgrid')
          from sklearn.decomposition import PCA
          from sklearn.model_selection import train_test_split,cross_val_score
          from sklearn.ensemble import RandomForestClassifier,AdaBoostClassifier
          from sklearn.svm import SVC
          from sklearn.pipeline import Pipeline
          from sklearn.preprocessing import StandardScaler
          from sklearn.metrics import f1_score as f1
          from sklearn.metrics import confusion_matrix
          import scikitplot as skplt

          plt.rc('figure',figsize=(18,9))
          %pip install imbalanced-learn
          from imblearn.over_sampling import SMOTE

          遇到任何 No module named "XXX" 都可以嘗試pip install一下。

          如果pip install沒解決,可以谷歌/百度一下,看看別人是怎么解決的。

          3.2 加載數(shù)據(jù)

          c_data = pd.read_csv('./BankChurners.csv')
          c_data = c_data[c_data.columns[:-2]]
          c_data.head(3)

          這里去掉了最后兩列的樸素貝葉斯分類結(jié)果。

          顯示前三行數(shù)據(jù), 可以看到所有的字段:

          3.3 探索性數(shù)據(jù)分析

          下面看看這20+列數(shù)據(jù)中,哪一些是對我們有用的。

          首先,我想知道數(shù)據(jù)集中的客戶年齡分布:

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Customer_Age'],name='Age Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Customer_Age'],name='Age Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of Customer Ages")
          fig.show()

          可以看到,客戶的年齡分布大致遵循正態(tài)分布,因此使用可以在正態(tài)假設(shè)下進(jìn)一步使用年齡特征。

          同樣滴,我想知道性別分布如何:

          ex.pie(c_data,names='Gender',title='Propotion Of Customer Genders')

          可見,在我們的數(shù)據(jù)集中,女性的樣本比男性更多,但是差異的百分比不是那么顯著,所以我們可以說性別是均勻分布的。

          每個客戶的家庭人數(shù)的分布怎么樣?

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Dependent_count'],name='Dependent count Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Dependent_count'],name='Dependent count Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of Dependent counts (close family size)")
          fig.show()

          它也大致符合正態(tài)分布,偏右一點(diǎn),或許后續(xù)分析能用得上。

          客戶的受教育水平如何?

          ex.pie(c_data,names='Education_Level',title='Propotion Of Education Levels')

          假設(shè)大多數(shù)教育程度不明(Unknown)的顧客都沒有接受過任何教育。我們可以指出,超過70%的顧客都受過正規(guī)教育,其中約35%的人受教育程度達(dá)到碩士以上水平,45%的人達(dá)到本科以上水準(zhǔn)。

          他們的婚姻狀態(tài)如何?

          ex.pie(c_data,names='Marital_Status',title='Propotion Of Different Marriage Statuses')

          看來,這家銀行幾乎一半的客戶都是已婚人士,有趣的是,另一半客戶幾乎都是單身人士,另外只有7%的客戶離婚了。

          看看收入分布和卡片類型的分布:

          ex.pie(c_data,names='Income_Category',title='Propotion Of Different Income Levels')
          ex.pie(c_data,names='Card_Category',title='Propotion Of Different Card Categories')

          可見大部分人的年收入處于60K美元以下。

          在持有的卡片的類型上,藍(lán)卡占了絕大多數(shù)。

          每月賬單數(shù)量有沒有特征?

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Months_on_book'],name='Months on book Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Months_on_book'],name='Months on book Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of months the customer is part of the bank")
          fig.show()

          可以看到中間的峰值特別高,顯然這個指標(biāo)不是正態(tài)分布的。

          每位客戶持有的銀行業(yè)務(wù)數(shù)量有沒有特征呢?

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Total_Relationship_Count'],name='Total no. of products Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Total_Relationship_Count'],name='Total no. of products Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of Total no. of products held by the customer")
          fig.show()

          基本上都是均勻分布的,顯然這個指標(biāo)對于我們而言也沒太大意義。

          用戶不活躍月份數(shù)量是不是好用的特征?

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Months_Inactive_12_mon'],name='number of months inactive Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Months_Inactive_12_mon'],name='number of months inactive Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of the number of months inactive in the last 12 months")
          fig.show()

          這個似乎有點(diǎn)用處,會不會越不活躍的用戶越容易流失呢?我們先往后看。

          信用卡額度的分布如何?

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Credit_Limit'],name='Credit_Limit Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Credit_Limit'],name='Credit_Limit Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of the Credit Limit")
          fig.show()

          大部分人的額度都在0到10k之間,這比較正常,暫時看不出和流失有什么關(guān)系。

          客戶總交易額的分布怎么樣?

          fig = make_subplots(rows=2, cols=1)

          tr1=go.Box(x=c_data['Total_Trans_Amt'],name='Total_Trans_Amt Box Plot',boxmean=True)
          tr2=go.Histogram(x=c_data['Total_Trans_Amt'],name='Total_Trans_Amt Histogram')

          fig.add_trace(tr1,row=1,col=1)
          fig.add_trace(tr2,row=2,col=1)

          fig.update_layout(height=700, width=1200, title_text="Distribution of the Total Transaction Amount (Last 12 months)")
          fig.show()

          這個有點(diǎn)意思,總交易額的分布體現(xiàn)出“多組”分布,如果我們根據(jù)這個指標(biāo)將客戶聚類為不同的組別,看他們之間的相似性,并作出不同的畫線,也許對我們最終的流失分析有一定的意義。

          接下來,最重要的流失用戶分布

          ex.pie(c_data,names='Attrition_Flag',title='Proportion of churn vs not churn customers')

          我們可以看到,只有16%的數(shù)據(jù)樣本代表流失客戶,在接下來的步驟中,我將使用SMOTE對流失樣本進(jìn)行采樣,使其與常規(guī)客戶的樣本大小匹配,以便給后面選擇的模型一個更好的機(jī)會來捕捉小細(xì)節(jié)。

          3.4 數(shù)據(jù)預(yù)處理

          使用SMOTE模型前,需要根據(jù)不同的特征對數(shù)據(jù)進(jìn)行One Hot編碼:

          c_data.Attrition_Flag = c_data.Attrition_Flag.replace({'Attrited Customer':1,'Existing Customer':0})
          c_data.Gender = c_data.Gender.replace({'F':1,'M':0})
          c_data = pd.concat([c_data,pd.get_dummies(c_data['Education_Level']).drop(columns=['Unknown'])],axis=1)
          c_data = pd.concat([c_data,pd.get_dummies(c_data['Income_Category']).drop(columns=['Unknown'])],axis=1)
          c_data = pd.concat([c_data,pd.get_dummies(c_data['Marital_Status']).drop(columns=['Unknown'])],axis=1)
          c_data = pd.concat([c_data,pd.get_dummies(c_data['Card_Category']).drop(columns=['Platinum'])],axis=1)
          c_data.drop(columns = ['Education_Level','Income_Category','Marital_Status','Card_Category','CLIENTNUM'],inplace=True)

          顯示熱力圖:

          sns.heatmap(c_data.corr('pearson'),annot=True)

          3.5 SMOTE模型采樣

          SMOTE模型經(jīng)常用于解決數(shù)據(jù)不平衡的問題,它通過添加生成的少數(shù)類樣本改變不平衡數(shù)據(jù)集的數(shù)據(jù)分布,是改善不平衡數(shù)據(jù)分類模型性能的流行方法之一。

          oversample = SMOTE()
          X, y = oversample.fit_resample(c_data[c_data.columns[1:]], c_data[c_data.columns[0]])
          usampled_df = X.assign(Churn = y)
          ohe_data =usampled_df[usampled_df.columns[15:-1]].copy()
          usampled_df = usampled_df.drop(columns=usampled_df.columns[15:-1])
          sns.heatmap(usampled_df.corr('pearson'),annot=True)

          3.6 主成分分析

          我們將使用主成分分析來降低單次編碼分類變量的維數(shù),從而降低方差。同時使用幾個主成分而不是幾十個單次編碼特征將幫助我構(gòu)建一個更好的模型。

          N_COMPONENTS = 4

          pca_model = PCA(n_components = N_COMPONENTS )

          pc_matrix = pca_model.fit_transform(ohe_data)

          evr = pca_model.explained_variance_ratio_
          cumsum_evr = np.cumsum(evr)

          ax = sns.lineplot(x=np.arange(0,len(cumsum_evr)),y=cumsum_evr,label='Explained Variance Ratio')
          ax.set_title('Explained Variance Ratio Using {} Components'.format(N_COMPONENTS))
          ax = sns.lineplot(x=np.arange(0,len(cumsum_evr)),y=evr,label='Explained Variance Of Component X')
          ax.set_xticks([i for i in range(0,len(cumsum_evr))])
          ax.set_xlabel('Component number #')
          ax.set_ylabel('Explained Variance')
          plt.show()
          usampled_df_with_pcs = pd.concat([usampled_df,pd.DataFrame(pc_matrix,columns=['PC-{}'.format(i) for i in range(0,N_COMPONENTS)])],axis=1)
          usampled_df_with_pcs

          特征變得越來越明顯:

          sns.heatmap(usampled_df_with_pcs.corr('pearson'),annot=True)

          4.模型選擇及測試

          選擇出以下特征劃分訓(xùn)練集并進(jìn)行訓(xùn)練:

          X_features = ['Total_Trans_Ct','PC-3','PC-1','PC-0','PC-2','Total_Ct_Chng_Q4_Q1','Total_Relationship_Count']

          X = usampled_df_with_pcs[X_features]
          y = usampled_df_with_pcs['Churn']

          train_x,test_x,train_y,test_y = train_test_split(X,y,random_state=42)

          4.1 交叉驗(yàn)證

          分別看看隨機(jī)森林、AdaBoost和SVM模型三種模型的表現(xiàn)如何:

          rf_pipe = Pipeline(steps =[ ('scale',StandardScaler()), ("RF",RandomForestClassifier(random_state=42)) ])
          ada_pipe = Pipeline(steps =[ ('scale',StandardScaler()), ("RF",AdaBoostClassifier(random_state=42,learning_rate=0.7)) ])
          svm_pipe = Pipeline(steps =[ ('scale',StandardScaler()), ("RF",SVC(random_state=42,kernel='rbf')) ])


          f1_cross_val_scores = cross_val_score(rf_pipe,train_x,train_y,cv=5,scoring='f1')
          ada_f1_cross_val_scores=cross_val_score(ada_pipe,train_x,train_y,cv=5,scoring='f1')
          svm_f1_cross_val_scores=cross_val_score(svm_pipe,train_x,train_y,cv=5,scoring='f1')
          plt.subplot(3,1,1)
          ax = sns.lineplot(x=range(0,len(f1_cross_val_scores)),y=f1_cross_val_scores)
          ax.set_title('Random Forest Cross Val Scores')
          ax.set_xticks([i for i in range(0,len(f1_cross_val_scores))])
          ax.set_xlabel('Fold Number')
          ax.set_ylabel('F1 Score')
          plt.show()
          plt.subplot(3,1,2)
          ax = sns.lineplot(x=range(0,len(ada_f1_cross_val_scores)),y=ada_f1_cross_val_scores)
          ax.set_title('Adaboost Cross Val Scores')
          ax.set_xticks([i for i in range(0,len(ada_f1_cross_val_scores))])
          ax.set_xlabel('Fold Number')
          ax.set_ylabel('F1 Score')
          plt.show()
          plt.subplot(3,1,3)
          ax = sns.lineplot(x=range(0,len(svm_f1_cross_val_scores)),y=svm_f1_cross_val_scores)
          ax.set_title('SVM Cross Val Scores')
          ax.set_xticks([i for i in range(0,len(svm_f1_cross_val_scores))])
          ax.set_xlabel('Fold Number')
          ax.set_ylabel('F1 Score')
          plt.show()

          看看三種模型都有什么不同的表現(xiàn):

          看得出來隨機(jī)森林 F1分?jǐn)?shù)是最高的,達(dá)到了0.92。

          4.2 模型預(yù)測

          對測試集進(jìn)行預(yù)測,看看三種模型的效果:

          rf_pipe.fit(train_x,train_y)
          rf_prediction = rf_pipe.predict(test_x)

          ada_pipe.fit(train_x,train_y)
          ada_prediction = ada_pipe.predict(test_x)

          svm_pipe.fit(train_x,train_y)
          svm_prediction = svm_pipe.predict(test_x)

          print('F1 Score of Random Forest Model On Test Set - {}'.format(f1(rf_prediction,test_y)))
          print('F1 Score of AdaBoost Model On Test Set - {}'.format(f1(ada_prediction,test_y)))
          print('F1 Score of SVM Model On Test Set - {}'.format(f1(svm_prediction,test_y)))

          4.3 對原始數(shù)據(jù)(采樣前)進(jìn)行模型預(yù)測

          接下來對原始數(shù)據(jù)進(jìn)行模型預(yù)測:

          ohe_data =c_data[c_data.columns[16:]].copy()
          pc_matrix = pca_model.fit_transform(ohe_data)
          original_df_with_pcs = pd.concat([c_data,pd.DataFrame(pc_matrix,columns=['PC-{}'.format(i) for i in range(0,N_COMPONENTS)])],axis=1)

          unsampled_data_prediction_RF = rf_pipe.predict(original_df_with_pcs[X_features])
          unsampled_data_prediction_ADA = ada_pipe.predict(original_df_with_pcs[X_features])
          unsampled_data_prediction_SVM = svm_pipe.predict(original_df_with_pcs[X_features])

          效果如下:

          F1最高的隨機(jī)森林模型有0.63分,偏低,這也比較正常,畢竟在這種分布不均的數(shù)據(jù)集中,查全率是比較難拿到高分?jǐn)?shù)的。

          4.4 結(jié)果

          讓我們看看最終在原數(shù)據(jù)上使用隨機(jī)森林模型的運(yùn)行結(jié)果:

          ax = sns.heatmap(confusion_matrix(unsampled_data_prediction_RF,original_df_with_pcs['Attrition_Flag']),annot=True,cmap='coolwarm',fmt='d')
          ax.set_title('Prediction On Original Data With Random Forest Model Confusion Matrix')
          ax.set_xticklabels(['Not Churn','Churn'],fontsize=18)
          ax.set_yticklabels(['Predicted Not Churn','Predicted Churn'],fontsize=18)

          plt.show()

          可見,沒有流失的客戶命中了7709人,未命中791人。

          流失客戶命中了1130人,未命中497人。

          整體而言,是一個比較優(yōu)秀的模型了。

          我們的文章到此就結(jié)束啦,如果你喜歡今天的Python 實(shí)戰(zhàn)教程,請持續(xù)關(guān)注早起Python,如果喜歡本文的話可以點(diǎn)贊、在看、分享!

          -END-



          掃碼添加早小起

          1. 回復(fù)「進(jìn)群」進(jìn)入Python技術(shù)交流群

          2. 回復(fù)「Python」獲得Python技術(shù)圖書

          3. 回復(fù)「習(xí)題」領(lǐng)取Python數(shù)據(jù)處理200題




          瀏覽 69
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区三区在线 | 欧美精品区 | 亚洲精品免费AV | 黄色视频图片免费看 | 尻屄免费视频 |