1 Introducción

Este informe unificado presenta un análisis estadístico descriptivo de los comparendos de tránsito registrados en la ciudad de Barranquilla durante los meses de abril y mayo de 2010, a partir de una base de datos captada principalmente por cámaras de fotodetección.

Mediante el uso del lenguaje de programación R, se aplicaron funciones estadísticas básicas, técnicas de transformación de datos y visualizaciones gráficas para examinar características clave de los comparendos, como la distribución diaria de infracciones, el género del infractor, el tipo de vehículo involucrado, las infracciones más comunes y los recaudos generados por tipo de falta.

Este análisis permite identificar patrones de comportamiento vial, evaluar dinámicas operativas y demográficas de las infracciones, y aportar insumos útiles para la toma de decisiones orientadas a la gestión del tránsito y la seguridad vial en la ciudad.

1.1 Carga y exploración de los datos

# Leer archivo Excel (asegúrate de tenerlo en tu directorio de trabajo)
df <- read_excel("BD Comparendos SENA TAD-8.xlsx", sheet = "BD_Comparendos")

# Ver nombres de columnas
colnames(df)
##  [1] "No. MANDAMIENTO DE PAGO"   "FECHA MANDAMIENTO DE PAGO"
##  [3] "EJECUTADO"                 "TIPO DE IDENTIFICACION"   
##  [5] "SUMA_IDENTIFICACION"       "No. IDENTIFICACION"       
##  [7] "GENERO"                    "SUMA_GENERO"              
##  [9] "COD. INFRACCION"           "NOM. INFRACCION"          
## [11] "SUMA_NOM_INFRACCION"       "VALOR_COMPARENDO"         
## [13] "COMPARENDO"                "FECHA_COMPARENDO"         
## [15] "DIA"                       "NOM_DIA"                  
## [17] "MES"                       "NOM_MES"                  
## [19] "AÑO"                       "PLACA DE VEHICULO"        
## [21] "TIPO_VECHICULO"            "SUMA_TIPO_VEHICULO"

La carga de datos es el primer paso fundamental en cualquier proceso de análisis. Consiste en importar la información desde una fuente externa (en este caso, un archivo Excel) hacia el entorno de trabajo, lo cual permite su posterior manipulación y análisis. En este proyecto, se utilizó la función read_excel() del paquete readxl para leer la hoja llamada "BD_Comparendos" del archivo "BD Comparendos SENA TAD-8.xlsx".

Una vez cargados los datos, se realizó una selección de columnas relevantes mediante la función select() del paquete dplyr. Esta acción permite enfocar el análisis solo en las variables de interés, eliminando aquellas que no aportan valor al estudio.

Posteriormente, se aplicó una transformación a la variable FECHA_COMPARENDO usando mutate() para convertirla al formato de fecha (Date), lo que es esencial para realizar análisis temporales más precisos.

La exploración de los datos consiste en examinar su estructura, contenido y calidad. Esta etapa permite identificar aspectos clave como:

  • El tipo de variables (categóricas, numéricas, fechas).

  • La presencia de valores atípicos o faltantes.

  • La distribución de los datos.

  • Posibles errores o inconsistencias.

Esta exploración inicial es crítica para orientar decisiones posteriores como la limpieza, transformación y visualización de la información.

2 Interpretación del Valor de Comparendos

summary(df$VALOR_COMPARENDO)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  257490  257490  257490  304737  257490  514980
  • El valor más común de los comparendos es 257,490 pesos, que corresponde a una sanción base en ese periodo (probablemente equivalente a 15 SMDLV).

  • La mediana y ambos cuartiles son iguales a 257,490, lo que indica que más del 75% de los comparendos tienen ese mismo valor.

  • La media es 304,737, ligeramente superior, lo que sugiere que hay valores más altos (outliers) que elevan el promedio.

  • El valor máximo registrado es 514,980 pesos, que equivale al doble del valor más común. Esto puede corresponder a infracciones graves como:

    • Transitar en sentido contrario.

    • No detenerse ante luz roja.

    • No respetar paso peatonal.

3 Comparendos por Día

## 3. Comparendos por Día

df %>%
  count(FECHA_COMPARENDO) %>%
  ggplot(aes(x = FECHA_COMPARENDO, y = n)) +
  geom_line(color = "steelblue") +
  labs(title = "Comparendos por Día", x = "Fecha", y = "Cantidad de Comparendos")

La variabilidad diaria sugiere que el comportamiento infractor está fuertemente influenciado por factores exógenos y temporales. No se observa una distribución uniforme, lo que refuerza la necesidad de incorporar variables como clima, eventos locales o actividad económica para enriquecer los modelos predictivos.

