1. Resumen Ejecutivo: Evolución de la Cartera de Créditos

Este conjunto de datos ofrece una visión detallada de la evolución de la cartera de créditos de las entidades financieras en Colombia, proporcionando datos clave sobre las diferentes modalidades de crédito, incluyendo crédito comercial, de consumo, microcrédito y de vivienda. Algunos temas importantes del conjunto de datos incluyen, para el corte de agosto 2023 vs agosto 2024:

Desglose por Modalidad de Crédito:

Indicadores de Riesgo y Mora:

Fuente: https://www.superfinanciera.gov.co/powerbi/reportes/510/542/

1.1 Visión Panorámica de los datos y Limpieza:

Se genera una limpieza basica en los datos, para un mejor manejo.

library(dplyr)
library(scales)
options(scipen=999)


CarteraDatos <- CarteraDatos %>%
  rename_all(~ gsub(" ", "", .)) %>%
  mutate(across(6:9, ~ as.numeric(.) / 1000000))%>%
  mutate(across(10:12, ~ as.numeric(sub(",", ".", gsub(" %", "", .))) / 100))

2. Tabla Dinámica en HTML:

Se presentan los primeros 20 registros por practicidad.

library(formattable)

CarteraDatos<-as.data.frame(CarteraDatos)
CarteraDatosv <- CarteraDatos %>% 
  slice(1:20)

##formattable(CarteraDatosv, list(
##  area(col = 6:9) ~ formatter("span",
  ##                            x ~ dollar(x, prefix = "$", big.mark = ",", digits = 2),
    ##                          style = ~ style(color = "Blue")),
##  area(col = 10:12) ~ formatter("span",
  ##                              x ~ paste0(formatC(x * 100, format = "f", digits = 1), "%"),
    ##                            style = ~ style(color = "Green"))
##))

library(dplyr)
library(DT)

# Crear la tabla interactiva
datatable(CarteraDatosv, 
          options = list(pageLength = 10, autoWidth = TRUE),
          rownames = FALSE) %>%
  # Formato de las columnas 6 a 9 en dólares con dos decimales
  formatCurrency(columns = 6:9, currency = "$", interval = 3, mark = ",", digits = 2) %>%
  # Formato de las columnas 10 a 12 en porcentaje con un decimal
  formatPercentage(columns = 10:12, digits = 1) %>%
  # Aplicar color de fondo dinámico a las columnas 6 a 9 si el valor es alto
  formatStyle(columns = 6:9,
              backgroundColor = styleInterval(100000, c("transparent", "rgba(255, 99, 71, 0.2)")),
              color = "blue") %>%
  # Aplicar color dinámico a las columnas 10 a 12 basado en el valor
  formatStyle(columns = 10:12,
              color = styleInterval(c(0.05, 0.1), c("red", "orange", "green")))

3. Filtro por Variable Categórica: 2024-08 vs 2023-08 en Modalidad Comercial

Se desea detallar la variacion del Promedio de Indicador de Mora y el Indicador de Riesgo en todas las entidades financieras; de agosto de 2024 versus el cierre de agosto del 2023 de la modalidad de credito comercial.

CarteraDatos_Corte <- CarteraDatos %>%
  filter(Modalidad == "Comercial") %>%  
  group_by(Fecha) %>%
  summarise(
    PromedioIndicadorMora = mean(Indicadormora, na.rm = TRUE),
    PromedioIndicadorRiesgo = mean(Indicadorriesgo, na.rm = TRUE)
  )


formattable(CarteraDatos_Corte, list(
  area(col = 2:3) ~ formatter("span",
                                x ~ paste0(formatC(x * 100, format = "f", digits = 1), "%"),
                                style = x ~ style(
                                  color = "Green",
                                  background = ifelse(x > 0.10, "rgba(255, 0, 0, 0.2)", "transparent")
                                ))
))
Fecha PromedioIndicadorMora PromedioIndicadorRiesgo
2023-08-31 9.1% 15.2%
2024-08-31 12.2% 17.3%

4. Acciones Importantes en el Dataset

4.1 Nueva Variable: Cumplimiento Indicador Mora

Se valida la variable “Indicadormora”, dando una categoría de acuerdo a su valor.

library(dplyr)
library(tidyr)
library(formattable)

CarteraDatos <- CarteraDatos %>%
  mutate(CategoriaIndicadorMora = cut(Indicadormora,
                                      breaks = c(0, 0.1, 0.2, 0.3, 0.4, Inf),
                                      labels = c("Bajo", "Moderado", "Alto", "Grave", "Crítico"),
                                      right = FALSE,
                                      include.lowest = TRUE)) %>%
  mutate(CategoriaIndicadorMora = ifelse(is.na(CategoriaIndicadorMora), "Sin Mora", as.character(CategoriaIndicadorMora)))


