Análisis Extendido de Tarificación Telemática: Un Enfoque Dual con Índices de Comportamiento

Author

AC (Versión Mejorada)

Published

June 16, 2025

1. Introducción al Análisis de Tarificación Telemática

1.1. ¿Qué es la Tarificación Telemática?

La tarificación telemática, también conocida como “pago por uso” (Pay-As-You-Drive) o “pago por cómo se conduce” (Pay-How-You-Drive), es un paradigma innovador en el sector de los seguros de automóviles. En lugar de basar las primas únicamente en factores demográficos y de historial tradicionales (edad, sexo, lugar de residencia, historial de siniestros), la telemática incorpora datos en tiempo real sobre el comportamiento de conducción.

Mediante dispositivos instalados en el vehículo o aplicaciones móviles, se recopila información detallada como la velocidad, los patrones de aceleración y frenado, los giros, las distancias recorridas y los horarios de conducción. Estos datos permiten a las aseguradoras crear un perfil de riesgo mucho más preciso y personalizado para cada conductor, recompensando los hábitos de conducción seguros con primas más bajas y, a la inversa, ajustando las tarifas para los conductores de mayor riesgo.

1.2. El Dataset: catelematic13 de CASdatasets

Para este análisis, utilizaremos el conjunto de datos catelematic13, disponible en el paquete de R CASdatasets. Este dataset proviene de una aseguradora canadiense y contiene información de una cartera de pólizas de automóviles donde se ha implementado un programa telemático. Es un recurso excelente para explorar cómo los datos de comportamiento pueden complementar los factores de tarificación tradicionales.

Variables Clave en el Dataset

El conjunto de datos se compone de tres grandes grupos de variables:

  • Variables Demográficas del Asegurado:
    • InsuredAge, InsuredSex, Marital: Edad, sexo y estado civil del conductor.
  • Variables del Vehículo:
    • CarAge, CarUse, Region: Antigüedad, uso principal (privado o comercial) y región de circulación del vehículo.
  • Variables Telemáticas y de Exposición:
    • TotalMilesDriven: Millas totales conducidas durante el período de observación.
    • Variables PctDrive...: Porcentaje del tiempo de conducción distribuido por día de la semana, hora del día (ej. PctDriveRushAM para hora punta matutina) y franjas horarias.
    • Variables Accel..., Brake..., LeftTurn..., RightTurn...: Conteos de eventos de aceleración, frenado y giros, categorizados por su intensidad (ej. Accel.0.2.0.4 se refiere a eventos de aceleración de intensidad media-baja).
  • Variable Objetivo:
    • NbClaim, AmtClaim: Número de siniestros y monto total de los mismos, que son las variables que buscamos predecir.

1.3. Enfoque de Modelado: Tradicional vs. Índices de Comportamiento

Este informe implementará y comparará dos metodologías de modelado para evaluar el impacto de las variables telemáticas:

  1. Ruta A - Enfoque Tradicional con Selección de Variables:
    • Se realiza una ingeniería de características básica (ej. frenos_por_milla).
    • Se utiliza una técnica de regularización, Elastic Net, para seleccionar automáticamente el subconjunto de variables (demográficas y telemáticas) más predictivo de entre un gran número de candidatos.
    • Este enfoque es potente pero puede carecer de interpretabilidad, ya que la selección es puramente algorítmica.
  2. Ruta B - Enfoque Sofisticado con Índices de Comportamiento:
    • Se agrupan las variables telemáticas en “dimensiones” lógicas de riesgo (ej. Agresividad, Patrón de Uso).
    • Se construyen índices para cada dimensión. Cada índice es en sí mismo un pequeño modelo que resume el riesgo de esa faceta del comportamiento en un solo número.
    • Este enfoque es más interpretable, ya que permite al analista entender qué tipos de comportamiento son más relevantes para el riesgo, en lugar de solo identificar variables individuales.

Ventajas del Enfoque con Índices

El enfoque de índices (Ruta B) ofrece varias ventajas clave, especialmente en el contexto actuarial: * Interpretabilidad Mejorada: Es más fácil explicar al negocio o a un regulador que el “índice de agresividad” de un conductor es alto, que explicar el impacto de diez variables individuales de aceleración y frenado. * Robustez: Al promediar la señal de múltiples variables, los índices tienden a ser más estables y menos sensibles al ruido que las variables individuales. * Reducción de Dimensionalidad Inteligente: Simplifica el modelo final de una manera guiada por el conocimiento del negocio, no solo por optimización matemática.

