Modelo Logit para analizar la probabilidad de siniestros viales en Bogotá

Cargar librerías necesarias

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
library(caret)
## Loading required package: lattice
## 
## Attaching package: 'caret'
## 
## The following object is masked from 'package:purrr':
## 
##     lift
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following object is masked from 'package:purrr':
## 
##     some
library(randomForest)   # Random Forest
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
## 
## The following object is masked from 'package:ggplot2':
## 
##     margin
library(ggplot2)
library(GGally)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2

1. Cargar y explorar la base de datos

Cargamos los datos generados desde ArcGIS:

vias <- read_excel("C:/Users/tomas/Documents/ArcGIS/Projects/Caso 1 Seguridad Vial/EstadoMallaVial__Ciclorruta.xlsx")

# Verificar estructura
glimpse(vias)
## Rows: 100,466
## Columns: 32
## $ OBJECTID       <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, …
## $ PK_ID_CALZ     <dbl> 355295, 445592, 160549, 24124340, 527859, 361350, 35885…
## $ CIV            <dbl> 7002011, 19002553, 10004728, 13002745, 10000001, 700443…
## $ LOCEST         <dbl> 7, 19, 10, 13, 10, 7, 7, 5, 11, 8, 5, 7, 8, 11, 4, 6, 3…
## $ SUPERFEST      <dbl> 2, 3, 2, 1, 2, 3, 2, 1, 2, 6, 6, 1, 1, 1, 1, 2, 2, 2, 2…
## $ KMCAINDEST     <dbl> 0.033, 0.035, 0.100, 0.068, 0.059, 0.053, 0.031, 0.041,…
## $ TIPOMALLAE     <chr> "Local", "Local", "Intermedia", "Troncal", "Arterial", …
## $ INDEST         <dbl> 81, 88, 90, 97, 85, 92, 94, 93, 74, 100, 0, 84, 89, 94,…
## $ ESTAASHTO      <chr> "SATISFACTORIO", "BUENO", "BUENO", "BUENO", "SATISFACTO…
## $ HAY_SINIESTROS <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ AASHTO_BUE     <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0…
## $ SINIESTROS     <dbl> 2, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 3, 2, 3, 1, 4, 1, 1, 1…
## $ ESTRATO_First  <dbl> 2, 1, 3, 4, 3, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 3, 3, 3, 3…
## $ ZAT            <dbl> 560, 623, 185, 335, 841, 947, 550, 735, 115, 414, 746, …
## $ mean_V_REF     <dbl> 773511.95, 119442.20, 1233204.23, 1797302.25, 1657157.5…
## $ mean_Denpob    <dbl> 523.98940, 247.70615, 471.66883, 323.41839, 487.31143, …
## $ mean_Denemp    <dbl> 22.8759290, 7.0974177, 137.7956290, 161.3095746, 138.06…
## $ mean_ESTRATO   <dbl> 1.8369474, 0.8586321, 2.3637646, 3.6675438, 2.9763595, …
## $ Tipo           <chr> "Residential", "Zona verde o humedal", "Residential", "…
## $ Ancho          <dbl> 7.072055, NA, 7.520000, 13.270000, 6.150000, 6.730000, …
## $ Carriles       <dbl> 2, NA, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, …
## $ SITP           <dbl> 0, NA, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, …
## $ Velocidad      <dbl> 7.363636, NA, 11.925000, 43.920000, 17.200000, 17.55000…
## $ Flujo          <dbl> 674.8853, NA, 2308.3819, 8863.3385, 3513.0228, 1810.787…
## $ Ancho_corr     <dbl> 0.9092420, NA, 2.7398611, 17.7127806, 2.5168989, 0.9921…
## $ Pres_anden     <dbl> 0, NA, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, …
## $ N_Arboles      <dbl> 0, NA, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, …
## $ N_Semaforo     <dbl> 0, NA, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, …
## $ RUTAS_SITP     <dbl> 1, 1, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 12,…
## $ CICLORRUTA     <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ Shape_Length   <dbl> 104.77610, 53.08657, 117.60992, 136.62554, 73.59672, 12…
## $ Shape_Area     <dbl> -114.08796, -123.66252, -350.72188, -238.87473, -204.88…