ConteoCategorias <- CarteraDatos %>%
  group_by(Fecha, CategoriaIndicadorMora) %>%
  summarise(Conteo = n()) %>%
  pivot_wider(names_from = Fecha, values_from = Conteo, values_fill = list(Conteo = 0)) 

# Aplicar formattable con color_bar a las columnas de conteo
formattable(ConteoCategorias, list(
  # Aplica barras de datos a todas las columnas excepto la primera
  area(col = 2:ncol(ConteoCategorias)) ~ color_bar("lightblue", fun = "proportion")
))
CategoriaIndicadorMora 2023-08-31 2024-08-31
Alto 7 2
Bajo 95 93
Crítico 4 7
Grave 2 3
Moderado 17 21
Sin Mora 15 14

4.2 Nueva Variable: Cumplimiento Indicador Riesgo

Se valida la variable “Indicadormora”, dando una categoría de acuerdo a su valor.

library(dplyr)

CarteraDatos <- CarteraDatos %>%
  mutate(CategoriaIndicadorRiesgo = cut(Indicadorriesgo,
                                      breaks = c(0, 0.05, 0.1, 0.2, 0.3, Inf),
                                      labels = c("Bajo", "Moderado", "Alto", "Grave", "Crítico"),
                                      right = FALSE,
                                      include.lowest = TRUE)) %>%
  mutate(CategoriaIndicadorRiesgo = ifelse(is.na(CategoriaIndicadorRiesgo), "Sin Riesgo", as.character(CategoriaIndicadorRiesgo)))


ConteoCategoriasRiesgo <- CarteraDatos %>%
  group_by(Fecha, CategoriaIndicadorRiesgo) %>%
  summarise(Conteo = n()) %>%
  pivot_wider(names_from = Fecha, values_from = Conteo, values_fill = list(Conteo = 0)) 

formattable(ConteoCategoriasRiesgo, list(
  area(col = 2:ncol(ConteoCategoriasRiesgo)) ~ color_bar("pink", fun = "proportion")
))
CategoriaIndicadorRiesgo 2023-08-31 2024-08-31
Alto 46 50
Bajo 22 17
Crítico 10 12
Grave 6 7
Moderado 43 43
Sin Riesgo 13 11

4.3 Valores Missing: Indicador Mora

Se identifican los valores nulos.

nulos_Indicadormora <- sum(is.na(CarteraDatos$Indicadormora))

print(paste("Número de valores nulos en Indicadormora:", nulos_Indicadormora))
## [1] "Número de valores nulos en Indicadormora: 29"

Reemplazamos por el percintil 75, de acuerdo a cada fecha de corte.

library(dplyr)

CarteraDatos <- CarteraDatos %>%
  group_by(Fecha) %>%
  mutate(
    percentil_75 = quantile(Indicadormora, 0.75, na.rm = TRUE),
    Indicadormora = ifelse(is.na(Indicadormora), percentil_75, Indicadormora)
  ) %>%
  select(-percentil_75) %>%
  ungroup()

Verificamos nuevamente presencia de nulos.

nulos_Indicadormora <- sum(is.na(CarteraDatos$Indicadormora))

print(paste("Número de valores nulos en Indicadormora:", nulos_Indicadormora))
## [1] "Número de valores nulos en Indicadormora: 0"

4.4 Box Plot: Indicador de Mora

library(ggplot2)
library(dplyr)
library(patchwork)