A lo largo de este documento, desarrollaremos ambas rutas y compararemos sus resultados para determinar qué enfoque ofrece el mejor equilibrio entre poder predictivo y valor de negocio.

2. Carga y Preparación de Datos

El proceso comienza con la carga del dataset, seguido de una limpieza estándar para asegurar la calidad de los datos y la creación de variables objetivo fundamentales.

# Carga de datos
data("catelematic13", package = "CASdatasets")

# Limpieza y preparación inicial
datos_base <- catelematic13 %>%
  janitor::clean_names() %>%
  # TRADUCCIÓN: Renombrar solo las columnas principales a español para mayor claridad y robustez.
  dplyr::rename(
    n_siniestros = nb_claim,
    monto_siniestro = amt_claim,
    millas_totales_conducidas = total_miles_driven,
    edad_asegurado = insured_age,
    sexo_asegurado = insured_sex,
    estado_civil = marital,
    antiguedad_vehiculo = car_age,
    uso_vehiculo = car_use,
    region = region
  ) %>%
  filter(millas_totales_conducidas > 0, antiguedad_vehiculo >= 0, monto_siniestro >= 0) %>%
  mutate(
    across(where(is.character), as.factor),
    tasa_frecuencia = n_siniestros / millas_totales_conducidas,
    indicador_siniestro = ifelse(n_siniestros > 0, 1, 0)
  )

# Vista previa de los datos
head(datos_base)
  duration edad_asegurado sexo_asegurado antiguedad_vehiculo estado_civil
1      182             44         Female                   3      Married
2      184             48         Female                   6      Married
3      183             71           Male                   6      Married
4      183             84           Male                  10      Married
5      365             35           Male                   8       Single
6      366             23         Female                   8       Single
  uso_vehiculo credit_score region annual_miles_drive years_noclaims territory
1      Commute          575  Urban           12427.42             20        26
2      Commute          847  Urban           12427.42             14        84
3      Private          842  Urban            6213.71             43        30
4      Private          856  Urban            6213.71             65        70
5      Commute          857  Urban           12427.42             18        43
6      Private          778  Urban           12427.42              7        52
  annual_pct_driven millas_totales_conducidas pct_drive_mon pct_drive_tue
1        0.46575342                 8092.3082     0.1476862    0.13691742
2        0.52054795                 3225.8325     0.1537350    0.09712431
3        0.06575343                  253.0245     0.1067015    0.05643660
4        0.44109589                 4374.3796     0.1238072    0.16966107
5        0.52054795                 4872.0788     0.1519791    0.15452400
6        0.55068493                 2692.0651     0.1222951    0.14776215
  pct_drive_wed pct_drive_thr pct_drive_fri pct_drive_sat pct_drive_sun
1     0.1350456     0.1881247     0.1597242     0.1480172    0.08448469
2     0.1297058     0.1824245     0.1686581     0.1574617    0.11089066
3     0.1340388     0.1854794     0.2533812     0.2357443    0.02821830
4     0.1572352     0.1650707     0.1827759     0.1050340    0.09641600
5     0.1622639     0.1089471     0.1615908     0.1455013    0.11519373
6     0.1504202     0.1369380     0.1957199     0.1496481    0.09721652
  pct_drive_2hrs pct_drive_3hrs pct_drive_4hrs pct_drive_wkday pct_drive_wkend
1    0.000376624    0.000376624    0.000188312       0.7662338       0.2337662
2    0.013296216    0.000059200    0.000000000       0.7305924       0.2694076
3    0.000000000    0.000000000    0.000000000       0.7329628       0.2670372
4    0.002334849    0.000832575    0.000000000       0.7949318       0.2050682
5    0.008138656    0.002441597    0.000000000       0.7430294       0.2569706
6    0.015872491    0.002820712    0.001410356       0.7512751       0.2487249
  pct_drive_rush_am pct_drive_rush_pm avgdays_week accel_06miles accel_08miles
1        0.15000000        0.16188312     6.500000            65             5
2        0.04059243        0.13118487     5.388865            70            15
3        0.07925931        0.14111170     5.148138             9             3
4        0.11158330        0.05832575     6.482803            73             3
5        0.11697059        0.15372269     5.358025            14             0
6        0.08422331        0.15011975     4.178091            43             5
  accel_09miles accel_11miles accel_12miles accel_14miles brake_06miles
1             2             1             1             1            83
2            11             9             7             6           152
3             0             0             0             0            93
4             0             0             0             0            27
5             0             0             0             0            34
6             4             3             2             1            56
  brake_08miles brake_09miles brake_11miles brake_12miles brake_14miles