2. Selección de variables relevantes

Reducimos la base al conjunto de variables clave:

vias_modelo <- vias %>% 
  dplyr::select(
    CIV,
    HAY_SINIESTROS, # variable dependiente binaria
    TIPOMALLAE,
    AASHTO_BUE,
    ESTRATO_First,
    mean_V_REF,
    mean_Denpob,
    mean_Denemp,
    Tipo,
    Ancho,
    Carriles,
    Velocidad,
    Pres_anden,
    N_Arboles,
    N_Semaforo,
    RUTAS_SITP,
    CICLORRUTA
  )

summary(vias_modelo)
##       CIV            HAY_SINIESTROS     TIPOMALLAE          AASHTO_BUE    
##  Min.   :        0   Min.   :0.00000   Length:100466      Min.   :0.0000  
##  1st Qu.:  6001724   1st Qu.:0.00000   Class :character   1st Qu.:0.0000  
##  Median : 10001068   Median :0.00000   Mode  :character   Median :1.0000  
##  Mean   :  9885856   Mean   :0.07911                      Mean   :0.7195  
##  3rd Qu.: 13002190   3rd Qu.:0.00000                      3rd Qu.:1.0000  
##  Max.   :100119153   Max.   :1.00000                      Max.   :1.0000  
##                                                                           
##  ESTRATO_First     mean_V_REF       mean_Denpob     mean_Denemp     
##  Min.   :0.000   Min.   :      0   Min.   :  0.0   Min.   :   0.00  
##  1st Qu.:1.000   1st Qu.: 578680   1st Qu.:213.0   1st Qu.:  20.66  
##  Median :2.000   Median : 975825   Median :340.5   Median :  38.92  
##  Mean   :2.204   Mean   :1078947   Mean   :354.1   Mean   :  64.16  
##  3rd Qu.:3.000   3rd Qu.:1376343   3rd Qu.:476.6   3rd Qu.:  72.90  
##  Max.   :6.000   Max.   :7411032   Max.   :965.2   Max.   :1005.31  
##  NA's   :112     NA's   :44        NA's   :44      NA's   :44       
##      Tipo               Ancho           Carriles       Velocidad      
##  Length:100466      Min.   : 1.950   Min.   :1.000   Min.   : 0.9634  
##  Class :character   1st Qu.: 5.770   1st Qu.:2.000   1st Qu.:11.3684  
##  Mode  :character   Median : 6.890   Median :2.000   Median :14.7951  
##                     Mean   : 6.983   Mean   :2.038   Mean   :16.2867  
##                     3rd Qu.: 7.710   3rd Qu.:2.000   3rd Qu.:19.0800  
##                     Max.   :26.330   Max.   :5.000   Max.   :69.5172  
##                     NA's   :2116     NA's   :2116    NA's   :2116     
##    Pres_anden       N_Arboles         N_Semaforo      RUTAS_SITP     
##  Min.   :0.0000   Min.   :  0.000   Min.   :0.000   Min.   :  1.000  
##  1st Qu.:0.0000   1st Qu.:  0.000   1st Qu.:0.000   1st Qu.:  1.000  
##  Median :1.0000   Median :  0.000   Median :0.000   Median :  1.000  
##  Mean   :0.6344   Mean   :  3.057   Mean   :0.039   Mean   :  3.963  
##  3rd Qu.:1.0000   3rd Qu.:  3.000   3rd Qu.:0.000   3rd Qu.:  1.000  
##  Max.   :1.0000   Max.   :297.000   Max.   :3.000   Max.   :107.000  
##  NA's   :2116     NA's   :2116      NA's   :2116                     
##    CICLORRUTA    
##  Min.   :0.0000  
##  1st Qu.:0.0000  
##  Median :0.0000  
##  Mean   :0.1538  
##  3rd Qu.:0.0000  
##  Max.   :1.0000  
## 

3. Limpieza y preparación de datos

Convertimos variables categóricas adecuadamente y manejamos valores faltantes:

