python数据分析实战之信用卡违约风险预测
文章目录
* * * 1、明确需求和目的
* 2、 数据收集
* 3、数据预处理
* * 3.1 数据整合
* * 3.1.1 加载相关库和数据集
* 3.1.2 主要数据集概览
* 3.2 数据清洗
* * 3.2.1 多余列的删除
* 3.2.2 数据类型转换
* 3.2.3 缺失值处理
* 3.2.4 异常值处理
* 3.2.5 重复值处理
* 4、数据分析
* * 4.1 相关系数分析
* 5、模型训练
* * 5.1 数据标准化
* 5.2 使用逻辑回归训练
* 5.3 简单优化
* 5.4 使用随机森林训练
* 6、总结
1、明确需求和目的
- 现代社会,越来越多的人使用信用卡进行消费,大部分人使用信用卡之后会按时还款,但仍然有少部分人不能在约定时间进行还款,这大大的增加了银行或者金融机构的风险。
- 本文以某金融机构的历史数据进行建模分析,对客户的还款能力进行评估,以预测新客户是否有信用卡的违约风险,从而决定是否贷款给新客户使用。
- 本文使用AUC(ROC)作为模型的评估标准。
2、 数据收集
- 本文使用的数据集来源于kaggle平台,主要有两份数据集。
- application_train , application_test :训练集和测试集数据,包括每个贷款申请的信息。每笔贷款都有自己的行,并由特性SK_ID_CURR标识。训练集的TARGET 0:贷款已还清,1:贷款未还清。
3、数据预处理
3.1 数据整合
3.1.1 加载相关库和数据集
- 使用的库主要有:pandas、numpy、matplotlib、seaborn
- 使用的数据集:kaggle平台提供的数据集文件
import numpy as np import pandas as pd
import os
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings(\'ignore\')
print(os.listdir("../input/")) # List files available
-------------------------------------------------
[\'sample_submission.csv\', \'credit_card_balance.csv\', \'installments_payments.csv\', \'HomeCredit_columns_description.csv\', \'previous_application.csv\', \'POS_CASH_balance.csv\', \'bureau_balance.csv\', \'application_test.csv\', \'bureau.csv\', \'application_train.csv\']
3.1.2 主要数据集概览
首先看一下训练集数据:
# Training data app_train = pd.read_csv(\'../input/application_train.csv\')
print(\'Training data shape: \', app_train.shape)
--------------------------------------
Training data shape: (307511, 122)
app_train.head()
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200507235136393.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM4NDc4NA==,size_16,color_FFFFFF,t_70#pic_center)
从上面可以看出,训练集数据有122个特征,307511条数据。
再来看一下测试集数据:
# Testing data app_test = pd.read_csv(\'../input/application_test.csv\')
print(\'Testing data shape: \', app_test.shape)
---------------------------------------------
Testing data shape: (48744, 121)
app_test.head()
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200507235148946.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM4NDc4NA==,size_16,color_FFFFFF,t_70#pic_center)
从上面可以看出,测试集数据有121个特征,48744条数据,相比训练集,少了一个特征
TARGET,即我们需要预测的目标值(0表示贷款按时偿还,1表示贷款未按时偿还。)。
检查一下TARGET列的分布,看看每一类贷款的数量:
app_train[\'TARGET\'].value_counts() ----------------------------------------
0 282686
1 24825
Name: TARGET, dtype: int64
可以看出按时还款的类别明显要比未按时还款的类别多, 属于样本不均衡的问题,后续可以考虑使用权重法或采样法等办法来进行解决。
转化成图形更直观的对比一下:
app_train[\'TARGET\'].astype(int).plot.hist()
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200507235203694.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM4NDc4NA==,size_16,color_FFFFFF,t_70#pic_center)
3.2 数据清洗
3.2.1 多余列的删除
首先删除空白值超过一半的列 :
app_train = app_train.dropna(thresh=len(app_train) / 2 , axis=1)
定义一个函数,看一下剩余数据的缺失值情况:
# 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)
---------------------------------------------------
Your selected dataframe has 81 columns.
There are 26 columns that have missing values.
missing_values.head(10)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200507235220671.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM4NDc4NA==,size_16,color_FFFFFF,t_70#pic_center)
从上面可以看出,还有好几列的值缺失将近50%,缺失比例较高,我们一并将这些列进行删除。当然,也可以选择进行缺失值填充,这里选择删除。
drop_columns = ["FLOORSMAX_AVG","FLOORSMAX_MODE","FLOORSMAX_MEDI","YEARS_BEGINEXPLUATATION_AVG","YEARS_BEGINEXPLUATATION_MODE", "YEARS_BEGINEXPLUATATION_MEDI","TOTALAREA_MODE","EMERGENCYSTATE_MODE"]
app_train = app_train.drop(drop_columns, axis=1)
删除了缺失值较多的列之后,还有其它列的缺失值需要处理,我们可以先进行数据类型转换,后面再统一采用填充的方式进行处理。
3.2.2 数据类型转换
先来看一下各个类型的数据有多少:
# Number of each type of column app_train.dtypes.value_counts()
----------------------------------
int64 41
float64 20
object 12
dtype: int64
object类型的有12个,需要将其转换成数值类型,我们先看一下它们各自的特征类别有多少:
注意 nunique 不考虑空值。
# Number of unique classes in each object column app_train.select_dtypes(\'object\').apply(pd.Series.nunique, axis = 0)
-------------------------------------
NAME_CONTRACT_TYPE 2
CODE_GENDER 3
FLAG_OWN_CAR 2
FLAG_OWN_REALTY 2
NAME_TYPE_SUITE 7
NAME_INCOME_TYPE 8
NAME_EDUCATION_TYPE 5
NAME_FAMILY_STATUS 6
NAME_HOUSING_TYPE 6
OCCUPATION_TYPE 18
WEEKDAY_APPR_PROCESS_START 7
ORGANIZATION_TYPE 58
dtype: int64
- 标签编码 ( Label Encoding):它给类别一个任意的顺序。分配给每个类别的值是随机的,不反映类别的任何固有方面。所以我们在只有两个类别的时候使用标签编码。例如上面的‘NAME_CONTRACT_TYPE’等,我们就可以使用标签编码。
- 独热编码 (One-Hot Encoding):为分类变量中的每个类别创建一个新列。当类别>2的时候,我们将使用独热编码。例如上面的‘CODE_GENDER’等。当然独热编码的缺点也很明显,就是特征可能会暴增,但我们可以使用PCA或其他降维方法来减少维数。
对于类别只有2个的特征,我们使用 Label Encoding 进行数据的转换(注意测试集也同样需要进行转换):
from sklearn.preprocessing import LabelEncoder # 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)
-------------------------------------------------
3 columns were label encoded.
从上面可以看出3个类别全部完成了转换,接下来使用 One-Hot Encoding
进行剩余数据的转换,此处选择使用pandas的get_dummies()函数,直接映射为数值型(测试集一并进行转换):
# one-hot encoding of categorical variables 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)
----------------------------------------------------
Training Features shape: (307511, 182)
Testing Features shape: (48744, 239)
从上面可以看出,此时测试集列数多于训练集,因为训练集删除了一些多余的列,我们对两份数据取并集,只需要处理共同拥有的列即可:
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)
------------------------------------------------
Training Features shape: (307511, 179)
Testing Features shape: (48744, 178)
3.2.3 缺失值处理
数据类型转换完成,我们就可以统一进行缺失值处理,可以采用中位数进行填充:
from sklearn.preprocessing import Imputer imputer = Imputer(strategy = \'median\')
train = app_train.drop(columns = [\'TARGET\'])
column_list = train.columns.tolist()
# fit with Training_data, fill both Training_data and Testing_data
imputer.fit(train)
train = imputer.transform(train)
test = imputer.transform(app_test)
train = pd.DataFrame(train, columns = column_list)
app_train = pd.concat([train, app_train[\'TARGET\']], axis=1)
app_test = pd.DataFrame(test, columns = column_list)
print(\'Training data shape: \', app_train.shape)
print(\'Testing data shape: \', app_test.shape)
--------------------------------------------------
Training data shape: (307511, 179)
Testing data shape: (48744, 178)
检查是否还有缺失值:
print(app_train.isnull().sum()) print(app_test.isnull().sum())
需要注意的是,此处"SK_ID_CURR" 经过处理之后变成float类型,需要重新转换成 int类型:
app_train["SK_ID_CURR"] = app_train["SK_ID_CURR"].astype(int) app_test["SK_ID_CURR"] = app_test["SK_ID_CURR"].astype(int)
3.2.4 异常值处理
对年龄进行异常值检查(原始数据为天,需要除以365,并且取负数):
(app_train[\'DAYS_BIRTH\'] / -365).describe() -----------------------------------------------
count 307511.000000
mean 43.936973
std 11.956133
min 20.517808
25% 34.008219
50% 43.150685
75% 53.923288
max 69.120548
Name: DAYS_BIRTH, dtype: float64
看起来很正常,无异常,再看一下在职天数:
app_train[\'DAYS_EMPLOYED\'].describe() -----------------------------------------
count 307511.000000
mean 63815.045904
std 141275.766519
min -17912.000000
25% -2760.000000
50% -1213.000000
75% -289.000000
max 365243.000000
Name: DAYS_EMPLOYED, dtype: float64
最大值为365243天,换算成年即100年,明显不合理,属于异常值。
出于好奇,对异常客户进行分析,看看他们的违约率比其他客户高还是低。
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))
-------------------------------------------------------------
The non-anomalies default on 8.66% of loans
The anomalies default on 5.40% of loans
There are 55374 anomalous days of employment
可以看到,这些异常值的客户违约率比其他客户还要低,且数量还不少。
我们需要对异常值进行处理,处理异常值取决于具体情况,没有固定的规则。最安全的方法之一就是将异常值视为缺失值处理,然后在使用算法之前填充它们。这里我们将用(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_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)))
---------------------------------------------------
There are 9274 anomalies in the test data out of 48744 entries
使用中位数对异常值转换后的缺失值进行填充:
from sklearn.preprocessing import Imputer imputer = Imputer(strategy = \'median\')
train = app_train[\'DAYS_EMPLOYED\'].values.reshape(-1, 1)
imputer.fit(train)
train = imputer.transform(app_train[\'DAYS_EMPLOYED\'].values.reshape(-1, 1))
test = imputer.transform(app_test[\'DAYS_EMPLOYED\'].values.reshape(-1, 1))
app_train[\'DAYS_EMPLOYED\'] = train
app_test[\'DAYS_EMPLOYED\'] = test
print(app_train[\'DAYS_EMPLOYED\'].describe())
print(app_test[\'DAYS_EMPLOYED\'].describe())
---------------------------------------------
count 307511.000000
mean -2251.606131
std 2136.193492
min -17912.000000
25% -2760.000000
50% -1648.000000
75% -933.000000
max 0.000000
Name: DAYS_EMPLOYED, dtype: float64
count 48744.000000
mean -2319.063639
std 2102.150130
min -17463.000000
25% -2910.000000
50% -1648.000000
75% -1048.000000
max -1.000000
Name: DAYS_EMPLOYED, dtype: float64
从上面可以看出,已经没有异常值了。
3.2.5 重复值处理
看一下有没有重复值,有则直接删除:
print(app_train.duplicated().sum()) # 查看重复值的数量 ------------------------------------------
0
没有重复值,不需要进行处理。
4、数据分析
4.1 相关系数分析
使用.corr方法计算每个变量与目标之间的相关系数。
相关系数并不是表示特征“相关性”的最佳方法,但它确实让我们了解了数据中可能存在的关系。
# 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))
---------------------------------------------------------------
Most Positive Correlations:
OCCUPATION_TYPE_Laborers 0.043019
FLAG_DOCUMENT_3 0.044346
REG_CITY_NOT_LIVE_CITY 0.044395
FLAG_EMP_PHONE 0.045982
NAME_EDUCATION_TYPE_Secondary / secondary special 0.049824
REG_CITY_NOT_WORK_CITY 0.050994
DAYS_ID_PUBLISH 0.051457
CODE_GENDER_M 0.054713
DAYS_LAST_PHONE_CHANGE 0.055218
NAME_INCOME_TYPE_Working 0.057481
REGION_RATING_CLIENT 0.058899
REGION_RATING_CLIENT_W_CITY 0.060893
DAYS_EMPLOYED 0.063368
DAYS_BIRTH 0.078239
TARGET 1.000000
Name: TARGET, dtype: float64
Most Negative Correlations:
EXT_SOURCE_2 -0.160295
EXT_SOURCE_3 -0.155892
NAME_EDUCATION_TYPE_Higher education -0.056593
CODE_GENDER_F -0.054704
NAME_INCOME_TYPE_Pensioner -0.046209
DAYS_EMPLOYED_ANOM -0.045987
ORGANIZATION_TYPE_XNA -0.045987
AMT_GOODS_PRICE -0.039623
REGION_POPULATION_RELATIVE -0.037227
NAME_CONTRACT_TYPE -0.030896
AMT_CREDIT -0.030369
FLAG_DOCUMENT_6 -0.028602
NAME_HOUSING_TYPE_House / apartment -0.028555
NAME_FAMILY_STATUS_Married -0.025043
HOUR_APPR_PROCESS_START -0.024166
Name: TARGET, dtype: float64
从上面看出,和目标值有较大正相关性的有 DAYS_BIRTH 这个特征,有较大负相关性的有 EXT_SOURCE_2 和 EXT_SOURCE_3
这两个特征,可以进一步进行分析:
plt.figure(figsize=(10, 8)) # KDE图中按时偿还的贷款
sns.kdeplot(app_train.loc[app_train[\'TARGET\'] == 0, \'DAYS_BIRTH\'] / -365, label = \'target == 0\')
# KDE图中未按时偿还的贷款
sns.kdeplot(app_train.loc[app_train[\'TARGET\'] == 1, \'DAYS_BIRTH\'] / -365, label = \'target == 1\')
plt.xlabel(\'Age (years)\')
plt.ylabel(\'Density\')
plt.title(\'Distribution of Ages\')
plt.show()
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200507235247598.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM4NDc4NA==,size_16,color_FFFFFF,t_70#pic_center)
target ==
1曲线向范围的较年轻端倾斜,说明随着客户年龄的增长,他们往往会更经常地按时偿还贷款。虽然这不是一个显著的相关性(-0.07相关系数),但这个变量很可能在机器学习模型中有用,因为它确实会影响目标。
对于其余两个特征,也可以进行相应的分析,并可以利用这几个特征进行特征工程的选择。(本文为简单起见,未进行特征工程)
5、模型训练
5.1 数据标准化
- 在使用算法之前,进行 数据标准化处理,将数据集中的特征转换成相同的量纲,从而消除不同量纲对算法造成的负面影响。
from sklearn.preprocessing import Imputer, MinMaxScaler imputer = Imputer(strategy = \'median\')
scaler = MinMaxScaler(feature_range = [0,1])
train = app_train.drop(columns = [\'TARGET\'])
scaler.fit(train)
train = scaler.transform(train)
test = scaler.transform(app_test)
print(\'Training data shape: \', train.shape)
print(\'Testing data shape: \', test.shape)
--------------------------------------------
Training data shape: (307511, 183)
Testing data shape: (48744, 183)
5.2 使用逻辑回归训练
- 直接使用简单的逻辑回归
from sklearn.linear_model import LogisticRegression log_reg = LogisticRegression()
log_reg.fit(train, train_labels)
log_reg_pred = log_reg.predict_proba(test)[:, 1]
submit = app_test[[\'SK_ID_CURR\']]
submit[\'TARGET\'] = log_reg_pred
submit.head()
submit.to_csv(\'log_reg_baseline.csv\', index = False)
提交之后,分数如下:
Private Score:0.72683, Public Score:0.73322
- 使用网格交叉验证计算出最佳参数的逻辑回归:
from sklearn.linear_model import LogisticRegression from sklearn.model_selection import GridSearchCV
param_grid = {\'C\' : [0.01,0.1,1,10,100],
\'penalty\' : [\'l1\',\'l2\']}
log_reg = LogisticRegression()
grid_search = GridSearchCV(log_reg, param_grid, scoring = \'roc_auc\', cv = 5)
grid_search.fit(train, train_labels)
# Train on the training data
log_reg_best = grid_search.best_estimator_
log_reg_pred = log_reg_best.predict_proba(test)[:, 1]
submit = app_test[[\'SK_ID_CURR\']]
submit[\'TARGET\'] = log_reg_pred
submit.head()
submit.to_csv(\'log_reg_baseline_gridsearch2.csv\', index = False)
使用交叉验证,效果有一点提升,分数如下:
Private Score:0.72770, Public Score:0.73452
5.3 简单优化
增加领域知识特征,我们可以创建几个特性,试图捕捉我们认为对于判断客户是否会拖欠贷款可能很重要的信息。
- CREDIT_INCOME_PERCENT: 信贷金额占客户收入的百分比
- ANNUITY_INCOME_PERCENT: 贷款年金占客户收入的百分比
- CREDIT_TERM: 以月为单位支付的期限(因为年金是每月到期的金额)
- DAYS_EMPLOYED_PERCENT: 就职天数占客户年龄的百分比
app_train_domain = app_train.copy() app_train[\'CREDIT_INCOME_PERCENT\'] = app_train_domain[\'AMT_CREDIT\'] / app_train_domain[\'AMT_INCOME_TOTAL\']
app_train[\'ANNUITY_INCOME_PERCENT\'] = app_train_domain[\'AMT_ANNUITY\'] / app_train_domain[\'AMT_INCOME_TOTAL\']
app_train[\'CREDIT_TERM\'] = app_train_domain[\'AMT_ANNUITY\'] / app_train_domain[\'AMT_CREDIT\']
app_train[\'DAYS_EMPLOYED_PERCENT\'] = app_train_domain[\'DAYS_EMPLOYED\'] / app_train_domain[\'DAYS_BIRTH\']
测试集进行同样处理:
app_test_domain = app_test.copy() app_test[\'CREDIT_INCOME_PERCENT\'] = app_test_domain[\'AMT_CREDIT\'] / app_test_domain[\'AMT_INCOME_TOTAL\']
app_test[\'ANNUITY_INCOME_PERCENT\'] = app_test_domain[\'AMT_ANNUITY\'] / app_test_domain[\'AMT_INCOME_TOTAL\']
app_test[\'CREDIT_TERM\'] = app_test_domain[\'AMT_ANNUITY\'] / app_test_domain[\'AMT_CREDIT\']
app_test[\'DAYS_EMPLOYED_PERCENT\'] = app_test_domain[\'DAYS_EMPLOYED\'] / app_test_domain[\'DAYS_BIRTH\']
增加领域特征之后,再次使用简单的逻辑回归进行训练(交叉验证太耗时间,用简单的比较看看效果):
from sklearn.linear_model import LogisticRegression log_reg = LogisticRegression()
log_reg.fit(train, train_labels)
log_reg_pred = log_reg.predict_proba(test)[:, 1]
submit = app_test[[\'SK_ID_CURR\']]
submit[\'TARGET\'] = log_reg_pred
submit.head()
submit.to_csv(\'log_reg_baseline_domain.csv\', index = False)
增加领域特征之后,效果有一点提升,分数如下:
Private Score:0.72805, Public Score:0.73434
5.4 使用随机森林训练
- 增加领域特征之后,使用随机森林训练,看看效果怎么样
from sklearn.ensemble import RandomForestClassifier # 随机森林
random_forest = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)
random_forest.fit(train, train_labels)
predictions = random_forest.predict_proba(test)[:, 1]
submit = app_test[[\'SK_ID_CURR\']]
submit[\'TARGET\'] = predictions
submit.to_csv(\'random_forest_baseline_domain.csv\', index = False)
使用随机森林,效果比逻辑回归还要差一些:
Private Score:0.70975, Public Score:0.70120
6、总结
- 本文仅仅使用一个数据集进行模型训练,并使用逻辑回归和随机森林分别预测,并通过简单优化提升了模型效果。
- 如果进一步进行特征分析,并且使用其它数据集进行训练的话,应该会得到更好的训练模型。
以上是 python数据分析实战之信用卡违约风险预测 的全部内容, 来源链接: utcz.com/z/387611.html