1            10             4             1             1             0
2            14            10             9             7             6
3             4             1             0             0             0
4             2             1             0             0             0
5             2             1             0             0             0
6             9             5             3             2             2
  left_turn_intensity08 left_turn_intensity09 left_turn_intensity10
1                   469                   225                    58
2                     0                     0                     0
3                     0                     0                     0
4                    80                    22                     2
5                   817                   349                    57
6                     6                     1                     1
  left_turn_intensity11 left_turn_intensity12 right_turn_intensity08
1                    24                    11                   1099
2                     0                     0                      0
3                     0                     0                      0
4                     0                     0                    325
5                    13                     4                   1217
6                     0                     0                     12
  right_turn_intensity09 right_turn_intensity10 right_turn_intensity11
1                    615                    219                    101
2                      0                      0                      0
3                      0                      0                      0
4                    111                     18                      4
5                    538                     88                     18
6                      4                      1                      0
  right_turn_intensity12 n_siniestros monto_siniestro tasa_frecuencia
1                     40            1        883.5548    0.0001235741
2                      0            0          0.0000    0.0000000000
3                      0            0          0.0000    0.0000000000
4                      2            0          0.0000    0.0000000000
5                      7            0          0.0000    0.0000000000
6                      0            0          0.0000    0.0000000000
  indicador_siniestro
1                   1
2                   0
3                   0
4                   0
5                   0
6                   0

3. Análisis Descriptivo Ampliado

Antes de modelar, es crucial entender la distribución de las variables y su relación con la siniestralidad.

3.1. Variables Demográficas y del Vehículo

Gráficos de Densidad para Variables Continuas

# Densidad de la Edad del Asegurado
p1 <- ggplot(datos_base, aes(x = edad_asegurado)) +
  geom_density(fill = "#0275D8", alpha = 0.7) +
  labs(title = "Distribución de la Edad del Asegurado", x = "Edad", y = "Densidad") +
  theme_light()

# Densidad de la Antigüedad del Vehículo
p2 <- ggplot(datos_base, aes(x = antiguedad_vehiculo)) +
  geom_density(fill = "#5CB85C", alpha = 0.7) +
  labs(title = "Distribución de la Antigüedad del Vehículo", x = "Antigüedad (Años)", y = "Densidad") +
  theme_light()

# Combinar gráficos
p1 + p2

Gráficos de Barras para Variables Categóricas

