Una mirada a las infracciones de tránsito, su frecuencia, impacto económico y comportamiento en el tiempo.
Objetivo
Este informe aplica herramientas estadísticas básicas en R al análisis de los comparendos de tránsito impuestos en Barranquilla entre el 12/04/2010 y el 27/05/2010. Se exploran, describen y visualizan los datos para identificar patrones, tendencias y relaciones relevantes, mediante medidas de frecuencia, concentración y evolución en el tiempo, que apoyen la toma de decisiones en la gestión del tránsito.
Pasa el cursor sobre cualquier gráfico para leer sus datos exactos. Cada gráfico se dibuja al llegar a él, y el botón «↻ Repetir» lo anima de nuevo. El botón «Ocultar código» (arriba a la derecha) muestra u oculta el código en R de todo el informe.
Indicadores
La base tiene buena integridad (0 valores vacíos), pero se detectaron 1 fila duplicada exacta y 17 números de comparendo repetidos con distinto mandamiento. Fueron depurados o señalados para garantizar la calidad del análisis.
Descripción general
La base reúne comparendos aplicados a conductores y propietarios de vehículos en Barranquilla (Atlántico). Cada registro incluye datos del infractor (nombre, género, tipo y número de identificación), del vehículo (placa y tipo), de la infracción (código y nombre), la fecha del comparendo desglosada en día, mes y año, y el valor a pagar. En total tiene 25 columnas y 2.120 registros originales (2.119 tras la depuración), lo que permite un análisis estadístico descriptivo sólido.
Origen y estructura
La información proviene de la base de comparendos de tránsito con 25 variables originales sobre el infractor, la infracción, la fecha, el vehículo y el valor económico del comparendo. A continuación se muestra el código con el que se leyó y depuró la base.
# Lectura y depuración de la base
raw <- read_excel(ruta, sheet = "BD Comparendos OK") # lee la hoja 'BD Comparendos OK' del Excel
n_raw <- nrow(raw) # guarda el número de filas originales
datos <- raw %>% select(-starts_with("#")) %>% distinct() %>% janitor::clean_names() # quita columnas que empiezan con '#', elimina filas duplicadas y estandariza nombres de columnas
n_dup <- n_raw - nrow(datos) # calcula cuántas filas duplicadas se eliminaron
datos <- datos %>% mutate( # crea y corrige columnas
ejecutado = ejecutado %>% str_squish() %>% str_replace_all("G\\.M\\.A\\.C\\.", "GMAC"), # propietario: quita espacios sobrantes y unifica 'G.M.A.C.' como 'GMAC'
tipo_id = recode(tipo_de_identificacion, "Cedula de Ciudadanía" = "Cédula de ciudadanía", # tipo de identificación: corrige las etiquetas
"Cedula de Extanjería" = "Cédula de extranjería", "Nit" = "NIT"), # (continúa) etiqueta de extranjería y NIT
infraccion = str_to_sentence(nom_infraccion) %>% str_replace("maxima", "máxima"), # infracción: texto en formato oración y corrige la tilde de 'máxima'
fecha = as.Date(fecha_de_comparendo), # fecha: convierte a tipo Date
smlv = costo_de_comparendo / smlv_2010, # valor del comparendo expresado en SMLV de 2010
dia = factor(str_to_title(nom_dia), levels = c("Lunes","Martes","Miércoles","Jueves","Viernes","Sábado","Domingo")), # día de la semana como factor ordenado (lunes a domingo)
mes = factor(str_to_title(nom_mes), levels = c("Abril", "Mayo")), # mes como factor ordenado (abril, mayo)
vehiculo = str_to_title(vehiculo)) # tipo de vehículo en formato título (Auto, Moto)
# Resumen de la base y variables principales
resumen <- tibble(Indicador = c("Registros (base depurada)", "Columnas originales", "Valores vacíos", "Filas duplicadas exactas", # tabla resumen: nombres de los indicadores
"Comparendos únicos", "Propietarios únicos", "Placas únicas", "Período"), # (continúa los indicadores)
Valor = c(fm(nrow(datos)), ncol(raw), sum(is.na(raw)), n_dup, fm(n_distinct(datos$comparendo)), # valores de cada indicador: registros, columnas, vacíos y duplicados
fm(n_distinct(datos$ejecutado)), fm(n_distinct(datos$placa_de_vehiculo)), paste(f_ini, "a", f_fin))) # (continúa: únicos y período)
vars <- c("Tipo de vehículo", "Infracción", "Fecha del comparendo", "Valor del comparendo (COP y SMLV)", "Tipo de identificación", "Propietario (ejecutado)", "Género") # lista de variables principales
salida('<div class="dos-col">', tabla_html(resumen), '<div class="vars" id="o2"><b>Variables principales</b><ul>', paste0("<li>", vars, "</li>", collapse = ""), '</ul></div></div>') # imprime en dos columnas la tabla resumen y la caja de variables
| Indicador | Valor |
|---|---|
| Registros (base depurada) | 2.119 |
| Columnas originales | 25 |
| Valores vacíos | 0 |
| Filas duplicadas exactas | 1 |
| Comparendos únicos | 2.102 |
| Propietarios únicos | 1.247 |
| Placas únicas | 1.457 |
| Período | 12/04/2010 a 27/05/2010 |
Se eliminó 1 fila duplicada, se unificaron nombres (p. ej. G.M.A.C. por GMAC), se corrigieron etiquetas y se convirtió el valor a SMLV de 2010 ($ 515.000). Cada comparendo vale 0,1, 0,5 o 1 SMLV.
Aplicación de funciones: colnames()
Lo primero es identificar los nombres de las columnas para poder llamarlas correctamente en el análisis.
# Nombres de las columnas originales
cat(paste0('<span class="tag">', str_squish(colnames(raw)), '</span>', collapse = "")) # imprime cada nombre de columna como una etiqueta
No. MANDAMIENTO DE PAGOFECHA MANDAMIENTO DE PAGOEJECUTADOTIPO DE IDENTIFICACIONNo. IDENTIFICACION#TIPO DE IDENTIFICACIONGENERO# GENEROCOD. INFRACCION# COD. INFRACCIONNOM.INFRACCION#NOM. INFRACCIONCOMPARENDOFECHA DE COMPARENDOAÑOMESNOM MES# NOM. MESDIANOM DIA# NOM. DiaPLACA DE VEHICULOVEHICULO#VEHICULOCOSTO DE COMPARENDO
Aplicación de funciones: resumen estadístico
Resumen general de la fecha y del valor a pagar, para revisar la distribución y los cuartiles.
# Resumen estadístico (mínimo, cuartiles, mediana, media y máximo)
fq <- as.numeric(datos$fecha); vq <- datos$costo_de_comparendo # fechas y valores como números para calcular cuartiles
est <- tibble(`Estadístico` = c("Mínimo", "1.er cuartil", "Mediana", "Media", "3.er cuartil", "Máximo"), # tabla de estadísticos: nombres de las filas
`Fecha del comparendo` = format(as.Date(unname(c(min(fq), quantile(fq, .25), median(fq), mean(fq), quantile(fq, .75), max(fq))), origin = "1970-01-01"), "%d/%m/%Y"), # mínimo, cuartiles, mediana, media y máximo de la fecha
`Valor del comparendo` = cop(unname(c(min(vq), quantile(vq, .25), median(vq), mean(vq), quantile(vq, .75), max(vq))))) # mínimo, cuartiles, mediana, media y máximo del valor
salida(tabla_html(est)) # imprime la tabla
| Estadístico | Fecha del comparendo | Valor del comparendo |
|---|---|---|
| Mínimo | 12/04/2010 | $ 51.500 |
| 1.er cuartil | 27/04/2010 | $ 257.500 |
| Mediana | 06/05/2010 | $ 257.500 |
| Media | 05/05/2010 | $ 303.775 |
| 3.er cuartil | 16/05/2010 | $ 257.500 |
| Máximo | 27/05/2010 | $ 515.000 |
3.1 Comparendos por día de la semana y tipo de vehículo
Qué muestra: cuántos comparendos se impusieron cada día de la semana, separados entre autos (azul) y motos (coral).
Cómo leerlo: cada columna es un día; su altura es el total de comparendos y el número en negrita sobre ella es ese total. La parte azul de la columna son los autos y la coral las motos.
Al pasar el cursor: al situarte sobre un día aparece el detalle de autos, motos y el total de ese día.
Para qué sirve: identificar los días con más infracciones para programar operativos de control en esos momentos.
# Barras apiladas por día (autos y motos) con el total encima
d_dia <- datos %>% count(dia, vehiculo) %>% # cuenta comparendos por día y vehículo
mutate(dia = as.character(dia), tip = paste0(vehiculo, ": ", fm(n), " comparendos")) # convierte el día a texto y arma el texto del hover
tot_dia <- datos %>% count(dia) %>% # total de comparendos por día
mutate(dia = as.character(dia), tip = paste0("<b>Total: ", fm(n), " comparendos</b>")) # texto del hover con el total
p <- plot_ly(height = 460) # crea el gráfico plotly vacío (alto 460 px)
for (veh in names(col_veh)) { # para cada tipo de vehículo...
p <- add_bars(p, data = filter(d_dia, vehiculo == veh), x = ~dia, y = ~n, name = veh, # agrega una serie de barras con su color
marker = list(color = col_veh[[veh]]), hoverinfo = "text", hovertext = ~tip) # (continúa: color y hover)
} # fin del for
p <- p %>% # continúa el gráfico con %>%
add_trace(data = tot_dia, x = ~dia, y = ~n, type = "scatter", mode = "text", text = ~fm(n), # agrega capa de texto con el total sobre cada columna
textposition = "top center", textfont = list(size = 15, color = pal$marino), # (posición y estilo del texto)
showlegend = FALSE, hoverinfo = "text", hovertext = ~tip) %>% # (sin leyenda; hover propio)
layout(barmode = "stack", hovermode = "x unified", # barras apiladas y hover unificado por día
xaxis = eje_cat(levels(datos$dia)), yaxis = eje_val(max(tot_dia$n) * 1.18)) %>% # (continúa: ejes con días en orden y rango del eje vertical)
pl("Comparendos por día de la semana") # aplica el estilo común y el título
graf(p) # muestra el gráfico dentro de la tarjeta
Sábado y domingo concentran el 36,2% de los comparendos; el día con más registros es el domingo (396). Los autos representan el 91,4% del total.
3.2 Evolución diaria de los comparendos
La variación diaria permite detectar días atípicos, eventos especiales o aumentos asociados a operativos específicos, clima, festivos o tráfico inusual. El patrón temporal sirve para prever puntos críticos y ajustar los operativos de tránsito.
Qué muestra: el número de comparendos de cada fecha entre abril y mayo, como una línea con el área sombreada debajo.
Cómo leerlo: el eje horizontal son las fechas (una marca por semana) y el vertical la cantidad de comparendos de ese día. Los tres puntos coral con número son los días con más registros.
Al pasar el cursor: sobre cualquier punto de la línea aparecen la fecha, el día de la semana y los comparendos de ese día.
Para qué sirve: ver en qué fechas se disparan las infracciones y reconocer días atípicos, como operativos, festivos o puentes.
# Línea con área y los 3 días pico resaltados
diario <- datos %>% count(fecha, dia) %>% # comparendos por fecha y día de la semana
mutate(tip = paste0("<b>", dia, " ", format(fecha, "%d/%m/%Y"), "</b><br>", fm(n), " comparendos")) # texto del hover
picos <- diario %>% slice_max(n, n = 3, with_ties = FALSE) # los 3 días con más comparendos
p <- plot_ly(height = 440) %>% # inicia el gráfico plotly
add_trace(data = diario, x = ~fecha, y = ~n, type = "scatter", mode = "lines+markers", # línea con marcadores y área sombreada
fill = "tozeroy", fillcolor = "rgba(20,184,166,.20)", name = "Comparendos", # relleno hasta cero, color y nombre
line = list(color = pal$turquesa, width = 3), marker = list(color = pal$turquesa, size = 6), # estilo de línea y marcadores
hoverinfo = "text", hovertext = ~tip) %>% # hover
add_trace(data = picos, x = ~fecha, y = ~n, type = "scatter", mode = "markers+text", name = "Días pico", # capa de los días pico (puntos coral con número)
text = ~n, textposition = "top center", textfont = list(color = pal$coral, size = 15), # texto sobre los puntos
marker = list(color = pal$coral, size = 13, line = list(color = "white", width = 2)), # estilo del marcador con borde blanco
hoverinfo = "text", hovertext = ~tip) %>% # hover
layout(xaxis = list(title = "", type = "date", tickformat = "%d/%m", dtick = 7 * 24 * 3600 * 1000, # eje x como fecha con una marca por semana
tick0 = as.character(min(diario$fecha)), gridcolor = "#E5EAF2", fixedrange = TRUE), # (continúa: inicio de la marca semanal y cuadrícula)
yaxis = eje_val(max(diario$n) * 1.25)) %>% # eje y con rango hasta 1,25 × el máximo
pl("Comparendos por fecha de infracción") # estilo y título
graf(p) # muestra el gráfico
3.3 Comparendos por género o tipo de titular
Se cuenta la cantidad de comparendos según el género del infractor o si el titular es una empresa.
Qué muestra: cuántos comparendos corresponden a mujeres, hombres, empresas y cédulas nuevas.
Cómo leerlo: cada columna es una categoría; encima aparece la cantidad de comparendos y, entre paréntesis, el porcentaje que representa del total. Las columnas van de la más alta a la más baja.
Al pasar el cursor: sobre una columna aparecen la categoría, la cantidad y su porcentaje del total.
Para qué sirve: saber quién concentra las infracciones y si conviene diferenciar las estrategias entre personas naturales y empresas.
# Barras por género con cantidad y porcentaje
gen <- datos %>% count(genero = str_to_title(genero)) %>% mutate(p = n / sum(n)) %>% arrange(desc(n)) %>% # cuenta comparendos por género y calcula la proporción; ordena de mayor a menor
mutate(txt = paste0(fm(n), "<br>(", pct(p), ")"), # texto sobre la barra: cantidad y porcentaje
tip = paste0("<b>", genero, "</b><br>", fm(n), " comparendos<br>", pct(p), " del total"), # texto del hover
col = unname(col_gen[genero])) # color de cada categoría
p <- plot_ly(gen, x = ~genero, y = ~n, type = "bar", marker = list(color = gen$col), # gráfico de barras por género
text = ~txt, textposition = "outside", cliponaxis = FALSE, # texto exterior sobre las barras
textfont = list(size = 15, color = pal$marino), # tamaño y color del texto
hoverinfo = "text", hovertext = ~tip, height = 440) %>% # hover y alto
layout(xaxis = eje_cat(gen$genero), yaxis = eje_val(max(gen$n) * 1.3)) %>% # ejes: categorías ordenadas y rango del eje y
pl("Distribución de comparendos por género o empresa", leyenda = FALSE) # estilo y título (sin leyenda)
graf(p) # muestra el gráfico
La categoría con más comparendos es «Mujer» (45,7%). Las empresas aportan el 22,0% de los comparendos, lo que indica la necesidad de fortalecer el control sobre las flotas vehiculares corporativas y de diferenciar las estrategias entre personas naturales y jurídicas.
3.4 Valor económico por tipo de infracción
Se suman los valores de los comparendos agrupados por la infracción cometida. Es el valor impuesto, no el recaudo efectivo, porque la base no trae fechas de pago.
Qué muestra: cuánto dinero suman los comparendos de cada tipo de infracción.
Cómo leerlo: cada barra horizontal es una infracción y su longitud es el valor total en pesos. A la derecha se ve el monto y, entre paréntesis, el porcentaje del valor total. Van ordenadas de mayor a menor.
Al pasar el cursor: sobre una barra aparecen el nombre completo de la infracción, su valor, su participación y el número de comparendos.
Para qué sirve: priorizar las infracciones que más pesan en el valor total, que no siempre son las más frecuentes.
# Barras horizontales del valor económico por infracción
inf_v <- datos %>% group_by(infraccion) %>% summarise(v = sum(costo_de_comparendo), n = n(), .groups = "drop") %>% # valor y número de comparendos por infracción
arrange(desc(v)) %>% mutate(p = v / sum(v)) # ordena de mayor a menor y calcula el porcentaje del valor total
inf_g <- inf_v %>% arrange(v) %>% # ordena de menor a mayor para que la mayor quede arriba
mutate(et = wrapbr(infraccion, 28), txt = paste0(cop(v), " (", pct(p), ")"), # etiqueta con saltos de línea, texto de la barra y hover
tip = paste0("<b>", infraccion, "</b><br>Valor: ", cop(v), "<br>Participación: ", pct(p), "<br>Comparendos: ", fm(n))) # (continúa el hover)
p <- plot_ly(inf_g, x = ~v, y = ~et, type = "bar", orientation = "h", marker = list(color = pal$coral), # barras horizontales del valor por infracción
text = ~txt, textposition = "outside", cliponaxis = FALSE, # texto exterior en las barras
textfont = list(size = 14, color = pal$marino), # tamaño y color del texto
hoverinfo = "text", hovertext = ~tip, height = 430) %>% # hover y alto
layout(xaxis = eje_val(max(inf_g$v) * 1.65, showticklabels = FALSE, showgrid = FALSE), # eje x sin números ni cuadrícula, con espacio para el texto
yaxis = eje_cat(inf_g$et)) %>% # eje y con las infracciones en orden
pl("Valor económico de los comparendos por infracción", leyenda = FALSE) # estilo y título
graf(p) # muestra el gráfico
«Velocidad superior a la máxima permitida» aporta $ 418.695.000 (65,0%), lo que indica alta frecuencia y sanción elevada. Le sigue «No detenerse ante una luz roja o amarilla» con $ 197.760.000. Entre las dos suman el 95,8% del valor total; «Transitar en sentido contrario» tiene un valor mínimo, por su baja ocurrencia o menor detección.
3.5 Comparendos por mes
Se agrupan los comparendos por el mes en que ocurrieron para revisar la variación mensual.
Qué muestra: el total de comparendos de abril y de mayo.
Cómo leerlo: la altura de cada columna es el total del mes; encima aparece ese total y el promedio de comparendos por día. Abril empieza el 12/04, así que tiene menos días y conviene comparar con el promedio diario, no con el total.
Al pasar el cursor: sobre una columna aparecen el total, los días con registros, el promedio diario y el valor del mes.
Para qué sirve: comparar los dos meses de forma justa y ver si la actividad aumentó o bajó.
# Barras de comparendos por mes con promedio diario
mes_t <- datos %>% group_by(mes) %>% summarise(n = n(), dias = n_distinct(fecha), v = sum(costo_de_comparendo), .groups = "drop") %>% # por mes: comparendos, días con registros y valor
mutate(prom = n / dias, mes = as.character(mes), # promedio diario y mes como texto
txt = paste0("<b>", fm(n), " comparendos</b><br>", format(round(prom, 1), decimal.mark = ","), " por día"), # texto sobre la barra
tip = paste0("<b>", mes, "</b><br>", fm(n), " comparendos<br>", dias, " días con registros<br>", # texto del hover
format(round(prom, 1), decimal.mark = ","), " por día<br>Valor: ", cop(v))) # (continúa el hover)
p <- plot_ly(mes_t, x = ~mes, y = ~n, type = "bar", # gráfico de barras por mes
marker = list(color = unname(c(Abril = pal$azul, Mayo = pal$turquesa)[mes_t$mes])), # color de cada mes
text = ~txt, textposition = "outside", cliponaxis = FALSE, textfont = list(size = 15, color = pal$marino), # texto exterior
hoverinfo = "text", hovertext = ~tip, height = 430) %>% # hover y alto
layout(xaxis = eje_cat(mes_t$mes), yaxis = eje_val(max(mes_t$n) * 1.3), bargap = 0.5) %>% # ejes y separación entre barras
pl("Comparendos por mes", leyenda = FALSE) # estilo y título
graf(p) # muestra el gráfico
Mayo registra 1.408 comparendos frente a 711 de abril (98% más). Pero abril solo cubre 19 días (desde el 12/04/2010) y mayo 27, así que la comparación justa es el promedio diario: 37,4 frente a 52,1 (39% más).
3.6 Personas naturales con más comparendos
Se identifican las 10 personas con más comparendos registrados, excluyendo empresas (se incluyen los empates en el último lugar).
Qué muestra: las personas naturales (sin empresas) con mayor número de comparendos acumulados.
Cómo leerlo: cada barra horizontal es una persona y el número a la derecha es su cantidad de comparendos. La de arriba es la que más acumula.
Al pasar el cursor: sobre una barra aparecen el nombre, la cantidad de comparendos y el valor acumulado.
Para qué sirve: reconocer a los reincidentes para enfocar campañas educativas y acciones sancionatorias en ellos.
# Top 10 de personas naturales por número de comparendos
pers_all <- datos %>% filter(str_to_upper(genero) != "EMPRESA") %>% # comparendos por propietario, sin empresas
group_by(ejecutado) %>% summarise(n = n(), v = sum(costo_de_comparendo), .groups = "drop") # (continúa: agrupa y suma)
pers <- pers_all %>% slice_max(n, n = 10, with_ties = TRUE) %>% arrange(n) %>% # top 10 (con empates) ordenado de menor a mayor
mutate(et = wrapbr(ejecutado, 45), # etiqueta con saltos de línea
tip = paste0("<b>", ejecutado, "</b><br>", n, " comparendos<br>Valor: ", cop(v))) # texto del hover
p <- plot_ly(pers, x = ~n, y = ~et, type = "bar", orientation = "h", marker = list(color = pal$violeta), # barras horizontales de las personas
text = ~n, textposition = "outside", cliponaxis = FALSE, textfont = list(size = 14, color = pal$marino), # texto exterior
hoverinfo = "text", hovertext = ~tip, height = 120 + 40 * nrow(pers)) %>% # hover y alto proporcional al número de barras
layout(xaxis = eje_val(max(pers$n) * 1.15, tickformat = "d"), yaxis = eje_cat(pers$et), bargap = 0.3) %>% # ejes y separación
pl("Top 10 de personas naturales con más comparendos", leyenda = FALSE) # estilo y título
graf(p) # muestra el gráfico
La persona natural con más comparendos acumula 27, frente a 10 de la siguiente. Hay 289 personas naturales con 2 o más comparendos. Identificar a los reincidentes permite focalizar intervenciones educativas y sancionatorias más precisas.
Consulta 1. Frecuencia de infracciones según el tipo de vehículo
Qué muestra: a la izquierda, qué porcentaje de los comparendos corresponde a autos y a motos; a la derecha, cuántos comparendos hay de cada infracción, separados por tipo de vehículo.
Cómo leerlo: en la dona, cada color es un tipo de vehículo, el porcentaje va dentro del anillo y el total de registros, en el centro. En las barras, la longitud es la cantidad de comparendos y los colores indican cuánto aporta cada vehículo.
Al pasar el cursor: en la dona aparecen el vehículo, la cantidad y el porcentaje; en las barras, la infracción completa y los comparendos de cada vehículo.
Para qué sirve: ver qué vehículo infringe más y en cuáles infracciones pesan más los autos o las motos.
# Dona de participación por vehículo + barras por infracción
q1 <- datos %>% count(vehiculo) %>% mutate(p = n / sum(n)) # comparendos y proporción por vehículo
p_dona <- plot_ly(q1, labels = ~vehiculo, values = ~n, type = "pie", hole = 0.58, sort = FALSE, # dona: etiquetas, valores, tipo pie y hueco central
direction = "clockwise", textinfo = "percent", # sentido horario y texto en porcentaje
textfont = list(color = "white", size = 17), # texto blanco
marker = list(colors = unname(col_veh[q1$vehiculo]), line = list(color = "white", width = 3)), # colores por vehículo con borde blanco
hovertemplate = "<b>%{label}</b><br>%{value:,.0f} comparendos<br>%{percent:.1%}<extra></extra>", # texto del hover
height = 460) %>% # alto del gráfico
layout(annotations = list(list(text = paste0("Total<br><b>", fm(sum(q1$n)), "</b>"), x = 0.5, y = 0.5, # texto central con el total
showarrow = FALSE, font = list(size = 18, color = pal$marino)))) %>% # (continúa: sin flecha, tamaño y color)
pl("Participación por vehículo", margen = list(t = 70, r = 10, b = 20, l = 10)) %>% # estilo y título
layout(legend = list(orientation = "h", x = 0.5, xanchor = "center", y = -0.02)) # leyenda horizontal centrada bajo la dona
q1b <- datos %>% count(infraccion, vehiculo) %>% # comparendos por infracción y vehículo
mutate(et = wrapbr(infraccion, 24), tip = paste0("<b>", infraccion, "</b><br>", vehiculo, ": ", fm(n), " comparendos")) # etiqueta con saltos de línea y texto del hover
orden1 <- q1b %>% group_by(infraccion, et) %>% summarise(t = sum(n), .groups = "drop") %>% arrange(t) %>% # total por infracción ordenado de menor a mayor
mutate(tip = paste0("<b>", infraccion, "</b><br>Total: ", fm(t), " comparendos")) # texto del hover con el total
p_barras <- plot_ly(height = 460) # gráfico vacío para las barras
for (veh in names(col_veh)) { # para cada vehículo...
p_barras <- add_bars(p_barras, data = filter(q1b, vehiculo == veh), x = ~n, y = ~et, orientation = "h", # agrega barras horizontales con su color
name = veh, marker = list(color = col_veh[[veh]]), hoverinfo = "text", hovertext = ~tip) # (continúa: nombre y hover)
} # fin del for
p_barras <- p_barras %>% # continúa el gráfico con %>%
add_trace(data = orden1, x = ~t, y = ~et, type = "scatter", mode = "text", text = ~fm(t), # agrega capa de texto con el total
textposition = "middle right", textfont = list(size = 14, color = pal$marino), # (posición y estilo)
showlegend = FALSE, hoverinfo = "text", hovertext = ~tip) %>% # (sin leyenda; con hover)
layout(barmode = "stack", xaxis = eje_val(max(orden1$t) * 1.15), yaxis = eje_cat(orden1$et)) %>% # barras apiladas y ejes
pl("Comparendos por infracción y vehículo") # estilo y título
htmltools::div(class = "dos-col", style = "grid-template-columns:1fr 1.5fr", graf(p_dona), graf(p_barras)) # muestra la dona y las barras lado a lado (1 : 1,5)
# Tabla de la consulta 1
t1 <- datos %>% group_by(infraccion) %>% # por infracción: autos, motos, total y valor
summarise(Auto = sum(vehiculo == "Auto"), Moto = sum(vehiculo == "Moto"), Total = n(), Valor = sum(costo_de_comparendo), .groups = "drop") %>% # (continúa)
arrange(desc(Total)) %>% # ordena por total descendente
mutate(`% registros` = pct(Total / sum(Total)), Valor = cop(Valor), across(c(Auto, Moto, Total), fm)) %>% # porcentaje de registros y formato del valor y los conteos
rename(`Infracción` = infraccion) # renombra la columna de infracción con tilde
salida(tabla_html(t1)) # imprime la tabla
| Infracción | Auto | Moto | Total | Valor | % registros |
|---|---|---|---|---|---|
| Velocidad superior a la máxima permitida | 1.467 | 159 | 1.626 | $ 418.695.000 | 76,7% |
| No detenerse ante una luz roja o amarilla | 363 | 21 | 384 | $ 197.760.000 | 18,1% |
| No respetar el paso de peatones | 105 | 0 | 105 | $ 27.037.500 | 5,0% |
| Transitar en sentido contrario | 2 | 2 | 4 | $ 206.000 | 0,2% |
Consulta 2. Distribución de comparendos por propietario y tipo de vehículo
Qué muestra: los 15 propietarios con más comparendos y qué parte de ellos corresponde a autos y a motos.
Cómo leerlo: cada barra horizontal es un propietario; el número al final es su total de comparendos. Los colores dividen la barra entre autos (azul) y motos (coral).
Al pasar el cursor: sobre cada tramo de color aparecen el nombre completo del propietario y los comparendos de ese vehículo.
Para qué sirve: ubicar a los propietarios que más comparendos acumulan, casi siempre entidades financieras o empresas, para priorizar la gestión de cobro.
# Top 15 propietarios, barras apiladas por vehículo
top15 <- prop %>% slice_max(n, n = 15, with_ties = FALSE) %>% arrange(n) %>% # los 15 propietarios con más comparendos, de menor a mayor
mutate(et = wrapbr(ejecutado, 30), tip = paste0("<b>", ejecutado, "</b><br>Total: ", n, " comparendos")) # (continúa: etiqueta y hover)
q2 <- datos %>% filter(ejecutado %in% top15$ejecutado) %>% count(ejecutado, vehiculo) %>% # comparendos por propietario y vehículo
mutate(et = wrapbr(ejecutado, 30), tip = paste0("<b>", ejecutado, "</b><br>", vehiculo, ": ", n, " comparendos")) # (continúa: etiqueta y hover)
p <- plot_ly(height = 600) # gráfico vacío de alto 600
for (veh in names(col_veh)) { # para cada vehículo...
p <- add_bars(p, data = filter(q2, vehiculo == veh), x = ~n, y = ~et, orientation = "h", # agrega barras horizontales
name = veh, marker = list(color = col_veh[[veh]]), hoverinfo = "text", hovertext = ~tip) # (continúa)
} # fin del for
p <- p %>% # continúa el gráfico con %>%
add_trace(data = top15, x = ~n, y = ~et, type = "scatter", mode = "text", text = ~n, # agrega capa de texto con el total
textposition = "middle right", textfont = list(size = 14, color = pal$marino), # (posición y estilo)
showlegend = FALSE, hoverinfo = "text", hovertext = ~tip) %>% # (sin leyenda; con hover)
layout(barmode = "stack", xaxis = eje_val(max(top15$n) * 1.12, tickformat = "d"), yaxis = eje_cat(top15$et)) %>% # barras apiladas y ejes
pl("Los 15 propietarios con más comparendos") # estilo y título
graf(p) # muestra el gráfico
Los propietarios con más comparendos son entidades financieras y empresas (leasing y transporte). Hay 1.247 propietarios distintos y 899 (72%) tienen un solo comparendo. En el dashboard esta consulta estaba limitada a domingos y propietarios con 3 o más comparendos (67 registros).
Consulta 3. Distribución porcentual del valor económico por propietario
Qué muestra: qué porcentaje del dinero total de los comparendos corresponde a los 6 propietarios con mayor valor y qué parte queda en «Otros propietarios».
Cómo leerlo: cada porción de la dona es un propietario y su tamaño es su participación en el valor total; el porcentaje está dentro de la porción y en la leyenda de la derecha. El centro muestra el valor total.
Al pasar el cursor: sobre una porción aparecen el propietario, su valor en pesos y su porcentaje. Si haces clic en la leyenda, ocultas o muestras esa porción.
Para qué sirve: medir qué tan concentrado o repartido está el valor: si «Otros propietarios» es la porción más grande, el valor está muy Fragmentado.
# Dona del valor económico: top 6 propietarios y el resto
q3 <- datos %>% group_by(ejecutado) %>% summarise(v = sum(costo_de_comparendo), .groups = "drop") %>% arrange(desc(v)) %>% # valor por propietario, de mayor a menor
mutate(grupo = if_else(row_number() <= 6, ejecutado, "Otros propietarios")) %>% # agrupa fuera del top 6 como 'Otros propietarios'
group_by(grupo) %>% summarise(v = sum(v), .groups = "drop") %>% # suma el valor por grupo
mutate(p = v / sum(v), orden = if_else(grupo == "Otros propietarios", 0, v)) %>% arrange(desc(orden)) %>% # porcentaje y orden (Otros primero)
mutate(et = str_trunc(grupo, 32)) # recorta nombres largos a 32 caracteres
colores3 <- c(pal$azul, pal$turquesa, pal$ambar, pal$coral, pal$violeta, pal$marino, pal$gris) # colores de las porciones
p <- plot_ly(q3, labels = ~et, values = ~v, type = "pie", hole = 0.58, sort = FALSE, # dona del valor por propietario
direction = "clockwise", textinfo = "percent", insidetextorientation = "horizontal", # sentido horario y porcentaje dentro de la porción
textfont = list(color = "white", size = 14), # texto blanco
marker = list(colors = colores3[seq_len(nrow(q3))], line = list(color = "white", width = 3)), # colores y borde blanco
hovertemplate = "<b>%{label}</b><br>$ %{value:,.0f}<br>%{percent:.1%}<extra></extra>", # hover con valor en pesos
height = 500) %>% # alto del gráfico
layout(annotations = list(list(text = paste0("Total<br><b>", cop(sum(q3$v)), "</b>"), x = 0.5, y = 0.5, # texto central con el valor total
showarrow = FALSE, font = list(size = 16, color = pal$marino)))) %>% # (continúa: tamaño y color)
pl("Valor económico por propietario", margen = list(t = 70, r = 10, b = 20, l = 10)) %>% # estilo y título
layout(legend = list(orientation = "v", x = 1.02, y = 0.5, font = list(size = 13))) # leyenda vertical a la derecha
graf(p) # muestra el gráfico
El valor económico está muy fragmentado: los 6 mayores propietarios suman solo el 10,5% del valor total.
Consulta 4. Top 10 de propietarios con más comparendos
Qué muestra: una tabla con los 10 propietarios que más comparendos acumulan, con su tipo de identificación, cantidad, valor y participación.
Cómo leerlo: cada fila es un propietario, ordenado del que más comparendos tiene al que menos. La columna «% del total» incluye una barrita cuyo largo es proporcional a su participación.
Al pasar el cursor: la fila bajo el cursor se resalta para seguirla con facilidad.
Para qué sirve: tener a la mano la lista concreta de propietarios a priorizar en la gestión de cobro.
# Top 10 de propietarios (conteo de comparendos, no suma de códigos)
q4 <- datos %>% group_by(ejecutado, tipo_id) %>% summarise(n = n(), v = sum(costo_de_comparendo), .groups = "drop") %>% # comparendos y valor por propietario y tipo de ID
arrange(desc(n)) %>% slice_head(n = 10) %>% mutate(p = n / nrow(datos)) # ordena y toma los 10 primeros, con su porcentaje
t4 <- q4 %>% transmute(`#` = row_number(), Propietario = ejecutado, `Tipo de ID` = tipo_id, Comparendos = n, # tabla final: número, propietario, tipo de ID y comparendos
Valor = cop(v), SMLV = fm(v / smlv_2010), # valor en pesos y en SMLV
`% del total` = paste0('<span class="mini" style="width:', as.integer(round(p / max(p) * 60)), 'px"></span>', pct(p))) # porcentaje con barrita proporcional
salida(tabla_html(t4)) # imprime la tabla
| # | Propietario | Tipo de ID | Comparendos | Valor | SMLV | % del total |
|---|---|---|---|---|---|---|
| 1 | LEASING BANCOLOMBIA S.A. C.F. | NIT | 61 | $ 19.827.500 | 38 | 2,9% |
| 2 | TRANSPORTES MST Y CIA S EN C | NIT | 48 | $ 13.905.000 | 27 | 2,3% |
| 3 | FINANCIERA INTERNACIONAL S.A. | NIT | 32 | $ 10.557.500 | 20 | 1,5% |
| 4 | LEASING DE OCCIDENTE S.A. C.F. | NIT | 28 | $ 9.527.500 | 18 | 1,3% |
| 5 | LILIANA ROZO PINZON | Cédula de ciudadanía | 27 | $ 7.467.500 | 14 | 1,3% |
| 6 | GMAC FINANCIERA DE COLOMBI | NIT | 21 | $ 6.180.000 | 12 | 1,0% |
| 7 | LEASING DE CREDITO S.A. | NIT | 21 | $ 5.922.500 | 12 | 1,0% |
| 8 | UINON FAMILIAR CHARRIS S.EN C | NIT | 13 | $ 4.120.000 | 8 | 0,6% |
| 9 | ELVIRA DEL CARMEN GARRIDO AREVALO | Cédula de ciudadanía | 10 | $ 2.575.000 | 5 | 0,5% |
| 10 | MIGUEL HERNANDO GARAY CALLE | Cédula de ciudadanía | 9 | $ 2.111.500 | 4 | 0,4% |
El top 10 concentra el 12,7% de los comparendos y el 12,8% del valor.
Consulta 5. Frecuencia de infracciones por tipo de identificación y género
Qué muestra: a la izquierda, cuántos comparendos hay según el documento con que se identifica el infractor (cédula de ciudadanía, de extranjería o NIT); a la derecha, cada infracción dividida por género o tipo de titular.
Cómo leerlo: en las columnas, la altura y el número arriba indican la cantidad de comparendos. En las barras horizontales, la longitud es la cantidad de comparendos de cada infracción y los colores indican quién la cometió (mujer, hombre, empresa).
Al pasar el cursor: sobre una columna aparecen el tipo de documento y su cantidad; sobre un tramo de color, la infracción completa y los comparendos de ese género.
Para qué sirve: conocer el perfil de quien comete cada infracción para diseñar mensajes y controles distintos para personas y empresas.
# Barras por tipo de identificación + infracción por género
q5a <- datos %>% count(tipo_id) %>% arrange(desc(n)) %>% # comparendos por tipo de identificación
mutate(et = wrapbr(tipo_id, 14), tip = paste0("<b>", tipo_id, "</b><br>", fm(n), " comparendos")) # etiqueta con saltos de línea y texto del hover
pa <- plot_ly(q5a, x = ~et, y = ~n, type = "bar", # gráfico de columnas por tipo de ID
marker = list(color = c(pal$azul, pal$turquesa, pal$ambar)[seq_len(nrow(q5a))]), # colores de las columnas
text = ~fm(n), textposition = "outside", cliponaxis = FALSE, textfont = list(size = 14, color = pal$marino), # texto exterior
hoverinfo = "text", hovertext = ~tip, height = 470) %>% # hover y alto
layout(xaxis = eje_cat(q5a$et), yaxis = eje_val(max(q5a$n) * 1.2)) %>% # ejes
pl("Por tipo de identificación", leyenda = FALSE, margen = list(t = 70, r = 10, b = 60, l = 20)) # estilo y título
q5b <- datos %>% count(infraccion, genero = str_to_title(genero)) %>% # comparendos por infracción y género
mutate(et = wrapbr(infraccion, 22), tip = paste0("<b>", infraccion, "</b><br>", genero, ": ", fm(n), " comparendos")) # etiqueta y hover
orden5 <- q5b %>% group_by(et) %>% summarise(t = sum(n), .groups = "drop") %>% arrange(t) # total por infracción para ordenar
pb <- plot_ly(height = 470) # gráfico vacío para las barras
for (g in unique(q5b$genero)) { # para cada género...
pb <- add_bars(pb, data = filter(q5b, genero == g), x = ~n, y = ~et, orientation = "h", name = g, # agrega barras horizontales por género
marker = list(color = unname(col_gen[g])), hoverinfo = "text", hovertext = ~tip) # (continúa: color y hover)
} # fin del for
pb <- pb %>% layout(barmode = "stack", xaxis = eje_val(max(orden5$t) * 1.05), yaxis = eje_cat(orden5$et)) %>% # barras apiladas y ejes
pl("Infracción según género") # estilo y título
htmltools::div(class = "dos-col", style = "grid-template-columns:1fr 1.4fr", graf(pa), graf(pb)) # muestra ambos gráficos en dos columnas
La base depurada completa tiene 2.119 registros y 4 infracciones: «Transitar en sentido contrario» solo aparece en días que no son domingo.
Consulta 6. Evolución de los comparendos (semanal y mensual)
Qué muestra: cómo cambian semana a semana el número de comparendos (barras turquesa, eje izquierdo) y el dinero que suman (línea ámbar, eje derecho, en millones de pesos).
Cómo leerlo: cada barra es una semana que empieza el lunes indicado en el eje horizontal; su altura y el número encima son los comparendos. La línea ámbar sube o baja según el valor económico de esa semana. La última semana está incompleta.
Al pasar el cursor: sobre una barra o un punto de la línea aparecen la semana, los comparendos y el valor económico de esa semana.
Para qué sirve: ver si el valor sube igual que la cantidad de comparendos o si cambia por el tipo de infracción, y detectar semanas críticas.
# Barras de comparendos + línea de valor económico (doble eje), por semana
q6 <- datos %>% mutate(semana = lubridate::floor_date(fecha, "week", week_start = 1)) %>% # agrupa por semana (inicia el lunes)
group_by(semana) %>% summarise(n = n(), v = sum(costo_de_comparendo), .groups = "drop") %>% # cuenta comparendos y suma valor por semana
mutate(tip = paste0("<b>Semana del ", format(semana, "%d/%m/%Y"), "</b><br>Comparendos: ", fm(n), "<br>Valor: ", cop(v))) # texto del hover
p <- plot_ly(height = 480) %>% # gráfico vacío de alto 480
add_bars(data = q6, x = ~semana, y = ~n, width = 5 * 86400000, name = "Comparendos", # barras de comparendos por semana
marker = list(color = pal$turquesa), text = ~fm(n), textposition = "outside", cliponaxis = FALSE, # color, texto exterior
textfont = list(size = 14, color = pal$marino), hoverinfo = "text", hovertext = ~tip) %>% # tamaño y color; hover
add_trace(data = q6, x = ~semana, y = ~v, yaxis = "y2", type = "scatter", mode = "lines+markers", # línea del valor en el eje derecho (y2)
name = "Valor económico", line = list(color = pal$ambar, width = 4), # nombre y estilo de la línea
marker = list(color = pal$ambar, size = 9), hoverinfo = "text", hovertext = ~tip) %>% # marcadores y hover
layout(xaxis = list(title = "", type = "date", tickformat = "%d/%m", tickvals = as.character(q6$semana), # eje x: fechas de cada semana
gridcolor = "#E5EAF2", fixedrange = TRUE), # (continúa: cuadrícula)
yaxis = eje_val(max(q6$n) * 1.2, title = list(text = "Comparendos", font = list(color = pal$turquesa))), # eje izquierdo: comparendos
yaxis2 = list(overlaying = "y", side = "right", range = c(0, max(q6$v) * 1.2), showgrid = FALSE, # eje derecho: valor en pesos, formato abreviado
zeroline = FALSE, fixedrange = TRUE, tickprefix = "$ ", tickformat = ".2s", # (continúa: sin línea cero y prefijo '$')
title = list(text = "Valor económico", font = list(color = pal$ambar)))) %>% # (continúa: título del eje)
pl("Comparendos (barras) y valor (línea) por semana<br><sup>Semana que inicia el lunes indicado; la última está incompleta</sup>", # estilo y título con subtítulo
margen = list(t = 95, r = 90, b = 55, l = 70)) # (continúa: márgenes amplios)
graf(p) # muestra el gráfico
# Resumen mensual
t6 <- datos %>% group_by(Mes = mes) %>% # resumen mensual: comparendos, días y valor
summarise(Comparendos = n(), Dias = n_distinct(fecha), Valor = sum(costo_de_comparendo), .groups = "drop") %>% # (continúa)
mutate(`Promedio diario` = format(round(Comparendos / Dias, 1), nsmall = 1, decimal.mark = ",", trim = TRUE), Comparendos = fm(Comparendos), # promedio diario y formato
`Días con registros` = Dias, Valor = cop(Valor)) %>% select(-Dias) # (continúa: días con registros y valor)
salida(tabla_html(t6)) # imprime la tabla
| Mes | Comparendos | Valor | Promedio diario | Días con registros |
|---|---|---|---|---|
| Abril | 711 | $ 216.506.000 | 37,4 | 19 |
| Mayo | 1.408 | $ 427.192.500 | 52,1 | 27 |
Solo se analizan abril (desde el 12/04/2010) y mayo (hasta el 27/05/2010), con 2.119 registros y $ 643.698.500. No permite evaluar tendencias anuales ni estacionalidad; por eso la evolución se presenta por semana.
Hallazgos principales
Los resultados subrayan la importancia de implementar políticas públicas basadas en datos: desde campañas educativas focalizadas hasta operativos estratégicos según el tipo de vehículo, el día y el tipo de infractor. La gestión efectiva de los comparendos no solo debe sancionar, sino también prevenir, orientar y transformar los hábitos de conducción para mejorar la seguridad vial colectiva.
Calidad de los datos
Gestión y control
Política pública
Fuente de datos
Secretaría de Tránsito de Barranquilla. Base de datos de comparendos, abril–mayo de 2010 (archivo BD_Comparendos_TAD_13.xlsx).
Herramientas
Procesamiento y visualización en R version 4.6.1 (2026-06-24 ucrt) con los paquetes tidyverse, plotly, scales y htmltools.