vias_modelo <- vias_modelo %>%
  mutate(
    HAY_SINIESTROS = factor(HAY_SINIESTROS, levels = c(0,1)),
    TIPOMALLAE = factor(TIPOMALLAE),
    AASHTO_BUE = factor(AASHTO_BUE),
    ESTRATO_First = factor(ESTRATO_First),
    Tipo = factor(Tipo),
    Pres_anden = factor(Pres_anden),
    CICLORRUTA = factor(CICLORRUTA)
  ) %>%
  drop_na()

# Confirmar estructura
str(vias_modelo)
## tibble [96,700 × 17] (S3: tbl_df/tbl/data.frame)
##  $ CIV           : num [1:96700] 7.0e+06 1.0e+07 1.3e+07 1.0e+07 7.0e+06 ...
##  $ HAY_SINIESTROS: Factor w/ 2 levels "0","1": 2 2 2 2 2 2 2 2 2 2 ...
##  $ TIPOMALLAE    : Factor w/ 4 levels "Arterial","Intermedia",..: 3 2 4 1 3 3 3 3 3 3 ...
##  $ AASHTO_BUE    : Factor w/ 2 levels "0","1": 2 2 2 2 2 2 2 2 2 2 ...
##  $ ESTRATO_First : Factor w/ 7 levels "0","1","2","3",..: 3 4 5 4 3 3 3 3 3 3 ...
##  $ mean_V_REF    : num [1:96700] 773512 1233204 1797302 1657158 544439 ...
##  $ mean_Denpob   : num [1:96700] 524 471.7 323.4 487.3 99.3 ...
##  $ mean_Denemp   : num [1:96700] 22.88 137.8 161.31 138.06 3.68 ...
##  $ Tipo          : Factor w/ 8 levels "Comercial","Extracción del Suelo",..: 5 5 5 5 6 5 5 5 5 5 ...
##  $ Ancho         : num [1:96700] 7.07 7.52 13.27 6.15 6.73 ...
##  $ Carriles      : num [1:96700] 2 2 3 2 2 2 2 2 2 2 ...
##  $ Velocidad     : num [1:96700] 7.36 11.93 43.92 17.2 17.55 ...
##  $ Pres_anden    : Factor w/ 2 levels "0","1": 1 2 2 2 1 1 2 1 1 1 ...
##  $ N_Arboles     : num [1:96700] 0 1 0 0 0 5 0 0 0 0 ...
##  $ N_Semaforo    : num [1:96700] 0 1 0 0 0 0 0 0 0 0 ...
##  $ RUTAS_SITP    : num [1:96700] 1 3 4 1 1 1 1 1 1 1 ...
##  $ CICLORRUTA    : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...

Se estimó un modelo de regresión logística (logit) para evaluar la probabilidad de ocurrencia de siniestros viales en segmentos de vía (CIV) en Bogotá. La variable dependiente es HAY_SINIESTROS, binaria (1 si hubo al menos un siniestro, 0 si no hubo).

La base usada fue amplia, incluyendo inicialmente 100,466 observaciones, de las cuales quedaron 96,700 luego de limpiar datos faltantes.

4. Creación de variables dummy (controlando multicolinealidad)

Creamos variables dummy con referencia explícita para evitar multicolinealidad, estas serán las categorías base frente a las que las demás se compararán en el modelo:

vias_modelo <- vias_modelo %>%
  mutate(
    TIPOMALLAE = relevel(TIPOMALLAE, ref = "Local"),
    ESTRATO_First = relevel(ESTRATO_First, ref = "1"),
    Tipo = relevel(Tipo, ref = "Residential")
  )

5. División de datos (Entrenamiento y validación)

Separamos la base en entrenamiento (70%) y prueba (30%):

set.seed(123)
index <- createDataPartition(vias_modelo$HAY_SINIESTROS, p = 0.7, list = FALSE)
train <- vias_modelo[index, ]
test <- vias_modelo[-index, ]

6. Modelo de Regresión Logística (Logit)

Estimamos el modelo usando glm:

modelo_logit <- glm(HAY_SINIESTROS ~ 
                      TIPOMALLAE + AASHTO_BUE + ESTRATO_First + mean_V_REF + 
                      mean_Denpob + mean_Denemp + Tipo + Ancho + Carriles + 
                      Velocidad + Pres_anden + N_Arboles + N_Semaforo + 
                      RUTAS_SITP + CICLORRUTA,
                    data = train, family = binomial(link = "logit"))

# Resultados
summary(modelo_logit)
## 
## Call:
## glm(formula = HAY_SINIESTROS ~ TIPOMALLAE + AASHTO_BUE + ESTRATO_First + 
##     mean_V_REF + mean_Denpob + mean_Denemp + Tipo + Ancho + Carriles + 
##     Velocidad + Pres_anden + N_Arboles + N_Semaforo + RUTAS_SITP + 
##     CICLORRUTA, family = binomial(link = "logit"), data = train)
## 
## Coefficients:
##                            Estimate Std. Error z value Pr(>|z|)    
## (Intercept)              -4.807e+00  1.409e-01 -34.126  < 2e-16 ***
## TIPOMALLAEArterial        9.708e-01  5.185e-02  18.725  < 2e-16 ***
## TIPOMALLAEIntermedia      8.719e-03  4.632e-02   0.188 0.850687    
## TIPOMALLAETroncal         1.639e+00  5.922e-02  27.684  < 2e-16 ***
## AASHTO_BUE1               1.611e-01  4.026e-02   4.002 6.27e-05 ***
## ESTRATO_First0            1.508e+00  1.263e-01  11.940  < 2e-16 ***
## ESTRATO_First2            1.291e+00  1.228e-01  10.518  < 2e-16 ***
## ESTRATO_First3            1.612e+00  1.239e-01  13.014  < 2e-16 ***
## ESTRATO_First4            1.367e+00  1.341e-01  10.190  < 2e-16 ***
## ESTRATO_First5            1.179e+00  1.551e-01   7.606 2.83e-14 ***
## ESTRATO_First6            1.158e+00  1.771e-01   6.538 6.23e-11 ***
## mean_V_REF               -6.011e-08  3.182e-08  -1.889 0.058863 .  
## mean_Denpob              -3.081e-04  9.803e-05  -3.143 0.001671 ** 
## mean_Denemp              -2.413e-04  2.123e-04  -1.137 0.255629    
## TipoComercial             3.060e-02  5.393e-02   0.567 0.570468    
## TipoExtracción del Suelo -2.267e-01  6.061e-01  -0.374 0.708384    
## TipoIndustrial            5.083e-01  6.909e-02   7.356 1.89e-13 ***
## TipoOficial               2.273e-01  8.453e-02   2.689 0.007161 ** 
## TipoSin Construir         5.407e-01  6.461e-02   8.369  < 2e-16 ***
## TipoZona Histórica       -3.036e-02  1.292e-01  -0.235 0.814144    
## TipoZona verde o humedal -4.230e-02  1.180e-01  -0.359 0.719968    
## Ancho                     4.166e-02  8.919e-03   4.671 3.00e-06 ***
## Carriles                 -1.417e-01  3.942e-02  -3.593 0.000326 ***
## Velocidad                 6.107e-03  1.765e-03   3.460 0.000540 ***
## Pres_anden1               2.086e-01  4.550e-02   4.585 4.54e-06 ***
## N_Arboles                 3.849e-03  2.050e-03   1.878 0.060390 .  
## N_Semaforo                2.141e-01  5.579e-02   3.837 0.000124 ***
## RUTAS_SITP                2.999e-02  1.345e-03  22.292  < 2e-16 ***
## CICLORRUTA1               4.237e-01  3.567e-02  11.880  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 38026  on 67690  degrees of freedom
## Residual deviance: 31440  on 67662  degrees of freedom
## AIC: 31498
## 
## Number of Fisher Scoring iterations: 7