# Función auxiliar para gráficos de frecuencia media
plot_freq_media <- function(data, var) {
  data %>%
    group_by({{var}}) %>%
    summarise(freq_media = mean(tasa_frecuencia) * 1000, n = n()) %>%
    filter(n > 30) %>%
    ggplot(aes(x = reorder({{var}}, -freq_media), y = freq_media)) +
    geom_col(fill = "#D9534F") +
    labs(y = "Frecuencia Media (/1000 millas)", x = "") +
    theme_light() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Frecuencia por Región y Uso del Vehículo
p_region <- plot_freq_media(datos_base, region) + labs(title = "Frecuencia por Región")
p_uso <- plot_freq_media(datos_base, uso_vehiculo) + labs(title = "Frecuencia por Uso del Vehículo")

p_region + p_uso

4. Ingeniería de Características

Se preparan las variables telemáticas para el modelado. Este es el punto donde nuestras dos rutas de análisis divergen.

# --- Ingeniería de Características Común para Ambas Rutas ---
datos_ingenieria <- datos_base %>%
  mutate(
    aceleraciones_totales = rowSums(dplyr::select(., starts_with("accel_")), na.rm = TRUE),
    frenadas_totales = rowSums(dplyr::select(., starts_with("brake_")), na.rm = TRUE),
    giros_totales = rowSums(dplyr::select(., starts_with("left_turn_"), starts_with("right_turn_")), na.rm = TRUE),
    pct_manejo_punta_total = pct_drive_rush_am + pct_drive_rush_pm,
    frenadas_por_milla = frenadas_totales / millas_totales_conducidas,
    aceleraciones_por_milla = aceleraciones_totales / millas_totales_conducidas,
    giros_por_milla = giros_totales / millas_totales_conducidas
  )

RUTA A: ENFOQUE TRADICIONAL CON ELASTIC NET

En esta ruta, utilizaremos un conjunto amplio de características y dejaremos que un algoritmo de regularización, Elastic Net, seleccione las más importantes.

4.1.A. Selección de Variables con Elastic Net

Concepto: Elastic Net es un modelo de regresión que penaliza la complejidad del modelo, forzando a que los coeficientes de las variables menos importantes se reduzcan a cero. Es una mezcla de las penalizaciones Lasso (que es buena para la selección de variables) y Ridge (que es buena para manejar predictores correlacionados). El resultado es un modelo más simple y robusto.

# Preparar datos para Elastic Net
vars_predictoras_A <- datos_ingenieria %>%
  dplyr::select(
    # Demográficas
    edad_asegurado, sexo_asegurado, antiguedad_vehiculo, estado_civil, region, uso_vehiculo,
    # Telemáticas de Ingeniería
    aceleraciones_totales, frenadas_totales, giros_totales,
    pct_manejo_punta_total, frenadas_por_milla, aceleraciones_por_milla, giros_por_milla,
    # Telemáticas originales de % de uso (nombres originales)
    starts_with("pct_drive_")
  )

matriz_x_A <- model.matrix(~ . - 1, data = vars_predictoras_A)
vector_y_A <- datos_ingenieria$n_siniestros
offset_A <- log(datos_ingenieria$millas_totales_conducidas)

# Ajustar Elastic Net con validación cruzada
set.seed(123)
ajuste_enet <- cv.glmnet(matriz_x_A, vector_y_A, family = "poisson", offset = offset_A, alpha = 0.5)

# Extraer las variables seleccionadas (coeficientes no nulos)
coeficientes_enet <- coef(ajuste_enet, s = "lambda.1se")
vars_seleccionadas_enet <- rownames(coeficientes_enet)[coeficientes_enet[, 1] != 0]
# Excluir el intercepto
vars_seleccionadas_enet <- vars_seleccionadas_enet[vars_seleccionadas_enet != "(Intercept)"]

cat("--- Variables Seleccionadas por Elastic Net ---\n")
--- Variables Seleccionadas por Elastic Net ---
print(vars_seleccionadas_enet)
 [1] "edad_asegurado"      "antiguedad_vehiculo" "estado_civilSingle" 
 [4] "regionUrban"         "frenadas_totales"    "giros_totales"      
 [7] "pct_drive_sun"       "pct_drive_wkday"     "pct_drive_wkend"    
[10] "pct_drive_rush_am"  

4.2.A. Modelado con Variables Seleccionadas

Ahora, ajustamos los modelos finales usando solo el conjunto de predictores que Elastic Net consideró relevantes.

Modelos Utilizados

  • GLM (Modelo Lineal Generalizado): Es la extensión de la regresión lineal para variables de respuesta que no siguen una distribución normal (como nuestro conteo de siniestros). Usamos una familia Poisson, adecuada para datos de conteo. Es simple, rápido y muy interpretable.
  • Random Forest: Es un modelo de ensamblaje que construye múltiples árboles de decisión y promedia sus predicciones. Es muy potente, captura interacciones complejas y es robusto al sobreajuste. Su desventaja es que es una “caja negra”, menos interpretable que un GLM.
# Las predicciones del modelo GLM se harán directamente desde el objeto 'ajuste_enet'.

# Fórmula para Random Forest
# Es necesario recrear un dataframe con las variables seleccionadas por enet
# ya que RF no puede usar la matriz directamente de la misma forma.
datos_para_rf_enet <- as.data.frame(as.matrix(matriz_x_A[, vars_seleccionadas_enet]))
datos_para_rf_enet$tasa_frecuencia <- datos_ingenieria$tasa_frecuencia

formula_rf_enet <- as.formula(paste("tasa_frecuencia ~ ."))
modelo_rf_enet <- randomForest::randomForest(formula_rf_enet, data = datos_para_rf_enet, ntree = 50)

RUTA B: ENFOQUE SOFISTICADO CON ÍNDICES

En esta ruta, creamos nuestros propios predictores de alto nivel (índices) basados en el conocimiento del negocio.

4.1.B. Creación de Índices de Comportamiento

Concepto: En lugar de tratar cada variable telemática por separado, las agrupamos en dimensiones lógicas (Agresividad, Patrón de Uso, etc.). Para cada dimensión, ajustamos un mini-modelo GLM para predecir la siniestralidad. La predicción de este modelo, estandarizada, se convierte en nuestro “índice”, un único y potente predictor que resume esa faceta del comportamiento.

# Función Auxiliar para Crear Índices
crear_indice <- function(data, vars_dimension) {
  if (length(vars_dimension) == 0) return(rep(0, nrow(data)))
  formula_dim <- as.formula(paste("n_siniestros ~", paste(vars_dimension, collapse = " + "), "+ offset(log(millas_totales_conducidas))"))
  modelo_dim <- glm(formula_dim, data = data, family = poisson(link = "log"))
  indice_bruto <- predict(modelo_dim, type = "response") / data$millas_totales_conducidas
  if (sd(indice_bruto, na.rm = TRUE) > 1e-6) return(as.numeric(scale(indice_bruto)))
  else return(rep(0, length(indice_bruto)))
}

# Definir Dimensiones y Crear los Índices
vars_agresividad <- c("frenadas_por_milla", "aceleraciones_por_milla")
vars_curvas <- c("giros_por_milla")
vars_patron_horario <- c("pct_manejo_punta_total")
# Se usan los nombres originales de las columnas, que empiezan con 'pct_drive_'
vars_patron_semanal <- names(datos_ingenieria)[grepl("^pct_drive_(mon|tue|th|fri|sat|sun)$", names(datos_ingenieria))]

datos_con_indices <- datos_ingenieria %>%
  mutate(
    indice_agresividad = crear_indice(., vars_agresividad),
    indice_curvas      = crear_indice(., vars_curvas),
    indice_patron_horario  = crear_indice(., vars_patron_horario),
    indice_patron_semanal = crear_indice(., vars_patron_semanal)
  )

summary(dplyr::select(datos_con_indices, starts_with("indice_")))
 indice_agresividad   indice_curvas        indice_patron_horario
 Min.   : -0.072756   Min.   : -0.003209   Min.   :0            
 1st Qu.: -0.004595   1st Qu.: -0.003209   1st Qu.:0            
 Median : -0.004593   Median : -0.003208   Median :0            
 Mean   :  0.000000   Mean   :  0.000000   Mean   :0            
 3rd Qu.: -0.004586   3rd Qu.: -0.003207   3rd Qu.:0            
 Max.   :243.005484   Max.   :313.138896   Max.   :0            
 indice_patron_semanal
 Min.   :-5.14529     
 1st Qu.:-0.49198     
 Median :-0.05787     
 Mean   : 0.00000     
 3rd Qu.: 0.42245     
 Max.   :23.83461     

4.2.B. Análisis Descriptivo de los Índices

Verificamos que nuestros índices no estén demasiado correlacionados, lo que confirmaría que miden aspectos distintos del riesgo. El índice_patron_horario muestra una varianza nula, lo que significa que la variable pct_manejo_punta_total no tiene poder predictivo por sí sola en este contexto y será excluida de los modelos.

# Filtrar índices con varianza cero antes de graficar
indices_para_analisis <- datos_con_indices %>%
  dplyr::select(starts_with("indice_")) %>%
  dplyr::select(where(~ sd(.x, na.rm = TRUE) > 1e-6))

# Gráfico de Cajas (Box Plot) para visualizar la distribución
if (ncol(indices_para_analisis) > 0) {
  plot_cajas <- indices_para_analisis %>%
    tidyr::pivot_longer(everything(), names_to = "indice", values_to = "valor") %>%
    ggplot(aes(x = indice, y = valor, fill = indice)) +
    geom_boxplot() +
    labs(title = "Distribución de los Índices de Comportamiento", 
         x = "Índice", 
         y = "Valor Estandarizado") +
    theme_light() +
    theme(legend.position = "none", axis.text.x = element_text(angle = 45, hjust = 1))

  print(plot_cajas)
} else {
   cat("No hay índices con varianza para graficar su distribución.\n")
}

4.3.B. Modelado con Índices

Ajustamos los mismos tipos de modelos (GLM y Random Forest), pero esta vez usando nuestros índices de alto nivel como predictores.

# Preparar Fórmulas
variables_demograficas <- c("edad_asegurado", "sexo_asegurado", "antiguedad_vehiculo", "estado_civil", "region", "uso_vehiculo")
variables_indices <- names(indices_para_analisis)
predictores_indices <- c(variables_demograficas, variables_indices)
formula_indices_texto <- paste(predictores_indices, collapse = " + ")
formula_base_texto <- paste(variables_demograficas, collapse = " + ")

# Modelo 1: GLM Base (común a ambas rutas, solo demografía)
modelo_glm_base <- glm(as.formula(paste("n_siniestros ~", formula_base_texto, "+ offset(log(millas_totales_conducidas))")), 
                       data = datos_con_indices, family = poisson(link = "log"))

# Modelo 2B: GLM con Índices
modelo_glm_indices <- glm(as.formula(paste("n_siniestros ~", formula_indices_texto, "+ offset(log(millas_totales_conducidas))")), 
                          data = datos_con_indices, family = poisson(link = "log"))

# Modelo 3B: Random Forest con Índices
formula_rf_indices <- as.formula(paste("tasa_frecuencia ~", formula_indices_texto))
modelo_rf_indices <- randomForest::randomForest(formula_rf_indices, data = datos_con_indices, ntree = 50)

5. Evaluación de Resultados y Comparación de Rutas

Ahora comparamos el rendimiento de todos los modelos de ambas rutas.

5.1. Criterios de Decisión y su Interpretación

  • Tabla de Impacto Financiero: Comparamos el “Costo Observado” (la prima pura real) con el costo predicho por cada modelo, agrupando a los asegurados por nivel de riesgo. Un buen modelo tendrá predicciones cercanas a los valores observados en cada grupo.
  • Lift Chart (Curva de Ganancia): Esta gráfica muestra la capacidad de un modelo para separar a los clientes de alto riesgo de los de bajo riesgo. Una curva que sube rápidamente y se aleja de la diagonal indica un modelo con un alto poder predictivo. Por ejemplo, si la curva muestra que el 20% de la cartera de mayor riesgo (según el modelo) concentra el 50% de los siniestros, el modelo es muy eficiente.
# --- Generación de Predicciones ---
datos_resultados <- datos_con_indices %>%
  mutate(
    prediccion_glm_base = predict(modelo_glm_base, type = "response"),
    # Ruta A
    prediccion_glm_enet = as.numeric(predict(ajuste_enet, newx = matriz_x_A, s = "lambda.1se", type = "response", newoffset = offset_A)),
    prediccion_rf_enet = predict(modelo_rf_enet, newdata = datos_para_rf_enet) * millas_totales_conducidas,
    # Ruta B
    prediccion_glm_indices = predict(modelo_glm_indices, type = "response"),
    prediccion_rf_indices = predict(modelo_rf_indices, type = "response") * millas_totales_conducidas,
    siniestros_reales = n_siniestros
  )

# --- Impacto Financiero por Grupo de Riesgo ---
severidad_promedio <- sum(datos_resultados$monto_siniestro) / sum(datos_resultados$siniestros_reales, na.rm = TRUE)
datos_resultados <- datos_resultados %>%
  mutate(grupo_riesgo = ntile(prediccion_glm_indices, 5)) # Usamos el modelo de índices como referencia

resumen_impacto <- datos_resultados %>%
  group_by(grupo_riesgo) %>%
  summarise(
    Costo_Observado = sum(monto_siniestro) / n(),
    Costo_Pred_GLM_Base = mean(prediccion_glm_base * severidad_promedio, na.rm=T),
    Costo_Pred_GLM_ENet = mean(prediccion_glm_enet * severidad_promedio, na.rm=T),
    Costo_Pred_RF_ENet = mean(prediccion_rf_enet * severidad_promedio, na.rm=T),
    Costo_Pred_GLM_Indices = mean(prediccion_glm_indices * severidad_promedio, na.rm=T),
    Costo_Pred_RF_Indices = mean(prediccion_rf_indices * severidad_promedio, na.rm=T),
    .groups = 'drop'
  ) %>%
  janitor::adorn_totals("row")

cat("\n--- Comparación de Impacto Financiero por Quintil de Riesgo ---\n")

--- Comparación de Impacto Financiero por Quintil de Riesgo ---
knitr::kable(resumen_impacto, digits = 2,
             caption = "Costo Promedio (Prima Pura) por Quintil de Riesgo")
Costo Promedio (Prima Pura) por Quintil de Riesgo
grupo_riesgo Costo_Observado Costo_Pred_GLM_Base Costo_Pred_GLM_ENet Costo_Pred_RF_ENet Costo_Pred_GLM_Indices Costo_Pred_RF_Indices
1 25.84 15.30 19.31 17.35 15.02 19.12
2 49.14 48.30 54.55 50.98 47.56 53.47
3 94.96 91.95 98.34 90.84 90.82 95.94
4 151.95 161.38 165.10 160.07 160.41 158.53
5 344.56 349.52 329.15 349.93 352.65 344.13
Total 666.45 666.45 666.45 669.17 666.45 671.20
# --- NUEVA TABLA: Siniestralidad por Grupo de Riesgo ---
resumen_siniestralidad <- datos_resultados %>%
  group_by(grupo_riesgo) %>%
  summarise(
    N_Polizas = n(),
    N_Siniestros = sum(siniestros_reales),
    Frecuencia_Observada_x_1000 = (N_Siniestros / N_Polizas) * 1000
  ) %>%
  janitor::adorn_totals("row")

cat("\n\n--- Siniestralidad Observada por Quintil de Riesgo ---\n")


--- Siniestralidad Observada por Quintil de Riesgo ---
knitr::kable(resumen_siniestralidad, digits = 2,
             caption = "Frecuencia de Siniestros Observada por Quintil de Riesgo")
Frecuencia de Siniestros Observada por Quintil de Riesgo
grupo_riesgo N_Polizas N_Siniestros Frecuencia_Observada_x_1000
1 19612 96 4.89
2 19612 306 15.60
3 19612 569 29.01
4 19611 1106 56.40
5 19611 2320 118.30
Total 98058 4397 224.21
# --- Lift Chart (Curva de Ganancia) ---
calcular_datos_lift <- function(datos, col_pred, col_real) {
  datos %>%
    dplyr::select(predicho = !!sym(col_pred), real = !!sym(col_real)) %>%
    filter(!is.na(predicho) & is.finite(predicho)) %>%
    arrange(desc(predicho)) %>%
    mutate(
      porc_siniestros = cumsum(real) / sum(real),
      poblacion_acumulada = row_number() / n()
    ) %>%
    group_by(decil = ntile(predicho, 10)) %>%
    summarise(
      max_porc_siniestros = max(porc_siniestros, na.rm = TRUE),
      max_poblacion_acumulada = max(poblacion_acumulada, na.rm = TRUE),
      .groups = 'drop'
    )
}

# Lista de modelos para comparar
lista_preds <- c("prediccion_glm_base", "prediccion_glm_enet", "prediccion_rf_enet", "prediccion_glm_indices", "prediccion_rf_indices")
nombres_modelos <- c("GLM Base", "GLM (Elastic Net)", "RF (Elastic Net)", "GLM (Índices)", "RF (Índices)")
colores_modelos <- c("gray", "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78")
names(colores_modelos) <- nombres_modelos

datos_lift_total <- purrr::map_dfr(lista_preds, ~calcular_datos_lift(datos_resultados, .x, "siniestros_reales"), .id = "modelo_id") %>%
  mutate(modelo_nombre = factor(nombres_modelos[as.integer(modelo_id)], levels = nombres_modelos))

# Gráfico con formato de leyenda ajustado
ggplot(datos_lift_total, aes(x = max_poblacion_acumulada, y = max_porc_siniestros, color = modelo_nombre, linetype = modelo_nombre)) +
  geom_line(linewidth = 1.2) +
  geom_segment(aes(x = 0, y = 0, xend = 1, yend = 1), color = "white", linetype = "dashed") +
  scale_color_manual(values = colores_modelos, name = "Estrategia de Modelado") +
  scale_linetype_manual(values = c("solid", "solid", "dashed", "solid", "dashed"), name = "Estrategia de Modelado") +
  scale_x_continuous(labels = scales::percent, name = "% Acumulado de Cartera (Ordenada por Riesgo)") +
  scale_y_continuous(labels = scales::percent, name = "% Acumulado de Siniestros Capturados") +
  labs(title = "Lift Chart: Comparación de Rutas de Modelado",
       subtitle = "Elastic Net (Azules) vs. Índices de Comportamiento (Naranjas)") +
  theme_light(base_size = 14) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"), 
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom",
    # CORRECCIÓN: Se ajusta el tamaño de la fuente de la leyenda
    legend.title = element_text(size = 10),
    legend.text = element_text(size = 8)
  )