# Calcular el IQR para identificar y eliminar outliers en Indicadormora
Q1 <- quantile(CarteraDatos$Indicadormora, 0.25, na.rm = TRUE)
Q3 <- quantile(CarteraDatos$Indicadormora, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1

# Filtrar los datos sin outliers
CarteraDatos_no_outliers <- CarteraDatos %>%
  filter(Indicadormora >= (Q1 - 1.5 * IQR) & Indicadormora <= (Q3 + 1.5 * IQR))

# Crear el boxplot con outliers
boxplot_outliers <- ggplot(CarteraDatos, aes(y = Indicadormora)) +
  geom_boxplot(fill = "skyblue", color = "darkblue", outlier.color = "red", outlier.shape = 16) +
  labs(title = "Boxplot con Outliers", y = "Indicador Mora") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

# Crear el boxplot sin outliers
boxplot_no_outliers <- ggplot(CarteraDatos_no_outliers, aes(y = Indicadormora)) +
  geom_boxplot(fill = "skyblue", color = "darkblue", outlier.shape = NA) +
  labs(title = "Boxplot sin Outliers", y = "Indicador Mora") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

# Organizar ambos gráficos en una misma fila
boxplot_outliers + boxplot_no_outliers

La presencia de outliers en el primer gráfico indica que existen casos de mora que son significativamente altos en comparación con el resto de la distribución. Al remover los outliers, el segundo gráfico permite observar una distribución más representativa del comportamiento típico de Indicador Mora, donde la mayoría de los datos se concentran en niveles bajos de mora. Al eliminar los outliers, el rango de la caja se extiende, permitiendo ver mejor cómo se distribuyen los datos sin la influencia de valores extremos. La distribución general parece estar en el rango de 0 a 0.15, y el rango intercuartílico (IQR) es más fácil de visualizar.

4.5 Tabla de Contingencia: Modalidad vs Categoría Indicador Riesgo Corte 2023

library(dplyr)

CarteraDatos <- CarteraDatos %>%
  mutate(Fecha = as.character(Fecha))

CarteraDatos_filtrada <- CarteraDatos %>%
  filter(Fecha == "2023-08-31")

tabla_contingencia <- table(CarteraDatos_filtrada$Modalidad, CarteraDatos_filtrada$CategoriaIndicadorRiesgo)



tabla_contingencia
##               
##                Alto Bajo Crítico Grave Moderado Sin Riesgo
##   Comercial      11    5       4     4       15          5
##   Consumo        22    5       2     0       14          0
##   Microcrédito   13    0       3     2        3          3
##   Vivienda        0   12       1     0       11          5

La modalidad de Consumo muestra una tendencia elevada hacia el riesgo, con un alto número de registros en la categoría Alto y una ausencia de créditos en la categoría Sin Riesgo. Esto sugiere que los créditos de consumo son más vulnerables a problemas de mora.

El Microcrédito también muestra una distribución preocupante, con un número considerable en los niveles de Alto y algunos en Crítico y Grave, indicando que los microcréditos están más expuestos a riesgos elevados. Los créditos de vivienda muestran un perfil de riesgo bajo, con la mayoría de los créditos clasificados en Bajo o Moderado, y una pequeña proporción sin riesgo. Esto indica que la modalidad de vivienda es la menos riesgosa en comparación con las otras.

4.6 Prueba de Hipotesis Diferencia de Medias: Indicador Riesgo por Modalidad Corte 2023

Se toma la modalidad de crédito de Comercial y Vivienda para todas las entidades Financieras presentes y se evaluara cual es mayor que la otra.

Planteamiento de la Hipótesis:

Queremos evaluar si el promedio del IndicadorRiesgo en la modalidad de Vivienda es mayor que en la modalidad de Comercial. Para ello, formulamos las siguientes hipótesis:

  • Hipótesis Nula (\(H_0\)): El promedio de IndicadorRiesgo en la modalidad de Vivienda es menor o igual que en la modalidad de Comercial.

    \[ H_0: \mu_{\text{Vivienda}} \leq \mu_{\text{Comercial}} \]

  • Hipótesis Alternativa (\(H_1\)): El promedio de IndicadorRiesgo en la modalidad de Vivienda es mayor que en la modalidad de Comercial.

    \[ H_1: \mu_{\text{Vivienda}} > \mu_{\text{Comercial}} \]

Decisión de la Prueba:

Para validar esta hipótesis, se utilizará una prueba t de dos muestras independientes (con varianzas iguales) y una cola. La hipótesis nula se rechazará si el valor p (\(p\)-value) es menor que el nivel de significancia \(\alpha = 0.05\).

library(dplyr)

CarteraDatos_filtrada <- CarteraDatos %>%
  filter(Fecha == "2023-08-31" & (Modalidad == "Vivienda" | Modalidad == "Comercial"))

# Separar los datos en dos muestras
riesgo_vivienda <- CarteraDatos_filtrada %>% filter(Modalidad == "Vivienda") %>% pull(Indicadorriesgo)
riesgo_comercial <- CarteraDatos_filtrada %>% filter(Modalidad == "Comercial") %>% pull(Indicadorriesgo)

# Realizar la prueba t (una cola, asumiendo varianzas iguales)
t_test <- t.test(riesgo_vivienda, riesgo_comercial, alternative = "greater", var.equal = TRUE)


t_test
## 
##  Two Sample t-test
## 
## data:  riesgo_vivienda and riesgo_comercial
## t = -1.3848, df = 61, p-value = 0.9144
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
##  -0.142813       Inf
## sample estimates:
## mean of x mean of y 
##  0.086825  0.151559

El valor p obtenido es 0.9144, lo cual es mucho mayor que el nivel de significancia común de 0.05. Dado que el valor p es tan alto, no rechazamos la hipótesis nula (H0). Esto significa que no tenemos suficiente evidencia para afirmar que el promedio de IndicadorRiesgo es mayor en Vivienda que en Comercial.

5 Análisis Estadístico