El análisis de outliers (días con picos extremos) podría utilizarse como trigger para auditorías forenses: por ejemplo, si un día tiene un aumento inusual, es probable que haya un fallo técnico, un operativo o incluso un fenómeno social subyacente.

4 Comparendos por Género o Entidad

## 4. Distribución por Género o Entidad

df %>%
  count(GENERO) %>%
  ggplot(aes(x = reorder(GENERO, -n), y = n, fill = GENERO)) +
  geom_col() +
  labs(title = "Comparendos por Género o Entidad", x = "Género / Empresa", y = "Cantidad") +
  theme(legend.position = "none")

Desde un enfoque de análisis demográfico aplicado, el sesgo hacia el género masculino es consistente con estudios globales de tránsito: los hombres, especialmente jóvenes, muestran mayor inclinación a conductas de riesgo. La baja participación de entidades jurídicas puede ser una oportunidad para auditar empresas de transporte formal (taxis, buses, logística), que podrían estar ocultando infracciones en la dispersión de sus flotas.

Sugerimos construir un índice de reincidencia por entidad, para discriminar entre actores ocasionales y estructuralmente infractores.

5 Recaudo por Tipo de Infracción

## 5. Recaudo por Tipo de Infracción

df %>%
  group_by(`NOM. INFRACCION`) %>%
  summarise(Total = sum(VALOR_COMPARENDO, na.rm = TRUE)) %>%
  arrange(desc(Total)) %>%
  top_n(10, Total) %>%
  ggplot(aes(x = reorder(`NOM. INFRACCION`, Total), y = Total)) +
  geom_col(fill = "darkred") +
  coord_flip() +
  labs(title = "Infracciones del Mayor al menor Recaudo", x = "Infracción", y = "Valor Total ($)") +
  scale_y_continuous(labels = comma)

Este comportamiento responde a una ley de poder (power law): pocas infracciones concentran la mayoría del recaudo. Este hallazgo es consistente con el principio de Pareto (80/20) y justifica una política de intervención basada en evidencia: si 3 tipos de infracción explican el 70% del recaudo, entonces deben ser el foco de intervención operativa, normativa y educativa.

A través de una matriz de severidad vs. frecuencia, se pueden categorizar las infracciones como:

  • Frecuentes y graves (prioridad máxima)

  • Graves pero infrecuentes (seguimiento)

  • Frecuentes pero leves (educación masiva)

6 Comparendos por Tipo de Vehículo

## 6. Comparendos por Tipo de Vehículo

df %>%
  count(TIPO_VECHICULO) %>%
  ggplot(aes(x = reorder(TIPO_VECHICULO, -n), y = n, fill = TIPO_VECHICULO)) +
  geom_col() +
  labs(title = "Comparendos por Tipo de Vehículo", x = "Tipo de Vehículo", y = "Cantidad") +
  theme(legend.position = "none")

Aquí se evidencia un patrón de riesgo diferencial. Las motocicletas cometen más infracciones, pero de menor impacto económico. Por tanto, representan una externalidad colectiva, no por el valor individual de cada infracción, sino por su volumen agregado.

En contraste, vehículos de carga, con menor frecuencia de infracción, tienen un mayor impacto económico, lo cual puede estar vinculado a una mayor severidad o reincidencia acumulada. Este análisis justifica un modelo de riesgo vehicular, en el que cada tipo de vehículo tenga una tasa base de vigilancia proporcional a su incidencia y daño potencial.

7 Distribución de comparendos por día de la semana

## 7. Comparendos por Día de la Semana

df %>%
  count(NOM_DIA) %>%
  mutate(NOM_DIA = factor(NOM_DIA, 
                          levels = c("lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"))) %>%
  ggplot(aes(x = NOM_DIA, y = n, fill = NOM_DIA)) +
  geom_col() +
  labs(title = "Comparendos por Día de la Semana", x = "Día", y = "Cantidad de Comparendos") +
  theme_minimal() +
  theme(legend.position = "none")

El comportamiento semanal exhibe un patrón bimodal clásico, donde lunes y viernes concentran los máximos. Esto es coherente con el estrés de entrada y salida laboral, combinado con congestión. El patrón sugiere que los días de menor infracción no necesariamente son los de menor riesgo, sino que podría haber subregistro por menor fiscalización activa o menor tráfico general.

8 Recaudo total por tipo de vehículo

## 8. Recaudo por Tipo de Vehículo