6. Conclusiones e Interpretación de Resultados

Al analizar los resultados de ambas rutas de modelado, podemos extraer varias conclusiones clave:

  1. Validación del Poder Predictivo: La tabla de impacto financiero muestra un resultado excelente. El “Costo Observado” aumenta de manera constante y significativa a través de los quintiles de riesgo (de 25.84 en el grupo 1 a 344.56 en el grupo 5). Esto demuestra que los modelos telemáticos son altamente efectivos para segmentar la cartera; los grupos de riesgo que creamos reflejan una diferencia real y material en la siniestralidad.

  2. Calibración de los Modelos: Todos los modelos (excepto el GLM Base) hacen un buen trabajo al predecir el costo promedio dentro de cada quintil, y sus totales se acercan mucho al “Costo Observado” total. Esto indica que los modelos no solo ordenan bien el riesgo, sino que también están bien calibrados a nivel de cartera.

  3. El Valor de la Telemática es Innegable: Como se vio en análisis anteriores y se confirma aquí, todos los modelos que incorporan variables telemáticas (Ruta A y Ruta B) superan drásticamente al GLM Base. El Lift Chart es la evidencia más clara, mostrando que los modelos telemáticos identifican el riesgo mucho antes.

  4. Rendimiento Predictivo Similar, Ventaja en Interpretabilidad:

    • Ruta A (Elastic Net): Ofrece un rendimiento predictivo muy sólido. El modelo GLM (Elastic Net) y el RF (Elastic Net) siguen de cerca la siniestralidad observada. Su desventaja es la complejidad; el modelo se basa en una lista de variables seleccionadas algorítmicamente que puede ser difícil de explicar en términos de negocio.
    • Ruta B (Índices): Logra un rendimiento predictivo prácticamente idéntico al de la Ruta A, como se ve en las predicciones del GLM (Índices) y RF (Índices). Sin embargo, su ventaja estratégica es inmensa. Nos permite afirmar con confianza que factores como la “agresividad” (frenazos/acelerones) o el “patrón semanal” (cuándo se conduce) son los verdaderos impulsores del riesgo. Esta narrativa es mucho más potente para la toma de decisiones.

