Modelo preditivo de classificação de empresas baixadas por porte - 2014 a 2024 - Dados Receita Federal do Brasil
Carregando o banco de dados
dados_parquet_BRF_filtrado = dados_parquet_BRF %>% select ( MATRIZ,
NOME_F,
SIT_CAD,
DATA_SIT_CAD,
MOT_SIT_CAD,
DATA_INICIO_ATV,
CNAE_PRINC,
CNAE_SEC,
MUNIC,
SIT_ESP,
DATA_SIT_ESP,
CNPJ,
RAZAO_SOCIAL,
NATUREZA_JURIDICA,
CAPITAL_SOCIAL,
PORTE,
OP_SIMPLES,
DT_OP_SIMPLES,
OP_MEI,
DT_OP_MEI,
DT_EX_SIMPLES,
DT_EX_MEI,
code_muni,
PUBLICO_SEBRAE
)
#dados_parquet_SOCIOS_filtrado = dados_parquet_SOCIOS %>% select( CNPJ_BAS,
#IDENTIFICADOR_DE_SÓCIO,
#DATA_DE_ENTRADA_SOCIEDADE)
#transformando o campo CNPJ de 8 dígitos
dados_parquet_BRF_filtrado$CNPJ_BAS = str_sub(dados_parquet_BRF_filtrado$CNPJ,1,8)
Transformação das variáveis de data e rótulos
dados_parquet_BRF_filtrado$ANO_DATA_INICIO_ATV = Year(dados_parquet_BRF_filtrado$DATA_INICIO_ATV)
dados_parquet_BRF_filtrado$MES_DATA_INICIO_ATV = Month(dados_parquet_BRF_filtrado$DATA_INICIO_ATV)
dados_parquet_BRF_filtrado$DIA_DATA_INICIO_ATV = day(dados_parquet_BRF_filtrado$DATA_INICIO_ATV)
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(SIT_CAD_ROTULADA = case_when(
SIT_CAD == 1 ~ "Nula",
SIT_CAD == 2 ~ "Ativa",
SIT_CAD == 3 ~ "Suspensa",
SIT_CAD == 4 ~ "Inapta",
SIT_CAD == 8 ~ "Baixada"
))
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(MATRIZ_ROTULADA = case_when(
MATRIZ == 1 ~ "Matriz",
MATRIZ == 2 ~ "Filial"
))
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(OP_SIMPLES_ROTULADA = case_when(
OP_SIMPLES == 0 ~ "Não",
OP_SIMPLES == 1 ~ "Sim"
))
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(OP_MEI_ROTULADA = case_when(
OP_MEI == 0 ~ "Não",
OP_MEI == 1 ~ "Sim"
))
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(PUBLICO_SEBRAE_ROTULADA = case_when(
PUBLICO_SEBRAE == 0 ~ "Não",
PUBLICO_SEBRAE == 1 ~ "Sim"
))
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(PORTE_ROTULADA = case_when(
PORTE == 1 ~ "MEI",
PORTE == 2 ~ "ME",
PORTE == 3 ~ "EPP",
PORTE == 4 ~ "Demais"
))
dados_parquet_BRF_filtrado$CAPITAL_SOCIAL_NUMERO = as.numeric(gsub(",",".",dados_parquet_BRF_filtrado$CAPITAL_SOCIAL))
#PEGAR AS CNAES PRINCIPAIS E CLASSIFICAR
dados_parquet_BRF_filtrado$CNAE_PRINC_2DIGITOS = as.numeric(str_sub(dados_parquet_BRF_filtrado$CNAE_PRINC,1,2))
dados_parquet_BRF_filtrado = dados_parquet_BRF_filtrado %>% mutate(CNAE_PRINC_2DIGITOS_ROTULADA = case_when(
(CNAE_PRINC_2DIGITOS >= 1 & CNAE_PRINC_2DIGITOS <= 3) ~ "Agricultura, pecuária, produção florestal, pesca e aqüicultura",
(CNAE_PRINC_2DIGITOS >= 5 & CNAE_PRINC_2DIGITOS <= 9) ~ "Indústrias extrativas",
(CNAE_PRINC_2DIGITOS >= 10 & CNAE_PRINC_2DIGITOS <= 33) ~ "Indústrias de transformação",
CNAE_PRINC_2DIGITOS == 35 ~ "Eletricidade e gás",
(CNAE_PRINC_2DIGITOS >= 36 & CNAE_PRINC_2DIGITOS <= 39) ~ "Água, esgoto, atividades de gestão de resíduos e descontaminação",
(CNAE_PRINC_2DIGITOS >= 41 & CNAE_PRINC_2DIGITOS <= 43) ~ "Construção",
(CNAE_PRINC_2DIGITOS >= 45 & CNAE_PRINC_2DIGITOS <= 47) ~ "Comércio, reparação de veículos automotores e motocicletas",
(CNAE_PRINC_2DIGITOS >= 49 & CNAE_PRINC_2DIGITOS <= 53) ~ "Transporte, armazenagem e correio",
(CNAE_PRINC_2DIGITOS >= 55 & CNAE_PRINC_2DIGITOS <= 56) ~ "Alojamento e alimentação",
(CNAE_PRINC_2DIGITOS >= 58 & CNAE_PRINC_2DIGITOS <= 63) ~ "Informação e comunicação",
(CNAE_PRINC_2DIGITOS >= 64 & CNAE_PRINC_2DIGITOS <= 66) ~ "Atividades financeiras, de seguros e serviços relacionados",
CNAE_PRINC_2DIGITOS == 68 ~ "Atividades imobiliárias",
(CNAE_PRINC_2DIGITOS >= 69 & CNAE_PRINC_2DIGITOS <= 75) ~ "Atividades profissionais, científicas e técnicas",
(CNAE_PRINC_2DIGITOS >= 77 & CNAE_PRINC_2DIGITOS <= 82) ~ "Atividades administrativas e serviços complementares",
CNAE_PRINC_2DIGITOS == 84 ~ "Administração pública, defesa e seguridade social",
CNAE_PRINC_2DIGITOS == 85 ~ "Educação",
(CNAE_PRINC_2DIGITOS >= 86 & CNAE_PRINC_2DIGITOS <= 88) ~ "Saúde humana e serviços sociais",
(CNAE_PRINC_2DIGITOS >= 90 & CNAE_PRINC_2DIGITOS <= 93) ~ "Artes, cultura, esporte e recreação",
(CNAE_PRINC_2DIGITOS >= 94 & CNAE_PRINC_2DIGITOS <= 96) ~ "Outras atividades de serviços",
CNAE_PRINC_2DIGITOS == 97 ~ "Serviços domésticos",
CNAE_PRINC_2DIGITOS == 99 ~ "Organismos internacionais e outras instituições extraterritoriais"
))
#natureza juridica
#CÓDIGO DA NATUREZA JURÍDICA
#2011: Empresa Pública
#2232: Sociedade Simples Pura
#2240: Sociedade Simples Limitada
#2305: Empresa Individual de Responsabilidade Limitada (EIRELI)
#3034: Serviço Notarial e Registral (Cartório)
#4014: Empresa Individual Imobiliária
Transformando para classe dicotômica
dados_parquet_BRF_filtrado_modelo = dados_parquet_BRF_filtrado %>% filter(MATRIZ==1 &
DATA_INICIO_ATV > "2014-01-01" &
DATA_INICIO_ATV < "2024-08-10" &
PORTE_ROTULADA == "ME" ) %>% mutate(SIT_CAD_SIM_NAO = case_when(
SIT_CAD == 8 ~ "Sim",
TRUE ~ "Não"
), NATUREZA_JURIDICA = factor(NATUREZA_JURIDICA)
)
Separando em treino e teste
Contando os exemplos da classe no treino e teste
#Quantos exemplos de cada classe na variável resposta no treino?
round(prop.table(table(train_data$SIT_CAD_SIM_NAO)),4)*100
##
## Não Sim
## 48.2 51.8
#Quantos exemplos de cada classe na variável no teste?
round(prop.table(table(test_data$SIT_CAD_SIM_NAO)),4)*100
##
## Não Sim
## 48.15 51.85
Podemos ver que a nossa variável target não sofre de um problema de desbalanceamento muito grande, o que pode facilitar a nossa abordagem de modelagem de classificação;
Para desenvolver o modelo, utlizaremos 3 variáveis explicativas, quais sejam:
O capital social da empresa (Valor investido ao abrir a empresa);
Os Cnaes principais de dois dígitos (Indicam a principal atividade principal da empresa);
Natureza jurídica da empresa (Suas diferentes classificações, por exemplo se é uma empresa pública, um serviço notarial, empresa individual de responsabilidade limitada (EIRELLI), etc…);
Criando o classificador da árvore de decisão
Selecionando variáveis para compor o modelo
Criando o modelo na base de treino
Resumo do modelo
## parsnip model object
##
## n= 161247
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 161247 77727 Sim (0.4820369 0.5179631)
## 2) NATUREZA_JURIDICA=2062,2070,2232,2240,2321,2348 43044 4855 Não (0.8872084 0.1127916) *
## 3) NATUREZA_JURIDICA=2135,2305,2313 118203 39538 Sim (0.3344924 0.6655076)
## 6) CAPITAL_SOCIAL_NUMERO>=9999.995 42899 19121 Não (0.5542787 0.4457213)
## 12) NATUREZA_JURIDICA=2135 42042 18264 Não (0.5655773 0.4344227)
## 24) CAPITAL_SOCIAL_NUMERO>=19934.45 25290 9319 Não (0.6315144 0.3684856) *
## 25) CAPITAL_SOCIAL_NUMERO< 19934.45 16752 7807 Sim (0.4660339 0.5339661) *
## 13) NATUREZA_JURIDICA=2305,2313 857 0 Sim (0.0000000 1.0000000) *
## 7) CAPITAL_SOCIAL_NUMERO< 9999.995 75304 15760 Sim (0.2092850 0.7907150) *
Desenho da árvore
#plot(as.party(dt_fit$fit),
# main = "Decision Tree Model",
# cex = 0.8, # Adjust font size
# col = "blue")
Importância das variáveis
Predizer categorias da target
Predizer probabilidades da target
Juntando teste e resultados
Visualizando resultados em uma tabela
## # A tibble: 6 × 4
## SIT_CAD_SIM_NAO .pred_class .pred_Não .pred_Sim
## <chr> <fct> <dbl> <dbl>
## 1 Sim Não 0.632 0.368
## 2 Sim Não 0.887 0.113
## 3 Não Não 0.887 0.113
## 4 Não Não 0.632 0.368
## 5 Não Não 0.632 0.368
## 6 Não Sim 0.209 0.791
transformando em fator a classe
Criando a matriz de confusão
## Truth
## Prediction Não Sim
## Não 13551 3580
## Sim 5859 17322
calculando a acurácia
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.766
calculando a sensibilidade
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 sens binary 0.698
Calculando a especificidade
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 spec binary 0.829
Customizando as métricas
Calculando metricas de forma customizada
## # A tibble: 3 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.766
## 2 sens binary 0.698
## 3 spec binary 0.829
Criando a Matriz de confusão
## # A tibble: 13 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.766
## 2 kap binary 0.529
## 3 sens binary 0.698
## 4 spec binary 0.829
## 5 ppv binary 0.791
## 6 npv binary 0.747
## 7 mcc binary 0.533
## 8 j_index binary 0.527
## 9 bal_accuracy binary 0.763
## 10 detection_prevalence binary 0.425
## 11 precision binary 0.791
## 12 recall binary 0.698
## 13 f_meas binary 0.742
Plotando a matriz de confusão
conf_mat(model_results,
truth = SIT_CAD_SIM_NAO,
estimate = .pred_class) %>%
# criando o heat map
autoplot(type = "heatmap")
Calculando métricas usando os limiares
Visualizando os limiares
## # A tibble: 6 × 3
## .threshold specificity sensitivity
## <dbl> <dbl> <dbl>
## 1 -Inf 0 1
## 2 0 0 1
## 3 0.209 0.0114 1
## 4 0.466 0.718 0.797
## 5 0.632 0.829 0.698
## 6 0.887 0.941 0.489
Calculando a área embaixo da curva
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 roc_auc binary 0.810
Criando o classificador dos Gradient boosting
Selecionando variáveis para compor o modelo
Criando o modelo na base de treino
Importância das variáveis
Predizer categorias da target
Predizer probabilidades da target
Juntando teste e resultados
Visualizando resultados em uma tabela
## # A tibble: 6 × 4
## SIT_CAD_SIM_NAO .pred_class .pred_Não .pred_Sim
## <chr> <fct> <dbl> <dbl>
## 1 Sim Não 0.736 0.264
## 2 Sim Não 0.904 0.0956
## 3 Não Não 0.935 0.0649
## 4 Não Não 0.659 0.341
## 5 Não Não 0.651 0.349
## 6 Não Sim 0.236 0.764
transformando em fator a classe
Criando a matriz de confusão
## Truth
## Prediction Não Sim
## Não 15104 3855
## Sim 4306 17047
calculando a acurácia
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.798
calculando a sensibilidade
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 sens binary 0.778
Calculando a especificidade
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 spec binary 0.816
Customizando as métricas
Calculando metricas de forma customizada
## # A tibble: 3 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.798
## 2 sens binary 0.778
## 3 spec binary 0.816
Criando a Matriz de confusão
## # A tibble: 13 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.798
## 2 kap binary 0.594
## 3 sens binary 0.778
## 4 spec binary 0.816
## 5 ppv binary 0.797
## 6 npv binary 0.798
## 7 mcc binary 0.594
## 8 j_index binary 0.594
## 9 bal_accuracy binary 0.797
## 10 detection_prevalence binary 0.470
## 11 precision binary 0.797
## 12 recall binary 0.778
## 13 f_meas binary 0.787
Plotando a matriz de confusão
conf_mat(model_results,
truth = SIT_CAD_SIM_NAO,
estimate = .pred_class) %>%
# criando o heat map
autoplot(type = "heatmap")
Calculando métricas usando os limiares
Visualizando os limiares
## # A tibble: 6 × 3
## .threshold specificity sensitivity
## <dbl> <dbl> <dbl>
## 1 -Inf 0 1
## 2 0.000293 0 1
## 3 0.000425 0.0000478 1
## 4 0.000432 0.0000957 1
## 5 0.000472 0.000144 1
## 6 0.000563 0.000191 1
Calculando a área embaixo da curva
## # A tibble: 1 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 roc_auc binary 0.864
Tabela de comparação de métricas dos modelos de classificação
tabela_metricas = data.frame( Modelo= c("Árvore de decisão","Gradient boosting"),
Acuracia = c("0,766","0,800"), F1=c("0,740","0,789"),Precisão =c("0,795","0,802"),Revocação=c("0,693","0,776"),Espeficidade=c("0,833","0,822"),Roc_Auc=c("0,812","0,866"))
kableExtra::kable(tabela_metricas,align = "lccrr",caption = "Tabela de comparação de métricas dos modelos de classificação")
Modelo | Acuracia | F1 | Precisão | Revocação | Espeficidade | Roc_Auc |
---|---|---|---|---|---|---|
Árvore de decisão | 0,766 | 0,740 | 0,795 | 0,693 | 0,833 | 0,812 |
Gradient boosting | 0,800 | 0,789 | 0,802 | 0,776 | 0,822 | 0,866 |
- Analisando os modelos e suas respectivas métricas, cabe destacar que
o modelo que obteve a melhor perfomance geral foi o Gradient
Boosting (GB), sendo superior em todas as métricas,
exceto na especificidade:
- F1 Score GB(78,9%);
- Acurácia GB (80,0%);
- Roc AUC GB (86,6%);
- F1-Score : É a média harmônica da precisão e do recall. É útil quando se deseja um bom equilíbrio entre precisão e recall.
- Interpretação: Um F1-score de 78,9% indica um bom equilíbrio entre precisão e recall, sugerindo que o modelo tem um bom desempenho geral.
- Acurácia : Indica a proporção total de previsões corretas. Em outras palavras, o modelo acertou 80,0% das vezes.
- Interpretação: É um bom indicador geral de desempenho, mas pode ser enganoso em casos de desbalanceamento de classes, que não é o caso da análise.
- Roc: A curva ROC pode fornecer uma visão mais completa do desempenho do modelo, especialmente em cenários com diferentes limiares de classificação. Ela plota a taxa de verdadeiros positivos (sensibilidade) contra a taxa de falsos positivos (1 - especificidade) em vários pontos de corte.
- Área Sob a Curva ROC: A área sob a curva ROC é um único valor que resume o desempenho global do modelo. Um AUC de 1 indica um classificador perfeito, enquanto um AUC de 0.5 indica um classificador aleatório, o nosso foi 0,866, indicando um desempenho bom do modelo.
- Especificidade (0,822) (Perdeu para Árvore
de decisão (0,833) )
- Especificidade: Indica a proporção de exemplos negativos que foram corretamente identificados, ou seja quantas empresas que de fato não foram baixadas e o modelo previu como não baixadas e estava correto em 82,2% das vezes.
- Desenvolvemos um modelo de classificação com alta precisão para prever quais empresas encerrariam suas atividades que contempla 3 variáveis explicativas, quais sejam: O capital social da empresa (Valor investido ao abrir a empresa), os Cnaes principais de dois dígitos (Indicam a principal atividade principal da empresa) e a natureza jurídica da empresa (Suas diferentes classificações, por exemplo se é uma empresa pública, um serviço notarial, empresa individual de responsabilidade limitada (EIRELLI), etc…). Os resultados indicam que o modelo proposto é capaz de identificar corretamente 80,0% dos casos, com um F1-score de 78,9% e uma AUC de 86,6%.