df %>%
  group_by(TIPO_VECHICULO) %>%
  summarise(Recaudo = sum(VALOR_COMPARENDO, na.rm = TRUE)) %>%
  arrange(desc(Recaudo)) %>%
  ggplot(aes(x = reorder(TIPO_VECHICULO, Recaudo), y = Recaudo)) +
  geom_col(fill = "darkgreen") +
  coord_flip() +
  labs(title = "Recaudo Total por Tipo de Vehículo", x = "Tipo de Vehículo", y = "Valor Total ($)") +
  scale_y_continuous(labels = scales::comma)

Este indicador permite inferir quién le cuesta más a la ciudad en términos económicos de infracción. Los automóviles y vehículos de carga, aunque menos frecuentes en infracción que las motocicletas, generan más recaudo por monto, lo que sugiere un perfil de infracción más grave o más acumulativa.

Se recomienda aplicar análisis de cohortes: por ejemplo, comparar el comportamiento infractor de vehículos nuevos vs. antiguos, o de diferentes empresas, para entender mejor la fuente del daño económico.

9 Modelo Predictivo – Regresión logística

## 9. Modelo Predictivo: Probabilidad de Multa Alta

# Crear variable binaria: 1 si la multa fue mayor a la mediana
df <- df %>%
  mutate(MULTA_ALTA = ifelse(VALOR_COMPARENDO > median(VALOR_COMPARENDO, na.rm = TRUE), 1, 0))

# Convertir variables categóricas en factores
df_modelo <- df %>%
  select(MULTA_ALTA, GENERO, TIPO_VECHICULO, `COD. INFRACCION`) %>%
  mutate(across(where(is.character), as.factor)) %>%
  na.omit()

# Ajustar modelo
modelo <- glm(MULTA_ALTA ~ GENERO + TIPO_VECHICULO + `COD. INFRACCION`,
              data = df_modelo, family = "binomial")

summary(modelo)
## 
## Call:
## glm(formula = MULTA_ALTA ~ GENERO + TIPO_VECHICULO + `COD. INFRACCION`, 
##     family = "binomial", data = df_modelo)
## 
## Coefficients:
##                      Estimate Std. Error z value Pr(>|z|)
## (Intercept)         -354.0736 96151.2420  -0.004    0.997
## GENEROHOMBRE           0.7188 15766.9864   0.000    1.000
## GENEROMUJER            0.1499 15338.6545   0.000    1.000
## TIPO_VECHICULOMOTO    11.1839 32712.0174   0.000    1.000
## `COD. INFRACCION`      4.9323  1370.1784   0.004    0.997
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 2.021e+03  on 2119  degrees of freedom
## Residual deviance: 2.187e-08  on 2115  degrees of freedom
## AIC: 10
## 
## Number of Fisher Scoring iterations: 25

La regresión logística fue correctamente estructurada pero pobre en términos de ajuste. Su debilidad radica en:

  • Escasa cantidad o calidad de variables predictoras.

  • Falta de codificación avanzada (variables dummies, interacciones).

  • No linealidades no captadas por el modelo.

La regresión logística podría reemplazarse por modelos más robustos ante datos categóricos y no lineales, como XGBoost, Random Forest o Regresión de Poisson (si se modela cantidad de infracciones). Además, sería clave introducir variables como:

  • Ubicación geográfica (zona caliente de infracción)

  • Hora del día

  • Edad del conductor

10 Segmentación con Clustering (k-means)

## 10. Clustering de Infractores

# Crear dataset resumido por infractor
df_cluster <- df %>%
  group_by(`No. IDENTIFICACION`) %>%
  summarise(
    frecuencia = n(),
    valor_total = sum(VALOR_COMPARENDO, na.rm = TRUE)
  ) %>%
  na.omit()

# Normalizar
df_scaled <- scale(df_cluster[, -1])

# Clustering (3 grupos por ejemplo)
set.seed(123)
km <- kmeans(df_scaled, centers = 3)

# Agregar resultados al dataset original
df_cluster$grupo <- as.factor(km$cluster)

# Visualizar
ggplot(df_cluster, aes(x = frecuencia, y = valor_total, color = grupo)) +
  geom_point(size = 3) +
  labs(title = "Segmentación de Infractores por Clustering", x = "Frecuencia de Infracciones", y = "Valor Total ($)") +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal()

K-means permitió generar arquetipos de infractores. Aunque útil, este algoritmo tiene límites al trabajar con datos categóricos y valores extremos. Aun así, los clústeres revelan patrones útiles:

  • Clúster 1: Conductores reincidentes de bajo valor (motores informales, reparto urbano).

  • Clúster 2: Conductores únicos o poco frecuentes (público general).

  • Clúster 3: Entidades o individuos con muchas infracciones y alto valor económico (probablemente flotas empresariales o transporte público).

Un análisis de segmentación supervisada, como CART o árboles de decisión, podría complementar esta visión y permitir reglas claras para definir perfiles de riesgo.