Conclusión Final: Si el único objetivo fuera la máxima precisión, ambas rutas serían casi indistinguibles. Sin embargo, en un contexto de negocio real donde la explicabilidad, la implementación y la comunicación son cruciales, el enfoque con índices de comportamiento (Ruta B) es claramente superior. Proporciona el mismo poder predictivo pero con una capa de interpretabilidad que lo convierte en una herramienta estratégica mucho más valiosa.

7. Mejoras Futuras y Próximos Pasos

Este análisis, aunque robusto, puede ser el punto de partida para exploraciones aún más sofisticadas.

7.1. Modelado Geoespacial

Concepto: El riesgo no es uniforme en el espacio. Conducir en una zona urbana densa con intersecciones complejas es inherentemente más riesgoso que en una autopista rural. Si tuviéramos datos de latitud y longitud, podríamos enriquecer el modelo con variables como: * Tipo de vía (autopista, calle urbana, zona rural). * Densidad de tráfico histórica en la zona. * Proximidad a zonas con alta siniestralidad (puntos negros).

Camino a Seguir en R: * Paquetes: sf (para manejar datos espaciales), sp, gstat (para análisis geoestadístico), y leaflet (para visualizaciones interactivas). * Proceso: 1. Convertir los datos de puntos (lat, lon) a objetos espaciales con sf. 2. Cruzar estos datos con capas de información geográfica (mapas de carreteras, datos de tráfico) para crear nuevas características. 3. Incluir estas nuevas variables geoespaciales en los modelos de frecuencia.