El resumen del modelo indica cuáles variables explican significativamente la probabilidad de ocurrencia de siniestros (con un nivel de significancia del 5% o menor):

  • Tipo de vía: Vía Troncal y Arterial son altamente significativas (p < 0.001) comparadas con la categoría referencia (“Local”). Esto implica que la probabilidad de siniestro es mayor en vías Troncales (exp(1.639) ≈ 5.15 veces más probable / +415%) y en menor medida en vías Arteriales (+164%). Esto puede estar relacionado con las velocidades, nível de exposicición y conflictos En cambio, las vías intermedias no mostraron significancia estadística.

  • Estado de la vía según AASHTO (AASHTO_BUE): Un buen estado de la vía está asociado significativamente (coeficiente positivo, 0.161 / +17%) con mayor probabilidad de siniestro, sugiriendo que las vías en buen estado concentran más siniestros posiblemente por una mayor exposición al riesgo (mayor flujo, mayor velocidad, etc.).

  • Estrato socioeconómico: Todas las categorías de estrato fueron significativas frente al estrato 1 (base). Los estratos más altos (especialmente estratos 3 - +401% - y 4 - +292% -) están fuertemente asociados con mayor ocurrencia de siniestros, posiblemente por mayor flujo vehicular y cobertura de la ciudad.

  • Valor promedio de predios: Marginalmente significativo (p ~ 0.059). El efecto es levemente negativo, a medida que sube el valor predial, disminuyen mínimamente las probabilidades de siniestro. Sin embargo, la magnitud es muy pequeña (-0.0000006).

  • Densidad poblacional: Aunque pequeña, la relación es estadísticamente significativa (p=0.0017) y de signo negativo. Para cada unidad adicional (p.ej. 1 habitante/ha más), las odds se reducen un 0.03%.

  • Uso del suelo: Destacan tres tipos de uso de suelo:

    • “Sin Construir” (exp(0.54)=1.72 o +72%) y Oficial (exp(0.214) ≈ 1.24 o +25%) asociados positivamente con más siniestros en comparación a las zonas residenciales. Las zonas sin construir suelen tener mayores incentivos al aumento en la velocidad por la falta de permeabilidad e interacción con actores vulnerables, así como menores puntos de referencia en el entorno construido. Y las zonas oficiales al contener importantes equipamientos administrativos, suelen ubicarse en puntos de fácil acceso cercanos a corredores de alta capacidad. En zonas comerciales no hubo efecto significativo. Zonas históricas y de extracción de suelo mostraron efectos negativos o no significativos (menos probabilidad de siniestro).
  • Características físicas y operativas de la vía:

    • Mayor Ancho de vía aumentó significativamente la probabilidad de siniestros (coeficiente positivo), cada metro adicional de ancho se asocia con +4% en las probabilidades de siniestro. Más Carriles está sorprendentemente asociado negativamente (menos probabilidad de siniestro), -13% de probabilidad por cada carril adicional. Podría indicar que vías con más carriles tienen mejor segregación del tráfico o mejores condiciones de cicloinfraestructura, reduciendo interacciones y siniestros.

    • Mayor Velocidad incrementa significativamente la probabilidad de siniestros, cada km/h adicional implica un 0.6% más en las probabilidades de un siniestro.

    • Presencia de andén (acera) no resultó significativa.

    • Presencia de semáforos se asoció significativamente a mayor probabilidad de siniestros (probablemente por concentración en intersecciones conflictivas), cada semáforo extra incrementa en 24% las probabilidades de un siniestro.

  • Rutas SITP (transporte público): Mayor número de rutas del SITP incrementa significativamente la probabilidad de accidentes, por cada ruta adicional del SITP, las probabilidades suben un 3%, indicando zonas con más interacción entre usuarios vulnerables (peatones, ciclistas, usuarios de transporte público).

  • Presencia de ciclorrutas: El efecto positivo es muy significativo; vías con ciclorrutas cercanas tienen un +53% de probabilidad considerablemente mayor de presentar siniestros. Esto posiblemente refleja mayor exposición o interacción entre ciclistas y otros vehículos, especialmente en intersecciones.

7. Diagnóstico del modelo

Evaluamos la presencia de multicolinealidad con el Factor de Inflación de la Varianza (VIF):

