--- title: Project07 tags: teach:MF --- # Santander Customer Transaction Prediction - Motivations and Why kaggle 題目:Santander Customer Transaction Prediction (https://www.kaggle.com/c/santander-customer-transaction-prediction) (1)動機:kaggle上看到的題目,我認為是比較實際且市場需求 取向的,這個題目是要幫忙預測哪些資料對消費有預測能力,所有的資料皆沒有名稱,所以無法用主觀方式進行判斷,需要透過許EDA去 抽絲剝繭,最後在進行模型預測。 (2)keywords:Customer Transaction, lightgbm - [data](https://www.kaggle.com/c/santander-customer-transaction-prediction/data) ### 變數 | 資料 | 型態 | ---- | ---- |test.csv |共有200個為給名稱之解釋變數、target為Binary應變數(0、1)| |train.csv|同上 | ### 1.EDA - 導入套件 ```python= import warnings import numpy as np import pandas as pd import seaborn as sns #import lightgbm as lgb import matplotlib.pyplot as plt from sklearn.metrics import roc_auc_score from sklearn.model_selection import StratifiedKFold import seaborn as sns import matplotlib.pyplot as plt from sklearn.metrics import roc_curve, roc_auc_score, auc, confusion_matrix, classification_report from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier pd.set_option('display.unicode.ambiguous_as_wide', True) pd.set_option('display.unicode.east_asian_width', True) from IPython.display import display warnings.filterwarnings('ignore') ``` - 先用 pandas 的 Dataframe 進行資料的導入,並觀察資料的比數以及資料的樣子。 ```python= #讀取dataframe test_df = pd.read_csv('test.csv') train_df = pd.read_csv('train.csv') #觀察資料筆數 train_df.shape, test_df.shape display(test_df.head()) display(train_df.head()) ``` - 觀察是否有缺值,計算缺值個數以及缺值所佔之比例,利用 concat 合併兩資料,檢查 test 跟 train 資料中皆無缺值需要處理。 ```python= def missing_data(data): total = data.isnull().sum() percent = (data.isnull().sum()/data.isnull().count()*100) tt = pd.concat([total, percent], axis=1, keys=['Total', 'Percent']) types = [] for col in data.columns: dtype = str(data[col].dtype) types.append(dtype) tt['Types'] = types return(np.transpose(tt)) #test跟train data都檢查 print(missing_data(test_df)) print(missing_data(train_df)) #並無缺值 train_df.describe() ``` ![](https://i.imgur.com/zttWGTv.png) - 觀察 test 與 train data 是否有關聯性,若是存在正相關或是負相關等趨勢,則兩筆資料並不適合拿 來做後面 model 處理。我們觀察 test 與 train data 變數之間為隨機分佈,因此為為合理的資料。 X軸train data ; Y軸為test data. ```python= def plot_feature_scatter(df1, df2, features): i = 0 sns.set_style('whitegrid') plt.figure() fig, ax = plt.subplots(4,4,figsize=(14,14)) for feature in features: i += 1 plt.subplot(4,4,i) plt.scatter(df1[feature], df2[feature], marker='+') plt.xlabel(feature, fontsize=9) plt.show(); features = ['var_0', 'var_1','var_2','var_3', 'var_4', 'var_5', 'var_6', 'var_7', 'var_8', 'var_9', 'var_10','var_11','var_12', 'var_13', 'var_14', 'var_15',] plot_feature_scatter(train_df[::20],test_df[::20], features) ``` ![](https://i.imgur.com/BI83nrR.png) - 我們的目標是分析 target 是否為 0 或是 1,不過 target 的確切是什麼並沒有說,統計target分別為1及為0的個數,而資料顯示多數target皆為零,因此我猜測這裡想要了解 target=1時狀況。 ```python= #發現約10% target=1 sns.countplot(train_df['target'], palette='Set3') ``` ![](https://i.imgur.com/5cNGhfo.png =110%x) - 接著由於我們有許多變數,要如何分析哪些變數是有用,哪些變數較無相關,可以利用機率密度 函數,分別對各個變數的train data之target=0及target=1畫機率密度函數,若是變數沒有解釋能力,則應該以常態 分配呈現。因此從較特別的分配可以挑出有用的變數。 ```python= def plot_feature_distribution(df1, df2, label1, label2, features): i = 0 sns.set_style('whitegrid') plt.figure() fig, ax = plt.subplots(10,10,figsize=(18,22)) for feature in features: i += 1 plt.subplot(10,10,i) sns.distplot(df1[feature], hist=False,label=label1) sns.distplot(df2[feature], hist=False,label=label2) plt.xlabel(feature, fontsize=9) locs, labels = plt.xticks() plt.tick_params(axis='x', which='major', labelsize=6, pad=-6) plt.tick_params(axis='y', which='major', labelsize=6) plt.show(); t0 = train_df.loc[train_df['target'] == 0] t1 = train_df.loc[train_df['target'] == 1] features = train_df.columns.values[2:102] plot_feature_distribution(t0, t1, '0', '1', features) features = train_df.columns.values[102:202] plot_feature_distribution(t0, t1, '0', '1', features) ``` ![](https://i.imgur.com/Ee1adrp.png) ![](https://i.imgur.com/OJAgcVI.png) - 變數間之correlation並無相關性 ![](https://i.imgur.com/MCvrW98.jpg) ![](https://i.imgur.com/xIKdUeq.png =120%x) ### 2.Benchmark method: Logistic model $P(Y|X=1)=\dfrac{e^{\beta^ x}}{1+e^{\beta x}}$ $Y= Target$ $X = 其他Variables$ $test data =0.3$ - 先將資料的ID跟target切割,其餘變數切割成train跟test兩個部分,以logistic model進行迴歸預測。 ```python= train_log = train_df.drop(columns = ['target', 'ID_code']) test_log = test_df.drop(columns = ['ID_code']) Target = train_df['target'] #切割train及test資料集 X_train, X_test, Y_train, Y_test = train_test_split(train_log, Target, test_size= 0.3, random_state = 2019) logist = LogisticRegression() logist.fit(X_train, Y_train) logist_pred = logist.predict_proba(X_test)[:,1] def performance(Y_test, logist_pred): Y_predict = [0 if i < 0.5 else 1 for i in logist_pred] print('Confusion Matrix:') print(confusion_matrix(Y_test, Y_predict)) fpr, tpr, thresholds = roc_curve(Y_test, Y_predict, pos_label=1) print('AUC:') print(auc(fpr, tpr)) performance(Y_test, logist_pred) ``` #### 結果 ![](https://i.imgur.com/x94HxFk.png) ### 3.decision tree ```python= tree_clf = DecisionTreeClassifier(class_weight='balanced', random_state = 2019, max_features = 0.7, min_samples_leaf = 80) tree_clf.fit(X_train, Y_train) tree_preds = tree_clf.predict_proba(X_test)[:, 1] performance(Y_test, tree_preds) ``` #### 結果 ![](https://i.imgur.com/ThIEyEE.png) ### 4.Model Lightgbm 用lightgbm model 進行訓練,再以Kfold 進行交叉驗證,簡促模型的穩定度。 lightgbm model是基於決策樹(Decision Tree)學習算法的梯度提升框架,是基於XGBoost演化的方法,優點在於計算較為快速,且佔用內存較少。 ```python= features = [c for c in train_df.columns if c not in ['ID_code', 'target']] target = train_df['target'] #parameter param = { 'bagging_freq': 5, 'bagging_fraction': 0.4, 'boost_from_average':'false', 'boost': 'gbdt', 'feature_fraction': 0.05, 'learning_rate': 0.01, 'max_depth': -1, 'metric':'auc', 'min_data_in_leaf': 80, 'min_sum_hessian_in_leaf': 10.0, 'num_leaves': 13, 'num_threads': 8, 'tree_learner': 'serial', 'objective': 'binary', 'verbosity': 1 } ##run model folds = StratifiedKFold(n_splits=10, shuffle=False, random_state=None) oof = np.zeros(len(train_df)) predictions = np.zeros(len(test_df)) feature_importance_df = pd.DataFrame() for fold_, (trn_idx, val_idx) in enumerate(folds.split(train_df.values, target.values)): print("Fold {}".format(fold_)) trn_data = lgb.Dataset(train_df.iloc[trn_idx][features], label=target.iloc[trn_idx]) val_data = lgb.Dataset(train_df.iloc[val_idx][features], label=target.iloc[val_idx]) num_round = 1000000 clf = lgb.train(param, trn_data, num_round, valid_sets = [trn_data, val_data], verbose_eval=1000, early_stopping_rounds = 3000) oof[val_idx] = clf.predict(train_df.iloc[val_idx][features], num_iteration=clf.best_iteration) fold_importance_df = pd.DataFrame() fold_importance_df["Feature"] = features fold_importance_df["importance"] = clf.feature_importance() fold_importance_df["fold"] = fold_ + 1 feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0) predictions += clf.predict(test_df[features], num_iteration=clf.best_iteration) / folds.n_splits print("CV score: {:<8.5f}".format(roc_auc_score(target, oof))) cols = (feature_importance_df[["Feature", "importance"]] .groupby("Feature") .mean() .sort_values(by="importance", ascending=False)[:150].index) best_features = feature_importance_df.loc[feature_importance_df.Feature.isin(cols)] plt.figure(figsize=(14,28)) sns.barplot(x="importance", y="Feature", data=best_features.sort_values(by="importance",ascending=False)) plt.title('Features importance (averaged/folds)') plt.tight_layout() plt.savefig('FI.png') ``` ![](https://i.imgur.com/FiL5wDy.png) ![](https://i.imgur.com/3sEbIv0.png) ### 5. Analysis and Conclusion - 1.上述問題中是典型的分群問題,在變數資訊未知且無法利用domain knowlege的情況下,依賴EDA進行觀察。在benchmark model中,我嘗試利用主觀判斷target=1及0分佈變異較大的前10名進行邏輯斯回歸,發現精準程度不如將全部變數加入迴歸分析中,且差距明顯。由此可見即便透過變數分配,單純直覺分析仍不可靠。 - 2. 透過decision tree預測能力benchmark method差不多。 - 3. lightgbm在分類上顯著優於以上兩者,顯示在特徵分類上,lightgbm演算法有顯著優勢。 - 4. feature importance 與前面所提到變數分配得變異性有正相關性,但是無法用肉眼捕捉所有較有影響力之特徵。但lightgbm缺點為容易過擬合,因此若樣本數較小,仍可以利用變數之分配來進行挑選。