7.2. Análisis de Series Temporales

Concepto: El comportamiento de un conductor no es estático. Un conductor puede mejorar con el tiempo (gracias a la retroalimentación) o empeorar. Analizar las variables telemáticas como series temporales podría revelar tendencias importantes. * ¿La agresividad de un conductor aumenta los viernes por la tarde? * ¿Un conductor muestra una tendencia a la baja en eventos de frenado brusco después de los primeros tres meses del programa?

Camino a Seguir en R: * Paquetes: tsibble y fable (para un manejo moderno de series temporales), prophet (de Facebook, para pronósticos robustos). * Proceso: 1. Reestructurar los datos para que tengan un formato de serie temporal (ej. eventos por día o por semana). 2. Descomponer la serie para identificar tendencias, estacionalidad y ruido. 3. Crear características basadas en estas tendencias (ej. la pendiente de la tendencia de agresividad) para incluirlas en el modelo principal.

7.3. Enfoques con Deep Learning

Concepto: Los modelos como GLM o Random Forest requieren que nosotros hagamos la ingeniería de características. Las redes neuronales, especialmente las diseñadas para datos secuenciales, pueden aprender estas características por sí mismas a partir de los datos crudos. * Redes Neuronales Recurrentes (RNN) y LSTMs: Son ideales para analizar secuencias, como los datos de un viaje segundo a segundo (velocidad, aceleración, etc.). Podrían aprender patrones complejos que son invisibles con la agregación manual.

Camino a Seguir en R: * Paquetes: keras y tensorflow (interfaces de R para las populares librerías de Deep Learning). * Proceso: 1. Preparar los datos telemáticos crudos como secuencias (ej. cada viaje es una secuencia de longitud variable). 2. Diseñar y entrenar una arquitectura de red (ej. una LSTM) para que, a partir de la secuencia del viaje, prediga la probabilidad de siniestro. 3. El resultado de este modelo de Deep Learning podría usarse como un “puntaje telemático” ultra sofisticado en un modelo final.