21/01/2025, 12:43 modelling
Análise de Risco de Crédito - NuBank
Risco de crédito está associado à possibilidade de um cliente não cumprir com as
obrigações contratuais, como hipotecas, dívidas de cartão de crédito e outros tipos de
empréstimos.
Minimizar o risco de inadimplência é uma grande preocupação para instituições
financeiras. Por esse motivo, bancos comerciais e de investimento, fundos de capital de
risco, empresas de gestão de ativos e seguradoras, para citar alguns, estão cada vez mais
contando com a tecnologia para prever quais clientes são mais propensos a não honrar
com as suas dívidas.
Modelos de Machine Learning têm ajudado essas empresas a melhorar a precisão de
suas análises de risco de crédito, fornecendo um método científico para identificar
devedores em potencial com antecedência.
Neste projeto, construiremos um modelo para prever o risco de inadimplência do cliente
para o Nubank, uma das maiores e importantes Fintechs brasileira.
Modelagem
Objetivo
Prever o risco de inadimplência de clientes usando modelos de aprendizado de
máquina, incluindo Regressão Logística,XGBoost, LightGBM e CatBoost.
Problema: Identificar a possibilidade de clientes não cumprirem obrigações
contratuais, como empréstimos e pagamentos.
Importação das Bibliotecas
In [23]: # Data manipulation and visualization.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Modelling
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler
from category_encoders import TargetEncoder
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC, SVC
from sklearn.neighbors import KNeighborsClassifier
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 1/8
21/01/2025, 12:43 modelling
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.metrics import roc_auc_score, classification_report, confusion_matr
import time
import warnings
# Filter warnings.
import warnings
warnings.filterwarnings('ignore')
Importação do Dataset
In [24]: df = pd.read_csv('data/acquisition_train.csv')
Visualização dos Dados
In [25]: df.head(2)
Out[25]: ids target_default score_1 sco
343b7e7b-
2cf8-e508-
0 False 1Rk8w4Ucd5yR3KcqZzLdow== IOVu8au3ISbo6+zmfnYwM
b8fd-
0a0285af30aa
bc2c7502-
bbad-0f8c-
1 False DGCQep2AE5QRkNCshIAlFQ== SaamrHMo23l/3TwXOWgVz
39c3-
94e881967124
2 rows × 43 columns
Infos dos Dados
In [26]: df.info()
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 2/8
21/01/2025, 12:43 modelling
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 45000 entries, 0 to 44999
Data columns (total 43 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ids 45000 non-null object
1 target_default 41741 non-null object
2 score_1 44438 non-null object
3 score_2 44438 non-null object
4 score_3 44438 non-null float64
5 score_4 45000 non-null float64
6 score_5 45000 non-null float64
7 score_6 45000 non-null float64
8 risk_rate 44438 non-null float64
9 last_amount_borrowed 15044 non-null float64
10 last_borrowed_in_months 15044 non-null float64
11 credit_limit 31200 non-null float64
12 reason 44434 non-null object
13 income 44438 non-null float64
14 facebook_profile 40542 non-null object
15 state 44438 non-null object
16 zip 44438 non-null object
17 channel 44438 non-null object
18 job_name 41664 non-null object
19 real_state 44438 non-null object
20 ok_since 18455 non-null float64
21 n_bankruptcies 44303 non-null float64
22 n_defaulted_loans 44426 non-null float64
23 n_accounts 44438 non-null float64
24 n_issues 33456 non-null float64
25 application_time_applied 45000 non-null object
26 application_time_in_funnel 45000 non-null int64
27 email 45000 non-null object
28 external_data_provider_credit_checks_last_2_year 22372 non-null float64
29 external_data_provider_credit_checks_last_month 45000 non-null int64
30 external_data_provider_credit_checks_last_year 29876 non-null float64
31 external_data_provider_email_seen_before 42767 non-null float64
32 external_data_provider_first_name 45000 non-null object
33 external_data_provider_fraud_score 45000 non-null int64
34 lat_lon 43637 non-null object
35 marketing_channel 41422 non-null object
36 profile_phone_number 45000 non-null object
37 reported_income 45000 non-null float64
38 shipping_state 45000 non-null object
39 shipping_zip_code 45000 non-null int64
40 profile_tags 45000 non-null object
41 user_agent 44278 non-null object
42 target_fraud 1522 non-null object
dtypes: float64(18), int64(4), object(21)
memory usage: 14.8+ MB
Temos dados ausentes
Exclusão das Variáveis não significativas
Seguindo como base os processos feitos na Análise Exploratória de Dados - EDA,
vamos excluir logo de início as variáveis não relevantes para o projeto, seguido da
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 3/8
21/01/2025, 12:43 modelling
variável Shipping State por não agregar até o momento nenhuma significância para
o projeto. Não vejo valor na distribuição dos estados com relação as outras
variáveis.
In [27]: # cópia do dataframe
df2 = df.copy()
In [28]: exclude_columns = ["ids", "score_1", "score_2", "score_4", "score_5", "score_6",
"email", "external_data_provider_first_name", "external_data
"application_time_applied", "profile_phone_number", "applica
"profile_tags", "user_agent", "shipping_state","target_fraud
df2.drop(labels = exclude_columns, axis=1, inplace=True)
Alteração variável Target Default para Num
In [29]: df2['target_default'] = df2['target_default'].map({False: 0, True: 1})
Alteração da Variável credit_limit com valor 0
A variável credit_limit possui um valor mínimo de 0.000000, isso não existe em
instituições financeiras, é obrigatório liberar um valor X de crédito para o cliente.
Portanto, este valor será substituído por NaN.
In [30]: df2['credit_limit'] = df2['credit_limit'].apply(lambda x: np.nan if x == 0 else
Alteração da Variável reported_income com Valor Inf
A variável reported_income possui um valor máximo descrito como inf(infinito),
nesse caso, vamos alterar para o tipo NaN.
In [31]: df2.replace([np.inf, -np.inf], np.nan, inplace=True)
Feature Engineering
Seguindo o que foi feito com as "Perguntas de Negócio utilizando o Score 3" na Análise
Exploratória no eda.ipynb, que é o Score mais robusto para o projeto, vamos criar uma
nova variável chamada Scores, onde vamos dividir os valores seguindo com base na
descrição do Score anteriormente:
O score brasileiro é geralmente dividido em faixas:
Baixo (0-300): Alto risco de inadimplência.
Médio (301-700): Risco moderado.
Alto (701-1000): Baixo risco de inadimplência.
Então vamos ter uma variável chamada de "score" que será dividida com os valores
dessa forma e vamos excluir as outras colunas de score para essa nova análise.
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 4/8
21/01/2025, 12:43 modelling
In [32]: def scores(score):
if score <= 300:
return 'baixo'
elif score >= 301 and score <= 700:
return 'medio'
else:
return 'alto'
df2['score'] = df2['score_3'].apply(scores)
Exclusão da variável score_3
In [33]: df2.drop(columns=['score_3'], axis = 1, inplace = True)
Divisão dos dados em treino e teste
Antes de iniciar as transformações e pré-processamentos dos dados, é necessário
dividir o dataset em treino e teste, mas porque?
Porque precisamos evitar o "Data Leakage". Data Leakage ocorre quando
informações do conjunto de dados de teste ou validação vazam para o
conjunto de treinamento durante o pré-processamento ou modelagem, ou
quando informaçõpes do target vazam para as features. Nessas situações, o que
vai acontecer é que você vai ver um modelo muito bom, mas isso será ilusório,
pois o seu modelo "roubou" para ter o resultado bom.
Referências sobre Data Leakage:
https://www.linkedin.com/company/universidade-dos-dados/posts/?
feedView=all
https://www.casadocodigo.com.br/products/livro-escd
https://estatsite.com.br/2020/12/12/data-leakage-o-erro-que-ate-os-grandes-
cometem/
In [34]: # Dividindo os dados primeiro
X = df2.drop(columns=['target_default'], axis = 1)
y = df2['target_default'].copy()
In [35]: # Aplicando o One Hot Encoding
X = pd.get_dummies(X, columns=['score'])
In [36]: X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,random_s
In [37]: X_train.head()
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 5/8
21/01/2025, 12:43 modelling
Out[37]: risk_rate last_amount_borrowed last_borrowed_in_months credit_limit income
25180 0.17 NaN NaN 32451.0 95035.02
12555 0.23 NaN NaN NaN 86026.89
29153 0.40 NaN NaN NaN 45042.92
23838 0.28 NaN NaN NaN 74039.33
35686 0.22 6029.18 36.0 NaN 55026.98
Pré-Processamento
Preencher valores ausentes em X_train e X_test
In [38]: # Preencher valores ausentes em X_train e X_test com a média de X_train
X_train.fillna(X_train.mean(), inplace=True)
X_test.fillna(X_train.mean(), inplace=True) # Usar a média de X_train para X_te
Preencher valores ausentes em y_train e y_test
In [39]: # Preencher valores ausentes em y_train e y_test com a mediana
y_train.fillna(y_train.median(), inplace=True)
y_test.fillna(y_train.median(), inplace=True) # Usar a mediana de y_train para
In [40]: numerical_features = X_train.select_dtypes('number').columns.to_list()
In [41]: numerical_pipeline = Pipeline(
steps=[
('std_scaler', StandardScaler())
]
)
preprocessor = ColumnTransformer(
transformers=[
('std_scaler', numerical_pipeline, numerical_features)
], remainder="passthrough"
)
In [42]: X_train_prepared = preprocessor.fit_transform(X_train, y_train)
Função de avaliação para fornecer todas as métricas após o
treinamento do modelo
In [43]: def evaluate_model(true, predicted):
mae = mean_absolute_error(true, predicted)
mse = mean_squared_error(true, predicted)
rmse = np.sqrt(mean_squared_error(true, predicted))
r2_square = r2_score(true, predicted)
return mae, rmse, r2_square
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 6/8
21/01/2025, 12:43 modelling
In [44]: models = {
'Logistic Regression': LogisticRegression(),
'XGBoost': XGBClassifier()
}
model_list = []
r2_list =[]
models_val_scores = dict()
models_train_scores = dict()
n_folds = 5
stratified_kfold = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=
for i in range(len(list(models))):
model = list(models.values())[i]
start_time = time.time()
model.fit(X_train_prepared, y_train)
end_time = time.time()
training_time = end_time - start_time
y_train_pred = model.predict(X_train_prepared)
y_test_pred = model.predict(X_test)
y_train_pred = model.predict(X_train_prepared)
train_score = roc_auc_score(y_train, y_train_pred)
val_scores = cross_val_score(model, X_train_prepared, y_train, scoring='roc_
avg_val_score = val_scores.mean()
val_score_std = val_scores.std()
models_val_scores[model] = avg_val_score
models_train_scores[model] = train_score
model_list.append(list(models.keys())[i])
print(f'{model} results: ')
print('-'*50)
print(f'Training score: {train_score}')
print(f'Average validation score: {avg_val_score}')
print(f'Standard deviation: {val_score_std}')
print(f'Training time: {round(training_time, 5)} seconds')
print()
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 7/8
21/01/2025, 12:43 modelling
LogisticRegression() results:
--------------------------------------------------
Training score: 0.5
Average validation score: 0.6042375852567284
Standard deviation: 0.006494068734945479
Training time: 0.02824 seconds
XGBClassifier(base_score=None, booster=None, callbacks=None,
colsample_bylevel=None, colsample_bynode=None,
colsample_bytree=None, device=None, early_stopping_rounds=None,
enable_categorical=False, eval_metric=None, feature_types=None,
gamma=None, grow_policy=None, importance_type=None,
interaction_constraints=None, learning_rate=None, max_bin=None,
max_cat_threshold=None, max_cat_to_onehot=None,
max_delta_step=None, max_depth=None, max_leaves=None,
min_child_weight=None, missing=nan, monotone_constraints=None,
multi_strategy=None, n_estimators=None, n_jobs=None,
num_parallel_tree=None, random_state=None, ...) results:
--------------------------------------------------
Training score: 0.5465762872874316
Average validation score: 0.5729452332304513
Standard deviation: 0.005895054707272672
Training time: 0.15131 seconds
In [ ]:
file:///C:/Users/rafael/Documents/credit_risk_nubank/notebooks/modelling.html 8/8