library(tidyverse)
library(data.table)
library(lubridate)
library(janitor)
library(moments)
library(arrow)
library(glue)
library(zoo)
library(stringr)
library(survival)
library(survminer)
library(locfit)
library(ggfortify)
library(condSURV)
library(plotly)
library(readxl)
library(hence.aws.s3)
library(hence.plots)Probabilidade de recuperação de crédito usando análise de sobrevivência
1 Probabilidade de sobrevida
Em algumas situações é interessante saber a probabilidade condicional de ocorrência do evento de interesse dado que o indivíduo já sobreviveu por um determinado período. Essa probabilidade de sobrevida pode ser calculada através da expressão abaixo
\[ CS(x \mid T) = \frac{S(x+T)}{S(T)},\] em que \(x\) é o tempo adicional até a ocorrência do evento de interesse e \(T\) o tempo já decorrido sem que o evento tenha acontecido. As funções de sobrevivência do lado direito da equação podem ser estimadas por métodos bem estabelecidos em análise de sobrevivência como o estimador de Kaplan Meier ou o Modelo de Cox.
2 Preparação dos dados
O objetivo deste exercício é estimar a probabilidade de que um contrato permaneça em inadimplência por \(x\) dias adicionais dado que está em default há \(T\) dias. A preparação dos dados envolve os seguintes passos:
- Filtrar apenas contratos que possuam parcelas cuja data de recebimento é maior do que a data de vencimento ou que possuam parcelas em aberto na data em que a base de dados foi gerada.
- Calcular o tempo até a recuperação do contrato – ou seja: o tempo, em dias, decorrido do momento em que o contrato entrou em inadimplência até o momento em que todas as parcelas em atraso estão quitadas. Note que, por esse critério, o tempo de inadimplência do contrato pode ser maior que o tempo máximo em default de suas parcelas em aberto.
- Exemplo: considere um cliente que atrasou duas parcelas consecutivas – a primeira com vencimento em 01/01 e a segunda em 01/02. Suponha que ele conseguiu quitar a parcela referente a janeiro em 15/02, porém não foi capaz de pagar a parcela atrasada de fevereiro. Nesse caso, o tempo em inadimplência do contrato é de 45 dias e não de 14 dias.
- Caso o contrato continue em default na data de referência da análise, ele entrará no modelo como uma observação censurada.
Observe que, pelo critério 2, um contrato que tenha mudado do status inadimplente para adimplente mais de uma vez ao longo do seu tempo de vida vai aparecer múltiplas vezes na base de dados.
Como na análise de tempo até inadimplência, toda a modelagem será feita com a base de contratos Pro-Soluto da Direcional Engenharia. Os seguintes pacotes serão utilizados.
O código abaixo carrega os dados da AWS.
source("../R/global-s3.R")
cart <-
hence_s3_read_file(
p_bucket = g_bucket,
p_group = g_group,
p_project = g_project,
p_dat_folder = g_dir_raw,
p_file_name = "direcional_20220321-carteira_completa.parquet",
p_method = "paws",
p_file_type = "parquet") %>%
setDT() %>%
clean_names()A função abaixo calcula os dias em atraso de cada contrato de acordo com os passo (2) e (3). Ela será aplicada a todos os contratos filtrados de acordo com o passo (1).
retorna_dias_atraso <- function(df){
if (!"data.frame" %in% class(df)) {
stop("parametro df nao é data frame")
}
dias_atraso <- df %>%
mutate(start_inad = ifelse(
index == 1,data_vencimento,
ifelse(data_vencimento < lag(data_recebimento), NA, data_vencimento)
)
) %>%
fill(start_inad, .direction="down") %>%
mutate(start_inad = as_date(start_inad, origin = lubridate::origin)) %>%
mutate(end_inad = ifelse(is.na(data_recebimento) & index == nrow(.),
data_ref, ifelse(data_recebimento <
lead(data_vencimento) &
!is.na(data_recebimento), data_recebimento, NA))) %>%
mutate(end_inad=(as_date(end_inad, origin = lubridate::origin))) %>%
select("start_inad", "end_inad") %>%
drop_na(.) %>%
mutate(tempo_ate_evento = as.numeric(end_inad - start_inad)) %>%
mutate(evento = ifelse(end_inad == data_ref, 0, 1))
return(dias_atraso)
}Finalmente, o código abaixo gera a base de dados final que será utilizada no cálculo das probabilidades condicionais.
data_ref<-max(cart$data_recebimento, na.rm = TRUE)
cart_inad <-cart %>%
filter(data_recebimento>data_vencimento | (data_vencimento < data_ref &
is.na(data_recebimento))) %>%
select("id_venda", "cpf_cnpj", "uf_empreendimento", "categoria_produto",
"valor_venda", "data_vencimento", "data_recebimento",
"valor_nominal_das_parcelas") %>%
mutate(dias_atraso = ifelse(!is.na(data_recebimento),
data_recebimento - data_vencimento,
data_ref - data_vencimento)) %>%
select("id_venda", "data_vencimento", "data_recebimento", "dias_atraso") %>%
group_by(id_venda) %>%
arrange(data_vencimento, .by_group = T) %>%
mutate(index = row_number(id_venda)) %>%
group_modify(~retorna_dias_atraso(.x))
head(cart_inad)# A tibble: 6 × 5
# Groups: id_venda [1]
id_venda start_inad end_inad tempo_ate_evento evento
<chr> <date> <date> <dbl> <dbl>
1 1-CVBI-11 2019-12-20 2019-12-26 6 1
2 1-CVBI-11 2020-01-20 2020-01-23 3 1
3 1-CVBI-11 2020-02-20 2020-02-26 6 1
4 1-CVBI-11 2020-05-20 2020-05-22 2 1
5 1-CVBI-11 2020-09-20 2020-09-21 1 1
6 1-CVBI-11 2020-11-20 2020-12-03 13 1
Para estimar as funções de sobrevivência necessárias para computar as probabilidades condicionais vamos utilizar o estimador de Kaplan Meier. Sendo assim, como no exemplo anterior envolvendo o cálculo do tempo até a primeira inadimplência, devemos transformar a base de dados em um objeto do pacote ‘survival’.
surv_object<-survival::Surv(time=cart_inad$tempo_ate_evento,
event=cart_inad$evento)3 Resultados
Agora podemos traçar a curva de sobrevivência associada aos dados de tempo até recuperação. Ela mostra a probabilidade de um contrato com pelo menos um dia de atraso permanecer inadimplente por \(n\) dias. Note que a inflexão da curva ocorre perto do dia 180, sinalizando que, depois de 6 meses de atraso, o contrato se torna praticamente irrecuperável.
f1 <- survival::survfit(surv_object ~ 1,
data = cart_inad,
type="kaplan-meier")
kmplot_tempo_recuperacao <-ggsurvplot(
fit = f1,
title="Probabilidade do contrato permanecer inadimplente dado que está com pelo menos um dia de atraso",
xlab = "Tempo em dias",
ylab = "Probabilidade de sobrevivência",
conf.int = T,
conf.int.alpha=c(0.1),
legend="none",
censor = FALSE)
kmplot_tempo_recuperacao Ignoring unknown labels:
• fill : "Strata"
Ignoring unknown labels:
• fill : "Strata"

Observe que a inflexão da curva de Kaplan Meier ocorre perto do dia 180, sinalizando que, depois de 6 meses de atraso, o contrato se torna praticamente irrecuperável. Sendo assim, vamos usar a equação de probabilidade de sobrevida para calcular a probabilidade de que um contrato inadimplente a \(T\) dias alcance \(x=180\) dias de atraso. O código abaixo faz isso.
prob_cond <-NULL
for(i in 1:179){
prob_cond[i] <-f1$surv[180]/f1$surv[i]
}
prob_cond <-as.data.frame(prob_cond) %>%
mutate(dias = row_number()) %>%
setDT()
hence_line_chart(p_x = dias,
p_y = prob_cond,
p_dados = prob_cond,
p_xlab = "Dias em atraso",
p_ylab = "Probabilidade",
p_title = "Probabilidade de que o contrato chegue a pelo menos 180 dias de atraso") %>%
ggplotly