vif(modelo_logit)
##                   GVIF Df GVIF^(1/(2*Df))
## TIPOMALLAE    2.717433  3        1.181299
## AASHTO_BUE    1.067754  1        1.033322
## ESTRATO_First 2.981210  6        1.095299
## mean_V_REF    2.571141  1        1.603478
## mean_Denpob   1.574454  1        1.254772
## mean_Denemp   1.927230  1        1.388247
## Tipo          2.014305  7        1.051292
## Ancho         1.897708  1        1.377573
## Carriles      1.824602  1        1.350778
## Velocidad     1.581749  1        1.257676
## Pres_anden    1.326337  1        1.151667
## N_Arboles     1.063566  1        1.031293
## N_Semaforo    1.110772  1        1.053932
## RUTAS_SITP    1.560169  1        1.249067
## CICLORRUTA    1.188641  1        1.090248

El diagnóstico VIF muestra valores menores a 5 para todas las variables. Esto indica ausencia de multicolinealidad preocupante. Las variables pueden mantenerse en el modelo sin ajustes adicionales.

8. Predicciones y validación del modelo

Evaluamos la capacidad predictiva del modelo en la base de test:

test$predicciones_prob <- predict(modelo_logit, newdata = test, type = "response")
test$predicciones_bin <- ifelse(test$predicciones_prob > 0.5, 1, 0)

# Matriz de confusión
confusionMatrix(as.factor(test$predicciones_bin), test$HAY_SINIESTROS, positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction     0     1
##          0 26531  2140
##          1   133   205
##                                           
##                Accuracy : 0.9216          
##                  95% CI : (0.9185, 0.9247)
##     No Information Rate : 0.9192          
##     P-Value [Acc > NIR] : 0.06126         
##                                           
##                   Kappa : 0.1352          
##                                           
##  Mcnemar's Test P-Value : < 2e-16         
##                                           
##             Sensitivity : 0.087420        
##             Specificity : 0.995012        
##          Pos Pred Value : 0.606509        
##          Neg Pred Value : 0.925360        
##              Prevalence : 0.080837        
##          Detection Rate : 0.007067        
##    Detection Prevalence : 0.011652        
##       Balanced Accuracy : 0.541216        
##                                           
##        'Positive' Class : 1               
## 
  • Precisión del modelo:
    La precisión global (accuracy) es alta (92.16%), pero no es necesariamente indicativa de buen rendimiento debido al fuerte desbalance en los datos (pocos casos positivos).
  • Balanced Accuracy (más robusta en casos desbalanceados): fue solo de 54.12%, revelando dificultades del modelo en predecir correctamente ambos grupos por igual.
  • Sensibilidad baja (solo 0.7% detectados correctamente) indica que el modelo no es eficiente identificando tramos específicos donde sí ocurren siniestros.
  • Especificidad alta (muy alta), indica que predice muy bien cuando NO ocurren siniestros.
  • Precisión alta (60.65%) significa que cuando predice un siniestro, generalmente acierta.
  • Exactitud general alta (92.16%) pero principalmente por alta prevalencia del valor “no siniestro” en los datos. La precisión del modelo puede ser engañosa si no se considera esta distribución asimétrica.

9. Modelo Random Forest

El modelo Random Forest es un método de aprendizaje de máquina basado en árboles de decisión. Es útil cuando hay relaciones no lineales en los datos y cuando se busca una alta precisión predictiva. El modelo Random Forest es un algoritmo de aprendizaje supervisado que se basa en la construcción de múltiples árboles de decisión y la combinación de sus predicciones para mejorar la precisión y reducir el sobreajuste.

set.seed(123)  # Asegurar reproducibilidad

rf_model <- randomForest(
  HAY_SINIESTROS ~ TIPOMALLAE + AASHTO_BUE + ESTRATO_First + mean_V_REF + 
                    mean_Denpob + mean_Denemp + Tipo + Ancho + Carriles + 
                    Velocidad + Pres_anden + N_Arboles + N_Semaforo + 
                    RUTAS_SITP + CICLORRUTA,
  data       = vias_modelo,
  ntree      = 500,
  importance = TRUE,
  na.action  = na.omit
)

print(rf_model)
## 
## Call:
##  randomForest(formula = HAY_SINIESTROS ~ TIPOMALLAE + AASHTO_BUE +      ESTRATO_First + mean_V_REF + mean_Denpob + mean_Denemp +      Tipo + Ancho + Carriles + Velocidad + Pres_anden + N_Arboles +      N_Semaforo + RUTAS_SITP + CICLORRUTA, data = vias_modelo,      ntree = 500, importance = TRUE, na.action = na.omit) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: 7.64%
## Confusion matrix:
##       0    1 class.error
## 0 87813 1068  0.01201607
## 1  6318 1501  0.80803172
varImpPlot(rf_model)  # Importancia de las variables

10. Análisis exploratorio

# Supongamos que estas son tus variables significativas (continuas y categóricas):
cont_vars <- c("Ancho", "Velocidad", "RUTAS_SITP")  # Ejemplo
cat_vars  <- c("TIPOMALLAE", "AASHTO_BUE", "ESTRATO_First", "CICLORRUTA") # Ejemplo

# 1) Para variables continuas: Boxplot o violin plot por valor de HAY_SINIESTROS
for (var in cont_vars) {
  p <- ggplot(vias_modelo, aes_string(x = "HAY_SINIESTROS", y = var, fill = "HAY_SINIESTROS")) +
    geom_boxplot() +
    labs(title = paste("Distribución de", var, "según HAY_SINIESTROS")) +
    theme_minimal()
  print(p)
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

# 2) Para variables categóricas: Barras agrupadas o barras apiladas
for (var in cat_vars) {
  p <- ggplot(vias_modelo, aes_string(x = var, fill = "HAY_SINIESTROS")) +
    geom_bar(position = "fill") +  # 'fill' para ver proporciones
    labs(y = "Proporción", title = paste("Distribución de HAY_SINIESTROS por", var)) +
    theme_minimal()
  print(p)
}

df_significativas <- vias_modelo %>%
  dplyr::select(
    HAY_SINIESTROS, 
    TIPOMALLAE, 
    AASHTO_BUE, 
    ESTRATO_First, 
    Ancho, 
    Velocidad, 
    RUTAS_SITP, 
    CICLORRUTA
  )

ggpairs(
  df_significativas,
  aes(color = HAY_SINIESTROS), 
  upper = list(continuous = "points", combo = "box", discrete = "ratio"),
  lower = list(continuous = "smooth", combo = "facetdensity", discrete = "facetbar")
)

ggplot(vias_modelo, aes(x = Velocidad, fill = HAY_SINIESTROS)) +
  geom_histogram(bins = 30, alpha = 0.7) +
  facet_wrap(~ HAY_SINIESTROS, scales = "free_y") +
  labs(title = "Distribución de Velocidad según Siniestros") +
  theme_minimal()

ggplot(vias_modelo, aes(x = mean_Denpob, fill = HAY_SINIESTROS)) +
  geom_histogram(bins = 30, alpha = 0.7) +
  facet_wrap(~ HAY_SINIESTROS, scales = "free_y") +
  labs(title = "Distribución de densidad poblacional según Siniestros") +
  theme_minimal()

ggplot(vias_modelo, aes(x = mean_Denemp, fill = HAY_SINIESTROS)) +
  geom_histogram(bins = 30, alpha = 0.7) +
  facet_wrap(~ HAY_SINIESTROS, scales = "free_y") +
  labs(title = "Distribución de densidad de empleos generados según Siniestros") +
  theme_minimal()

11. Interpretación

El modelo logit actual revela factores clave en la ocurrencia de siniestros en Bogotá, con fuerte influencia del tipo de vía, infraestructura ciclística y características socioeconómicas del entorno. La calidad del modelo es aceptable para entender patrones globales, aunque es mejorable si se buscan predicciones más precisas sobre siniestros individuales. Se recomienda considerar métodos alternativos como Random Forest o modelos espaciales para complementar análisis predictivos futuros.

  • El modelo es bueno para identificar factores clave (tipo de vía, velocidad, ciclorruta, semáforos) que afectan significativamente la ocurrencia de siniestros viales.
  • El modelo general presenta buena especificidad y precisión global alta, aunque limitada en la detección efectiva de siniestros individuales debido a la baja sensibilidad.