library(tidyverse) # dplyr, ggplot2, readr, tidyr
library(sf) # mapas (shapefiles)
library(leaflet) # mapa interactivo
library(scales) # formato de números
library(knitr) # tablas
library(kableExtra) # tablas con formato
library(ggrepel) # etiquetas que no se pisan
library(patchwork) # combinar gráficos
# Paleta y tema del curso
morado <- "#351A4F"
magenta <- "#D9006C"
turquesa <- "#168C86"
gris <- "#D9D6DD"
tema_clase <- theme_minimal(base_size = 12) +
theme(
plot.title.position = "plot",
plot.title = element_text(face = "bold", size = 14, hjust = 0),
plot.subtitle = element_text(size = 10.5, color = "grey35", hjust = 0),
panel.grid.minor = element_blank(),
plot.caption = element_text(hjust = 0, color = "grey45", size = 8.5),
plot.margin = margin(10, 20, 10, 10)
)
theme_set(tema_clase)
num <- function(x, d = 0) format(round(x, d), big.mark = ".", decimal.mark = ",", nsmall = d)
pct <- function(x, d = 0) paste0(num(x, d), " %")
pesos <- function(x) paste0("$", num(x / 1e6, 1), " M")
Bogotá se divide en 112 UPZ urbanas. Para cada una sabemos cuántos delitos se denunciaron entre enero y julio de 2023, quién vive allí, cuánto gana, cómo es su entorno y qué tan insegura se siente. La pregunta de fondo del taller es:
¿Qué nos dicen los datos sobre la inseguridad en Bogotá y su relación con el territorio y las condiciones de vida de cada UPZ?
Partimos de un EDA preliminar (notebook en Python) centrado en el hurto y su relación con pobreza, entorno y CAI. Al revisarlo con cuidado descubrimos que varias de sus conclusiones eran artefactos de cómo se calcula la tasa (sección 2.3). Corregirlo nos llevó a la historia de este informe: en Bogotá no hay una inseguridad, sino dos, que viven en lugares distintos.
| # | Pregunta |
|---|---|
| P1 | ¿Las UPZ con más denuncias son las más inseguras, o solo las más pobladas? ¿Qué hacemos con las UPZ casi deshabitadas? |
| P2 | ¿Dónde se concentra el hurto a personas y tiene que ver con la pobreza? |
| P3 | ¿Cambia el tipo de delito predominante según el nivel de ingreso de la UPZ? |
| P4 | ¿La cercanía a ollas, bares, lotes oscuros y el alumbrado se relacionan con ciertos delitos? |
| P5 | ¿La percepción de inseguridad sigue a las denuncias? |
| P6 | ¿Qué tan lejos queda la policía (CAI) en las UPZ con tasas más altas? |
vars_elegidas <- tribble(
~bloque, ~variable, ~por_que,
"Demografía", "poblacion", "Denominador de toda tasa: hay que mirarla antes de confiar en cualquier tasa (P1).",
"Demografía", "densidad_hab_km2", "Distingue zonas residenciales densas de zonas de paso (P1, P2).",
"Vivienda/infraestructura", "luminarias_km2", "Alumbrado por área: proxy de calles iluminadas (P4).",
"Vivienda/infraestructura", "pct_luminarias_led", "Calidad del alumbrado (P4).",
"Delitos", "casos_total", "Volumen de denuncias: lo que se ve en un conteo simple (P1).",
"Delitos", "tasa_total", "Riesgo por habitante de los 10 delitos (P1, P6).",
"Delitos", "tasa_hurto_personas", "Delito más frecuente (más de la mitad de las denuncias) (P2).",
"Delitos", "tasa_homicidios", "Delito violento más grave (P3, P5).",
"Delitos", "tasa_violencia_intrafamiliar", "Violencia en el hogar, otra lógica distinta al hurto (P3, P5).",
"Delitos", "tasa_lesiones", "Violencia interpersonal en la calle (P3, P4).",
"Encuesta 2021", "salario_promedio", "Nivel de ingreso; base de los grupos socioeconómicos (P3).",
"Encuesta 2021", "pct_pobreza", "Pobreza, la hipótesis clásica del delito (P2).",
"Encuesta 2021", "pct_percibe_inseguridad", "Percepción, no conteo: ¿coincide con las denuncias? (P5).",
"Encuesta 2021", "pct_cerca_ollas", "Entorno: expendios de droga (P4).",
"Encuesta 2021", "pct_cerca_bares", "Entorno: bares y prostíbulos, zonas de rumba (P4).",
"Encuesta 2021", "pct_cerca_lotes_oscuros", "Entorno: lotes baldíos o sitios oscuros (P4).",
"Encuesta 2021", "min_cai_policia", "Minutos caminando al CAI: presencia policial (P6)."
)
kable(vars_elegidas, col.names = c("Bloque", "Variable", "Por qué la elegimos")) %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = TRUE, font_size = 13) %>%
column_spec(2, monospace = TRUE)
| Bloque | Variable | Por qué la elegimos |
|---|---|---|
| Demografía | poblacion | Denominador de toda tasa: hay que mirarla antes de confiar en cualquier tasa (P1). |
| Demografía | densidad_hab_km2 | Distingue zonas residenciales densas de zonas de paso (P1, P2). |
| Vivienda/infraestructura | luminarias_km2 | Alumbrado por área: proxy de calles iluminadas (P4). |
| Vivienda/infraestructura | pct_luminarias_led | Calidad del alumbrado (P4). |
| Delitos | casos_total | Volumen de denuncias: lo que se ve en un conteo simple (P1). |
| Delitos | tasa_total | Riesgo por habitante de los 10 delitos (P1, P6). |
| Delitos | tasa_hurto_personas | Delito más frecuente (más de la mitad de las denuncias) (P2). |
| Delitos | tasa_homicidios | Delito violento más grave (P3, P5). |
| Delitos | tasa_violencia_intrafamiliar | Violencia en el hogar, otra lógica distinta al hurto (P3, P5). |
| Delitos | tasa_lesiones | Violencia interpersonal en la calle (P3, P4). |
| Encuesta 2021 | salario_promedio | Nivel de ingreso; base de los grupos socioeconómicos (P3). |
| Encuesta 2021 | pct_pobreza | Pobreza, la hipótesis clásica del delito (P2). |
| Encuesta 2021 | pct_percibe_inseguridad | Percepción, no conteo: ¿coincide con las denuncias? (P5). |
| Encuesta 2021 | pct_cerca_ollas | Entorno: expendios de droga (P4). |
| Encuesta 2021 | pct_cerca_bares | Entorno: bares y prostíbulos, zonas de rumba (P4). |
| Encuesta 2021 | pct_cerca_lotes_oscuros | Entorno: lotes baldíos o sitios oscuros (P4). |
| Encuesta 2021 | min_cai_policia | Minutos caminando al CAI: presencia policial (P6). |
Dejamos por fuera, a propósito, los grupos de edad, las viviendas, el estrato detallado y los demás tiempos de desplazamiento: están muy correlacionados con el salario (estrato y educación cuentan casi la misma historia) o no se conectan con ninguna de nuestras preguntas. Preferimos 17 variables bien contadas a 67 a medias.
# Rutas relativas: el .Rmd está en la carpeta del taller, junto a input/
upz <- read_csv("input/upz_variables.csv", show_col_types = FALSE,
locale = locale(encoding = "UTF-8"))
upz_shp <- st_read("input/shp/upz_bogota.shp", quiet = TRUE)
loc_shp <- st_read("input/shp/localidades_bogota.shp", quiet = TRUE)
dim(upz)
## [1] 112 67
La tabla tiene 112 UPZ y 67
columnas. Todas las columnas de interés son numéricas, salvo
los nombres (upz_nombre, localidad) y la marca
lógica em_agrupada.
tibble(tipo = map_chr(upz, ~ class(.x)[1])) %>% count(tipo, name = "columnas")
# Faltantes en toda la tabla
sum(is.na(upz))
## [1] 0
No hay valores faltantes (0 NA). Que no falte nada no significa que todo sea confiable: los problemas de esta tabla no son vacíos, sino denominadores pequeños y valores repetidos.
mapa_upz <- upz_shp %>% left_join(upz %>% select(-upz_nombre), by = "upz_cod")
sum(is.na(mapa_upz$poblacion)) # 0 = todas las UPZ del mapa tienen datos
## [1] 0
Antes de calcular cualquier tasa miramos el denominador.
umbral <- 10000
n_bajas <- sum(upz$poblacion < umbral)
ggplot(upz, aes(x = poblacion)) +
geom_histogram(aes(fill = poblacion < umbral), bins = 30, color = "white",
boundary = 0) +
geom_vline(xintercept = umbral, linetype = "dashed", color = magenta) +
annotate("text", x = umbral + 4000, y = 13, hjust = 0, color = magenta, size = 3.6,
label = paste0(n_bajas, " UPZ con menos de 10.000 hab:\naeropuerto, parques, zonas industriales")) +
scale_fill_manual(values = c(`TRUE` = magenta, `FALSE` = gris), guide = "none") +
scale_x_continuous(labels = label_comma(big.mark = ".")) +
labs(title = paste0(n_bajas, " UPZ tienen tan poca gente que cualquier tasa por habitante se dispara"),
subtitle = "Distribución de la población por UPZ (DANE 2023)",
x = "Habitantes", y = "Número de UPZ",
caption = "Fuente: proyecciones DANE 2023.")
upz %>%
filter(poblacion < umbral) %>%
arrange(poblacion) %>%
transmute(UPZ = upz_nombre, Localidad = localidad,
Habitantes = num(poblacion), Denuncias = casos_total,
`Tasa total /10.000 hab` = num(tasa_total, 0)) %>%
kable(align = "llrrr") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE, font_size = 13)
| UPZ | Localidad | Habitantes | Denuncias | Tasa total /10.000 hab |
|---|---|---|---|---|
| El Mochuelo | Ciudad Bolívar | 30 | 34 | 11.333 |
| Aeropuerto El Dorado | Fontibón | 883 | 545 | 6.172 |
| Parque Entrenubes | Usme | 1.169 | 16 | 137 |
| Parque Salitre | Barrios Unidos | 1.442 | 233 | 1.616 |
| Paseo De Los Libertadores | Usaquén | 2.690 | 74 | 275 |
| Guaymaral | Suba | 2.692 | 45 | 167 |
| Jardin Botanico | Engativá | 2.900 | 266 | 917 |
| Parque Simon Bolivar - Can | Teusaquillo | 3.323 | 958 | 2.883 |
| La Academia | Suba | 5.260 | 123 | 234 |
| Monte Blanco | Ciudad Bolívar | 6.575 | 80 | 122 |
| Zona Industrial | Puente Aranda | 6.666 | 1573 | 2.360 |
El caso extremo es El Mochuelo: 30 habitantes y 34 denuncias producen una tasa de 11.333 por 10.000 hab, ¡más de una denuncia por habitante en siete meses! Lo mismo pasa en el Aeropuerto El Dorado o el Parque Simón Bolívar: son lugares donde mucha gente pasa pero casi nadie vive. Su tasa mide visitantes, no residentes.
Decisión: para todo análisis de tasas y correlaciones trabajamos con las 101 UPZ con 10.000 habitantes o más. Las 11 restantes se mantienen en el mapa (en gris) y en el análisis de casos absolutos. El umbral es pequeño frente a la mediana (54.102 hab) y deja fuera justo las UPZ no residenciales.
upz_f <- upz %>% filter(poblacion >= umbral)
Nuestro primer EDA promediaba tasas de hurto de UPZ por localidad, sin filtrar, y concluía que Ciudad Bolívar y Fontibón eran las localidades con más hurto a personas. Al repetir el cálculo correctamente (sumando casos y población de las UPZ residenciales de cada localidad) la conclusión se invierte:
loc_ingenuo <- upz %>% group_by(localidad) %>%
summarise(tasa_promedio_sin_filtro = mean(tasa_hurto_personas))
loc_correcto <- upz_f %>% group_by(localidad) %>%
summarise(tasa_corregida = sum(casos_hurto_personas) / sum(poblacion) * 10000)
comparacion_loc <- loc_ingenuo %>% left_join(loc_correcto, by = "localidad") %>%
mutate(puesto_antes = min_rank(desc(tasa_promedio_sin_filtro)),
puesto_despues = min_rank(desc(tasa_corregida))) %>%
arrange(puesto_antes)
comparacion_loc %>%
filter(localidad %in% c("Ciudad Bolívar", "Fontibón", "Santa Fe", "Teusaquillo",
"Los Mártires", "Chapinero", "Usme", "Bosa")) %>%
transmute(Localidad = localidad,
`Promedio simple, sin filtro` = num(tasa_promedio_sin_filtro),
`Puesto antes` = puesto_antes,
`Tasa corregida` = num(tasa_corregida),
`Puesto después` = puesto_despues) %>%
kable(align = "lrrrr", caption = "Hurto a personas por 10.000 hab: antes y después de corregir") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE, font_size = 13) %>%
row_spec(which(comparacion_loc %>%
filter(localidad %in% c("Ciudad Bolívar", "Fontibón", "Santa Fe", "Teusaquillo",
"Los Mártires", "Chapinero", "Usme", "Bosa")) %>%
pull(localidad) %in% c("Ciudad Bolívar", "Fontibón")),
bold = TRUE, color = magenta)
| Localidad | Promedio simple, sin filtro | Puesto antes | Tasa corregida | Puesto después |
|---|---|---|---|---|
| Ciudad Bolívar | 837 | 1 | 54 | 16 |
| Fontibón | 697 | 2 | 126 | 9 |
| Santa Fe | 582 | 3 | 434 | 3 |
| Teusaquillo | 572 | 4 | 344 | 5 |
| Los Mártires | 503 | 5 | 481 | 1 |
| Chapinero | 356 | 9 | 346 | 4 |
| Bosa | 43 | 18 | 44 | 18 |
| Usme | 42 | 19 | 43 | 19 |
Ciudad Bolívar pasa del puesto 1 al 16 de 19. Una sola UPZ de 30 habitantes había fabricado un “hallazgo”. El notebook también reportaba las tasas “por 100.000 hab”, cuando la fuente las da por 10.000, y atribuía a la población y la densidad el 80 % de la “importancia” en un Random Forest: era el denominador de la tasa reapareciendo como predictor. Lo contamos porque también es un hallazgo.
etiquetas <- c(
poblacion = "Población", densidad_hab_km2 = "Densidad (hab/km²)",
luminarias_km2 = "Luminarias por km²", pct_luminarias_led = "% luminarias LED",
casos_total = "Denuncias (casos)", tasa_total = "Tasa total /10.000",
tasa_hurto_personas = "Tasa hurto personas", tasa_homicidios = "Tasa homicidios",
tasa_violencia_intrafamiliar = "Tasa violencia intrafamiliar", tasa_lesiones = "Tasa lesiones",
salario_promedio = "Salario promedio ($)", pct_pobreza = "% pobreza",
pct_percibe_inseguridad = "% percibe inseguridad", pct_cerca_ollas = "% cerca de ollas",
pct_cerca_bares = "% cerca de bares", pct_cerca_lotes_oscuros = "% cerca lotes oscuros",
min_cai_policia = "Minutos al CAI"
)
vars <- names(etiquetas)
upz_f %>%
select(all_of(vars)) %>%
pivot_longer(everything()) %>%
mutate(name = factor(etiquetas[name], levels = etiquetas)) %>%
ggplot(aes(x = value)) +
geom_histogram(bins = 20, fill = morado, color = "white") +
facet_wrap(~ name, scales = "free", ncol = 4) +
scale_x_continuous(labels = label_number(big.mark = ".", decimal.mark = ",", scale_cut = cut_short_scale())) +
labs(title = "Casi todas las tasas de delito tienen una cola larga a la derecha",
subtitle = "Distribución de las 17 variables elegidas · 101 UPZ con 10.000 hab o más",
x = NULL, y = "UPZ") +
theme(strip.text = element_text(face = "bold", size = 9),
axis.text = element_text(size = 7.5))
Las tasas de delito (y el salario) son muy asimétricas: unas pocas UPZ concentran valores muy altos. Por eso, en lo que sigue usamos medianas además de medias y correlaciones de Spearman (por rangos), que no se dejan arrastrar por los extremos como la de Pearson.
En 32 UPZ la Encuesta Multipropósito no da un valor propio sino el de
su grupo (em_agrupada = TRUE): varias UPZ vecinas comparten
exactamente el mismo salario, la misma pobreza y la misma percepción.
Esto reduce la variación real y puede inflar o atenuar correlaciones.
Para comprobarlo, en la P4 repetimos las correlaciones solo con las UPZ
no agrupadas.
upz_f %>% count(em_agrupada, name = "UPZ")
resumen <- upz_f %>%
select(all_of(vars)) %>%
pivot_longer(everything(), names_to = "variable") %>%
group_by(variable) %>%
summarise(Media = mean(value), Mediana = median(value), `Desv. est.` = sd(value),
Q1 = quantile(value, .25), Q3 = quantile(value, .75),
Mín = min(value), Máx = max(value)) %>%
mutate(variable = factor(variable, levels = vars)) %>%
arrange(variable) %>%
mutate(Variable = etiquetas[as.character(variable)], .before = 1) %>%
select(-variable)
resumen %>%
mutate(across(where(is.numeric), ~ ifelse(abs(.x) >= 100, num(.x, 0), num(.x, 2)))) %>%
kable(align = "lrrrrrrr",
caption = "Estadística descriptiva de las variables elegidas (101 UPZ con ≥ 10.000 hab)") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE, font_size = 13) %>%
pack_rows("Demografía", 1, 2) %>%
pack_rows("Infraestructura", 3, 4) %>%
pack_rows("Delitos ene–jul 2023 (casos y tasas por 10.000 hab)", 5, 10) %>%
pack_rows("Encuesta Multipropósito 2021", 11, 17)
| Variable | Media | Mediana | Desv. est. | Q1 | Q3 | Mín | Máx |
|---|---|---|---|---|---|---|---|
| Demografía | |||||||
| Población | 78.247 | 62.139 | 56.791 | 38.856 | 103.350 | 11.247 | 305.907 |
| Densidad (hab/km²) | 21.117 | 21.229 | 9.404 | 14.209 | 27.471 | 3.042 | 46.157 |
| Infraestructura | |||||||
| Luminarias por km² | 982 | 1.047 | 346 | 727 | 1.192 | 116 | 1.751 |
| % luminarias LED | 43,93 | 50,42 | 32,13 | 9,92 | 74,44 | 0,00 | 95,77 |
| Delitos ene–jul 2023 (casos y tasas por 10.000 hab) | |||||||
| Denuncias (casos) | 1.573 | 1.285 | 996 | 888 | 2.031 | 115 | 5.441 |
| Tasa total /10.000 | 299 | 187 | 354 | 126 | 273 | 64,47 | 2.344 |
| Tasa hurto personas | 168 | 96,03 | 224 | 54,33 | 160 | 15,70 | 1.488 |
| Tasa homicidios | 0,90 | 0,56 | 1,13 | 0,24 | 1,14 | 0,00 | 6,25 |
| Tasa violencia intrafamiliar | 21,72 | 17,65 | 24,08 | 12,46 | 23,33 | 5,36 | 232 |
| Tasa lesiones | 15,34 | 11,76 | 14,49 | 8,25 | 15,92 | 4,15 | 98,54 |
| Encuesta Multipropósito 2021 | |||||||
| Salario promedio ($) | 2.553.007 | 1.826.624 | 1.768.638 | 1.229.038 | 3.574.567 | 845.688 | 7.977.532 |
| % pobreza | 3,99 | 2,70 | 3,83 | 0,99 | 5,79 | 0,07 | 16,56 |
| % percibe inseguridad | 61,29 | 65,97 | 18,67 | 48,52 | 74,30 | 17,45 | 92,11 |
| % cerca de ollas | 21,45 | 20,92 | 15,37 | 10,14 | 30,54 | 0,31 | 75,60 |
| % cerca de bares | 14,54 | 13,00 | 10,71 | 5,82 | 19,48 | 0,64 | 53,91 |
| % cerca lotes oscuros | 19,25 | 17,89 | 11,96 | 11,28 | 24,86 | 0,64 | 59,76 |
| Minutos al CAI | 18,09 | 17,49 | 4,40 | 15,06 | 20,54 | 10,06 | 35,11 |
Lectura rápida: la UPZ típica (mediana) registra 187 denuncias por 10.000 hab en siete meses, pero la media es 299: la diferencia la ponen unas pocas UPZ del centro. El salario medio por UPZ va de $0,8 M a $8,0 M, casi 10 veces más. Y en la UPZ mediana 66 % de los hogares dice tener problemas de inseguridad en su entorno.
rho_casos_pob <- cor(upz$casos_total, upz$poblacion, method = "spearman")
rho_tasa_pob <- cor(upz_f$tasa_total, upz_f$poblacion, method = "spearman")
top_casos <- upz %>% slice_max(casos_total, n = 6)
top_tasa <- upz_f %>% slice_max(tasa_total, n = 6)
ggplot(upz, aes(x = poblacion, y = casos_total)) +
geom_point(aes(color = poblacion < umbral), size = 2.3, alpha = 0.8) +
geom_text_repel(data = bind_rows(top_casos, top_tasa, upz %>% filter(poblacion < 1000)) %>% distinct(upz_cod, .keep_all = TRUE),
aes(label = upz_nombre), size = 3, color = "grey20", max.overlaps = 20,
min.segment.length = 0) +
scale_color_manual(values = c(`TRUE` = magenta, `FALSE` = morado),
labels = c(`TRUE` = "< 10.000 hab", `FALSE` = "≥ 10.000 hab"), name = NULL) +
scale_x_log10(labels = label_comma(big.mark = ".")) +
scale_y_log10(labels = label_comma(big.mark = ".")) +
labs(title = "Las denuncias crecen con la población, pero el centro se sale de la tendencia",
subtitle = paste0("Denuncias (ene–jul 2023) vs. población · escala log · Spearman = ",
num(rho_casos_pob, 2),
"\nLas UPZ que se salen hacia arriba son centros de comercio y paso."),
x = "Población (log)", y = "Denuncias de los 10 delitos (log)",
caption = "Fuente: SDSCJ (delitos) y DANE (población).") +
theme(legend.position = "top", legend.justification = "left")
En general, el volumen de denuncias crece con la población (Spearman 0,56): UPZ enormes como El Rincón (295.322 hab) o Bosa Central acumulan muchos casos porque allí vive muchísima gente. Pero las tres UPZ con más denuncias absolutas de toda la ciudad (La Sabana, Chico Lago, Las Nieves) tienen menos de 40.000 habitantes: están muy por encima de la tendencia. Cuando se divide por habitantes, la relación se invierte: la tasa total se relaciona negativamente con la población (Spearman -0,55). Las UPZ con más riesgo por habitante son chicas y céntricas, con mucha población flotante que trabaja, compra o se divierte allí pero no vive allí. Ni el conteo ni la tasa son “la verdad”: el conteo mide carga para la policía; la tasa por residente sobrestima el riesgo en zonas de paso.
# Quintiles calculados solo con UPZ residenciales; las de baja población quedan en gris
quintil <- function(x, ref) {
cortes <- quantile(ref, probs = seq(0, 1, 0.2))
cut(x, breaks = cortes, include.lowest = TRUE,
labels = c("Muy baja", "Baja", "Media", "Alta", "Muy alta"))
}
mapa_upz <- mapa_upz %>%
mutate(
q_hurto = if_else(poblacion >= umbral,
as.character(quintil(tasa_hurto_personas, upz_f$tasa_hurto_personas)), NA_character_),
tasa_violenta = tasa_homicidios + tasa_lesiones + tasa_violencia_intrafamiliar,
q_violencia = if_else(poblacion >= umbral,
as.character(quintil(tasa_violenta,
with(upz_f, tasa_homicidios + tasa_lesiones + tasa_violencia_intrafamiliar))),
NA_character_)
)
niveles <- c("Muy baja", "Baja", "Media", "Alta", "Muy alta")
mapa_largo <- mapa_upz %>%
select(upz_cod, q_hurto, q_violencia, geometry) %>%
pivot_longer(c(q_hurto, q_violencia), names_to = "indicador", values_to = "quintil") %>%
mutate(indicador = recode(indicador,
q_hurto = "Hurto a personas",
q_violencia = "Violencia (homicidios + lesiones + v. intrafamiliar)"),
quintil = factor(quintil, levels = niveles))
ggplot(mapa_largo) +
geom_sf(aes(fill = quintil), color = "white", linewidth = 0.15) +
geom_sf(data = loc_shp, fill = NA, color = "grey25", linewidth = 0.3) +
facet_wrap(~ indicador) +
scale_fill_manual(values = c("#F1EAF4", "#D4BEDE", "#A57DB8", "#6E4290", morado),
na.value = "grey85", na.translate = TRUE,
labels = c(niveles, "< 10.000 hab (excluida)"),
name = "Tasa por 10.000 hab\n(quintiles)", drop = FALSE) +
labs(title = "El hurto se concentra en el centro expandido; la violencia, en el centro y el sur",
subtitle = "Quintiles de la tasa por 10.000 hab (ene–jul 2023) · 101 UPZ con 10.000 hab o más",
caption = "Fuente: SDSCJ, DANE. Bordes oscuros = localidades. Gris = UPZ con menos de 10.000 habitantes.") +
theme_void(base_size = 12) +
theme(plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey35"),
plot.caption = element_text(hjust = 0, color = "grey45", size = 8.5),
strip.text = element_text(face = "bold", size = 11, margin = margin(b = 6)),
legend.position = "right")
rho_pob_hurto <- cor(upz_f$pct_pobreza, upz_f$tasa_hurto_personas, method = "spearman")
rho_sal_hurto <- cor(upz_f$salario_promedio, upz_f$tasa_hurto_personas, method = "spearman")
pearson_crudo <- cor(upz$pct_pobreza, upz$tasa_hurto_personas)
ggplot(upz_f, aes(x = salario_promedio, y = tasa_hurto_personas)) +
geom_smooth(method = "loess", se = FALSE, color = gris, linewidth = 1.2) +
geom_point(aes(color = pct_pobreza), size = 2.6, alpha = 0.9) +
geom_text_repel(data = upz_f %>% slice_max(tasa_hurto_personas, n = 5),
aes(label = upz_nombre), size = 3, color = "grey20") +
scale_x_log10(labels = label_number(scale = 1e-6, suffix = " M", prefix = "$", decimal.mark = ",")) +
scale_y_log10(labels = label_comma(big.mark = ".")) +
scale_color_gradient(low = "#F4C7DC", high = magenta, name = "% pobreza") +
labs(title = "Donde se gana más, se denuncian más hurtos a personas por habitante",
subtitle = paste0("Tasa de hurto a personas vs. salario promedio (escalas log) · Spearman salario = ",
num(rho_sal_hurto, 2), "; pobreza = ", num(rho_pob_hurto, 2)),
x = "Salario mensual promedio de la UPZ", y = "Hurto a personas por 10.000 hab",
caption = "Fuente: SDSCJ 2023, Encuesta Multipropósito 2021.")
El hurto a personas no es un fenómeno de la pobreza. Es al revés: la correlación con la pobreza es -0,62 y con el salario 0,71. El EDA preliminar encontraba una correlación casi nula (Pearson 0,02) porque mezclaba las UPZ deshabitadas y usaba una medida sensible a extremos. La curva sube con fuerza hasta unos $3 M de salario promedio y luego se aplana: entre las UPZ de mayor ingreso, el hurto ya no crece, y los extremos (Las Nieves, La Sabana, Puente Aranda) son zonas de comercio, no de residencia rica. El hurto ocurre donde hay cosas que robar y gente que pasa: centro, Chapinero, Teusaquillo, el corredor nororiental. Ojo con un sesgo probable: en zonas de mayor ingreso también se denuncia más (más acceso, más bienes asegurados).
upz_f <- upz_f %>%
mutate(grupo_ingreso = ntile(salario_promedio, 4),
grupo_ingreso = factor(grupo_ingreso, 1:4,
labels = c("Q1 · ingreso más bajo", "Q2", "Q3", "Q4 · ingreso más alto")))
nombres_delito <- c(
hurto_personas = "Hurto a personas", hurto_celulares = "Hurto de celulares",
violencia_intrafamiliar = "Violencia intrafamiliar", lesiones = "Lesiones",
hurto_comercio = "Hurto a comercio", hurto_bicicletas = "Hurto de bicicletas",
hurto_residencias = "Hurto a residencias", hurto_motos = "Hurto de motos",
hurto_automotor = "Hurto de automotores", homicidios = "Homicidios"
)
# Tasa agregada del grupo = suma de casos / suma de población (no promedio de tasas)
tasas_grupo <- upz_f %>%
select(grupo_ingreso, poblacion, starts_with("casos_"), -casos_total) %>%
pivot_longer(starts_with("casos_"), names_to = "delito", names_prefix = "casos_", values_to = "casos") %>%
group_by(grupo_ingreso, delito) %>%
summarise(casos = sum(casos), poblacion = sum(poblacion), .groups = "drop") %>%
group_by(delito) %>%
mutate(tasa = casos / poblacion * 10000,
indice = tasa / tasa[grupo_ingreso == "Q1 · ingreso más bajo"] * 100) %>%
ungroup() %>%
mutate(delito_nom = nombres_delito[delito])
rangos_salario <- upz_f %>% group_by(grupo_ingreso) %>%
summarise(n = n(), sal_min = min(salario_promedio), sal_max = max(salario_promedio))
violentos <- c("homicidios", "violencia_intrafamiliar", "lesiones")
orden <- tasas_grupo %>% filter(grupo_ingreso == "Q4 · ingreso más alto") %>%
arrange(indice) %>% pull(delito_nom)
tasas_grupo %>%
mutate(delito_nom = factor(delito_nom, levels = orden),
tipo = if_else(delito %in% violentos, "Violencia contra las personas", "Hurtos")) %>%
ggplot(aes(x = tasa, y = delito_nom)) +
geom_line(aes(group = delito_nom), color = gris, linewidth = 1.6) +
geom_point(aes(fill = grupo_ingreso), shape = 21, size = 3.4, color = "white", stroke = 0.6) +
facet_wrap(~ tipo, scales = "free", ncol = 2) +
scale_x_log10(labels = label_number(decimal.mark = ",", big.mark = ".", accuracy = 0.1)) +
scale_fill_manual(values = c("#E9A5C7", magenta, "#7A5A96", morado), name = "Grupo de ingreso") +
labs(title = "En las UPZ más pobres pesa la violencia; en las más ricas, el hurto",
subtitle = "Tasa agregada por 10.000 hab según cuartil de salario promedio de la UPZ (escala log)",
x = "Denuncias por 10.000 hab (ene–jul 2023)", y = NULL,
caption = "Tasa de cada grupo = suma de casos / suma de población de sus UPZ. Fuente: SDSCJ, EM 2021.") +
theme(legend.position = "top", legend.justification = "left",
strip.text = element_text(face = "bold", size = 11),
panel.grid.major.y = element_blank())
tasas_grupo %>%
filter(delito %in% c("homicidios", "violencia_intrafamiliar", "lesiones", "hurto_personas", "hurto_celulares")) %>%
select(delito_nom, grupo_ingreso, tasa) %>%
pivot_wider(names_from = grupo_ingreso, values_from = tasa) %>%
mutate(`Razón Q4/Q1` = .[[5]] / .[[2]]) %>%
mutate(across(where(is.numeric), ~ num(.x, 2))) %>%
rename(Delito = delito_nom) %>%
kable(align = "lrrrrr",
caption = "Tasa por 10.000 hab según cuartil de ingreso (Q1 = más bajo)") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE, font_size = 13)
| Delito | Q1 · ingreso más bajo | Q2 | Q3 | Q4 · ingreso más alto | Razón Q4/Q1 |
|---|---|---|---|---|---|
| Homicidios | 1,22 | 0,68 | 0,56 | 0,29 | 0,24 |
| Hurto de celulares | 17,35 | 25,51 | 55,75 | 75,52 | 4,35 |
| Hurto a personas | 50,04 | 74,00 | 153,82 | 197,62 | 3,95 |
| Lesiones | 12,86 | 12,26 | 13,39 | 9,29 | 0,72 |
| Violencia intrafamiliar | 27,18 | 19,47 | 16,58 | 12,66 | 0,47 |
h_q1 <- tasas_grupo %>% filter(delito == "homicidios", grupo_ingreso == "Q1 · ingreso más bajo") %>% pull(tasa)
h_q4 <- tasas_grupo %>% filter(delito == "homicidios", grupo_ingreso == "Q4 · ingreso más alto") %>% pull(tasa)
v_q1 <- tasas_grupo %>% filter(delito == "violencia_intrafamiliar", grupo_ingreso == "Q1 · ingreso más bajo") %>% pull(tasa)
v_q4 <- tasas_grupo %>% filter(delito == "violencia_intrafamiliar", grupo_ingreso == "Q4 · ingreso más alto") %>% pull(tasa)
hp_q1 <- tasas_grupo %>% filter(delito == "hurto_personas", grupo_ingreso == "Q1 · ingreso más bajo") %>% pull(tasa)
hp_q4 <- tasas_grupo %>% filter(delito == "hurto_personas", grupo_ingreso == "Q4 · ingreso más alto") %>% pull(tasa)
El hurto a personas es el delito más denunciado en casi todas las UPZ, pero la mezcla cambia con el ingreso. En el cuartil de menor salario la tasa de homicidios es 4,2 veces la del cuartil más rico y la de violencia intrafamiliar 2,1 veces; el hurto a personas, en cambio, es 3,9 veces mayor en el cuartil más rico, y lo mismo ocurre con el hurto a comercio y de celulares. Las lesiones y los hurtos de motos y automotores se mantienen parejos en los tres primeros cuartiles y caen en el más rico. Aquí aparecen las dos inseguridades: una contra el patrimonio, en zonas de comercio y alto ingreso; otra contra la vida y el cuerpo, dentro y alrededor del hogar, en zonas pobres.
delitos_p4 <- c(tasa_hurto_personas = "Hurto a personas", tasa_lesiones = "Lesiones",
tasa_homicidios = "Homicidios", tasa_violencia_intrafamiliar = "Violencia intraf.")
entorno_p4 <- c(pct_cerca_ollas = "% cerca de ollas", pct_cerca_bares = "% cerca de bares",
pct_cerca_lotes_oscuros = "% lotes oscuros", luminarias_km2 = "Luminarias por km²",
pct_luminarias_led = "% luminarias LED", pct_pobreza = "% pobreza (referencia)")
corr_spearman <- function(datos) {
cor(datos[names(entorno_p4)], datos[names(delitos_p4)], method = "spearman") %>%
as_tibble(rownames = "entorno") %>%
pivot_longer(-entorno, names_to = "delito", values_to = "rho")
}
corr_todas <- corr_spearman(upz_f)
corr_no_agr <- corr_spearman(filter(upz_f, !em_agrupada)) %>% rename(rho_no_agrupadas = rho)
corr_todas %>%
mutate(entorno = factor(entorno_p4[entorno], levels = rev(entorno_p4)),
delito = factor(delitos_p4[delito], levels = delitos_p4)) %>%
ggplot(aes(x = delito, y = entorno, fill = rho)) +
geom_tile(color = "white", linewidth = 1) +
geom_text(aes(label = num(rho, 2), color = abs(rho) > 0.45), size = 3.8) +
scale_fill_gradient2(low = turquesa, mid = "white", high = magenta, midpoint = 0,
limits = c(-1, 1), name = "Spearman") +
scale_color_manual(values = c(`TRUE` = "white", `FALSE` = "grey20"), guide = "none") +
scale_x_discrete(position = "top") +
labs(title = "Las ollas acompañan a la violencia, no al hurto",
subtitle = "Correlación de Spearman entre entorno y tasas de delito · 101 UPZ con 10.000 hab o más",
x = NULL, y = NULL,
caption = "Magenta = se mueven juntas; turquesa = en sentido opuesto. Fuente: EM 2021, SDSCJ 2023, Datos Abiertos (luminarias).") +
theme(panel.grid = element_blank(), axis.text.x = element_text(face = "bold"))
corr_todas %>% left_join(corr_no_agr, by = c("entorno", "delito")) %>%
filter(entorno %in% c("pct_cerca_ollas", "pct_cerca_lotes_oscuros", "pct_cerca_bares")) %>%
mutate(Entorno = entorno_p4[entorno], Delito = delitos_p4[delito]) %>%
transmute(Entorno, Delito, `Todas (101)` = num(rho, 2),
`Solo no agrupadas` = num(rho_no_agrupadas, 2)) %>%
kable(align = "llrr", caption = "Robustez: correlaciones con y sin las UPZ de encuesta agrupada") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE, font_size = 13)
| Entorno | Delito | Todas (101) | Solo no agrupadas |
|---|---|---|---|
| % cerca de ollas | Hurto a personas | -0,41 | -0,40 |
| % cerca de ollas | Lesiones | 0,47 | 0,40 |
| % cerca de ollas | Homicidios | 0,64 | 0,59 |
| % cerca de ollas | Violencia intraf. | 0,66 | 0,59 |
| % cerca de bares | Hurto a personas | 0,10 | 0,03 |
| % cerca de bares | Lesiones | 0,47 | 0,41 |
| % cerca de bares | Homicidios | 0,35 | 0,34 |
| % cerca de bares | Violencia intraf. | 0,44 | 0,43 |
| % lotes oscuros | Hurto a personas | -0,47 | -0,52 |
| % lotes oscuros | Lesiones | 0,15 | 0,05 |
| % lotes oscuros | Homicidios | 0,38 | 0,34 |
| % lotes oscuros | Violencia intraf. | 0,36 | 0,28 |
rho <- function(e, d) corr_todas %>% filter(entorno == e, delito == d) %>% pull(rho)
rho_perc_total <- cor(upz_f$pct_percibe_inseguridad, upz_f$tasa_total, method = "spearman")
rho_perc_vif <- cor(upz_f$pct_percibe_inseguridad, upz_f$tasa_violencia_intrafamiliar, method = "spearman")
rho_perc_hom <- cor(upz_f$pct_percibe_inseguridad, upz_f$tasa_homicidios, method = "spearman")
panel <- function(var_y, titulo, rho_val, color_pts) {
ggplot(upz_f, aes(x = pct_percibe_inseguridad, y = .data[[var_y]])) +
geom_point(color = color_pts, alpha = 0.75, size = 2.2) +
geom_smooth(method = "lm", se = FALSE, color = "grey30", linewidth = 0.8) +
scale_y_log10(labels = label_comma(big.mark = ".", decimal.mark = ",")) +
labs(subtitle = paste0(titulo, " · Spearman = ", num(rho_val, 2)),
x = "% de hogares que perciben inseguridad", y = "Tasa por 10.000 hab (log)")
}
(panel("tasa_total", "Total de denuncias", rho_perc_total, gris) +
panel("tasa_violencia_intrafamiliar", "Violencia intrafamiliar", rho_perc_vif, magenta)) +
plot_annotation(
title = "La gente se siente insegura donde hay violencia, no donde hay más denuncias",
caption = "Percepción: EM 2021. Denuncias: SDSCJ ene–jul 2023. 101 UPZ con 10.000 hab o más.",
theme = tema_clase)
La percepción de inseguridad no sigue al total de denuncias (ρ = -0,31, incluso negativa): las UPZ con más hurto por habitante, como Chicó Lago, tienen de las percepciones más bajas de la ciudad. En cambio, la percepción sí acompaña a la violencia intrafamiliar (ρ = 0,67) y a los homicidios (ρ = 0,58). Dos lecturas posibles, que los datos no permiten distinguir: (a) lo que la gente siente responde a la violencia y al deterioro del entorno, no al robo; (b) en las zonas centrales los hurtos los sufre la población flotante, que no es la que respondió la encuesta en su vivienda.
upz_f <- upz_f %>%
mutate(nivel_tasa = ntile(tasa_total, 4),
nivel_tasa = factor(nivel_tasa, 1:4,
labels = c("Q1\ntasa más baja", "Q2", "Q3", "Q4\ntasa más alta")))
med_cai <- upz_f %>% group_by(nivel_tasa) %>% summarise(mediana = median(min_cai_policia))
rho_cai <- cor(upz_f$min_cai_policia, upz_f$tasa_total, method = "spearman")
ggplot(upz_f, aes(x = nivel_tasa, y = min_cai_policia)) +
geom_boxplot(aes(fill = nivel_tasa == "Q4\ntasa más alta"), width = 0.55,
outlier.shape = NA, alpha = 0.9, color = "grey30") +
geom_jitter(width = 0.12, alpha = 0.5, size = 1.6, color = "grey25") +
geom_text(data = med_cai, aes(y = mediana, label = paste0(num(mediana, 1), " min")),
nudge_x = 0.42, size = 3.4, fontface = "bold", color = morado) +
scale_fill_manual(values = c(`TRUE` = magenta, `FALSE` = gris), guide = "none") +
scale_y_continuous(limits = c(0, NA)) +
labs(title = "Las UPZ con más denuncias por habitante tienen el CAI más cerca, no más lejos",
subtitle = paste0("Minutos caminando al CAI según cuartil de la tasa total · Spearman = ", num(rho_cai, 2)),
x = "Cuartil de la tasa total de denuncias", y = "Minutos caminando al CAI",
caption = "Fuente: EM 2021 (minutos), SDSCJ 2023 (tasas). 101 UPZ con 10.000 hab o más.")
En el cuartil con tasas más altas el CAI queda a una mediana de 15,6 minutos, contra 18,9 en el de tasas más bajas (ρ = -0,43). La lectura ingenua (“más CAI = más delito”) es absurda: lo más probable es la causalidad inversa (se ponen CAI donde hay más delito) y que en zonas centrales, compactas y con más servicios, todo queda más cerca. También es posible que tener la policía a mano facilite denunciar. Una vez más: correlación no es causalidad.
ranking <- upz_f %>%
slice_max(tasa_total, n = 10) %>%
mutate(posicion = row_number(),
tipo = if_else(tasa_homicidios > quantile(upz_f$tasa_homicidios, 0.75),
"También en el 25 % con más homicidios", "Predomina el hurto"))
n_violentas <- sum(ranking$tipo != "Predomina el hurto")
palabras <- c("una", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve", "diez")
ggplot(ranking, aes(x = tasa_total, y = fct_reorder(upz_nombre, tasa_total))) +
geom_col(aes(fill = tipo), width = 0.7) +
geom_text(aes(label = num(tasa_total)), hjust = -0.15, size = 3.4) +
geom_vline(xintercept = median(upz_f$tasa_total), linetype = "dashed", color = "grey40") +
scale_fill_manual(values = c("Predomina el hurto" = gris, "También en el 25 % con más homicidios" = magenta),
name = NULL) +
scale_x_continuous(expand = expansion(mult = c(0, 0.12)), labels = label_comma(big.mark = ".")) +
labs(title = paste0("El top 10 es el centro expandido de Bogotá; ", palabras[n_violentas], " UPZ suman hurto y homicidios"),
subtitle = paste0("Denuncias de los 10 delitos por 10.000 hab (ene–jul 2023) · línea punteada = mediana de Bogotá (",
num(median(upz_f$tasa_total)), ")"),
x = "Denuncias por 10.000 hab", y = NULL,
caption = "Magenta: además están en el cuartil superior de la tasa de homicidios (UPZ con 10.000 hab o más).") +
theme(legend.position = "top", legend.justification = "left", panel.grid.major.y = element_blank())
ranking %>%
transmute(`#` = posicion, UPZ = upz_nombre, Localidad = localidad,
Habitantes = num(poblacion),
`Tasa total` = num(tasa_total),
`Hurto personas` = num(tasa_hurto_personas),
Homicidios = num(tasa_homicidios, 2),
`Violencia intraf.` = num(tasa_violencia_intrafamiliar, 1),
`% percibe inseguridad` = pct(pct_percibe_inseguridad),
`Min. al CAI` = num(min_cai_policia, 1)) %>%
kable(align = "rllrrrrrrr", caption = "Top 10 UPZ por tasa total (por 10.000 hab, ene–jul 2023)") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE, font_size = 13) %>%
row_spec(which(ranking$tipo == "También en el 25 % con más homicidios"), bold = TRUE, color = magenta)
| # | UPZ | Localidad | Habitantes | Tasa total | Hurto personas | Homicidios | Violencia intraf. | % percibe inseguridad | Min. al CAI |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Las Nieves | Santa Fe | 17.203 | 2.344 | 1.488 | 5,23 | 32,5 | 60 % | 10,1 |
| 2 | Sagrado Corazon | Santa Fe | 11.247 | 1.518 | 851 | 1,78 | 232,1 | 60 % | 10,1 |
| 3 | La Sabana | Los Mártires | 38.856 | 1.400 | 852 | 6,18 | 84,4 | 83 % | 16,0 |
| 4 | Chico Lago | Chapinero | 38.608 | 1.291 | 842 | 0,26 | 15,3 | 21 % | 13,1 |
| 5 | Puente Aranda | Puente Aranda | 12.482 | 1.251 | 723 | 0,80 | 40,9 | 66 % | 17,2 |
| 6 | Chapinero | Chapinero | 26.491 | 1.095 | 700 | 0,75 | 18,1 | 50 % | 15,6 |
| 7 | Teusaquillo | Teusaquillo | 28.881 | 1.030 | 648 | 0,69 | 22,2 | 63 % | 10,4 |
| 8 | Galerias | Teusaquillo | 38.425 | 798 | 497 | 1,04 | 13,5 | 49 % | 12,8 |
| 9 | Los Alcazares | Barrios Unidos | 44.678 | 782 | 467 | 2,91 | 18,8 | 70 % | 12,8 |
| 10 | La Candelaria | Candelaria | 18.409 | 745 | 448 | 1,09 | 33,7 | 65 % | 12,7 |
El ranking confirma la geografía de P1 y P2: las diez UPZ están en Santa Fe, Los Mártires, Chapinero, Teusaquillo, Puente Aranda, Barrios Unidos y La Candelaria, el centro expandido. Pero no son iguales: Las Nieves, Sagrado Corazon, La Sabana, Los Alcazares combinan hurto altísimo con homicidios entre los más altos de la ciudad (el 25 % superior), mientras Chicó Lago tiene muchísimo hurto y casi nada de violencia, y la percepción de inseguridad más baja del top.
pal <- colorFactor(palette = c("#F1EAF4", "#D4BEDE", "#A57DB8", "#6E4290", morado),
levels = niveles, na.color = "#CCCCCC")
leaflet(mapa_upz) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(q_violencia), fillOpacity = 0.85, color = "white", weight = 0.6,
group = "Violencia",
label = ~paste0(upz_nombre, " · ", num(poblacion), " hab · violencia: ",
num(tasa_violenta, 1), " /10.000 · hurto: ", num(tasa_hurto_personas), " /10.000"),
highlightOptions = highlightOptions(weight = 2, color = magenta, bringToFront = TRUE)) %>%
addPolygons(
fillColor = ~pal(q_hurto), fillOpacity = 0.85, color = "white", weight = 0.6,
group = "Hurto a personas",
label = ~paste0(upz_nombre, " · ", num(poblacion), " hab · hurto: ",
num(tasa_hurto_personas), " /10.000 · violencia: ", num(tasa_violenta, 1), " /10.000"),
highlightOptions = highlightOptions(weight = 2, color = magenta, bringToFront = TRUE)) %>%
addLayersControl(baseGroups = c("Hurto a personas", "Violencia"),
options = layersControlOptions(collapsed = FALSE)) %>%
addLegend(pal = pal, values = niveles, position = "bottomright",
title = "Quintil de la tasa<br>(gris: < 10.000 hab)")
1. Primero, el denominador (P1). Contar denuncias es contar gente: las UPZ más pobladas del sur y occidente tienen más casos simplemente porque allí vive más gente. Al dividir por habitantes aparece otra ciudad, y aparece también una trampa: 11 UPZ casi deshabitadas tienen tasas absurdas. Nuestro propio EDA preliminar cayó en ella y “descubrió” que Ciudad Bolívar era la localidad con más hurto. Filtrar esas UPZ no es un detalle técnico: cambia las conclusiones.
2. El hurto vive donde está la riqueza y el movimiento (P2). Una vez corregido, el hurto a personas por habitante se concentra en el centro expandido y el norte, y crece con el salario de la UPZ (ρ = 0,71). Es un delito de oportunidad: donde hay comercio, oficinas, celulares y gente de paso.
3. La violencia vive en otra parte (P3). Homicidios, lesiones y violencia intrafamiliar dibujan otro mapa: el centro deteriorado (Las Nieves, La Sabana, Las Cruces) y el sur. En el cuartil más pobre la tasa de homicidios es 4,2 veces la del más rico. Son dos inseguridades, con víctimas, lugares y probablemente causas distintas.
4. El entorno acompaña a la violencia, no al robo (P4). Ollas y lotes oscuros se asocian con homicidios y violencia intrafamiliar, no con el hurto. Pero esas condiciones van juntas con la pobreza, así que no podemos decir cuál pesa. El alumbrado, contra lo esperado, no muestra relación protectora.
5. La gente siente la segunda inseguridad (P5). La percepción no sigue al total de denuncias (que domina el hurto), sino a la violencia. Los hogares del sur se sienten inseguros aunque se denuncien menos robos por habitante; los del norte, con más hurto, se sienten más seguros.
6. La policía está más cerca de donde más se denuncia (P6). Las UPZ con tasas más altas tienen el CAI más cerca. Más que un fracaso de la policía, sugiere que los CAI se ubican donde hay más delito y que la cercanía facilita denunciar.
La historia en una frase: en Bogotá el robo se concentra donde hay riqueza y movimiento, y la violencia donde hay pobreza y deterioro; la gente teme la segunda, y la cifra total de denuncias, dominada por el hurto, la esconde. Para la política pública esto importa: un solo indicador (“delitos por habitante”) pondría el foco en Chapinero y Teusaquillo y dejaría en segundo plano los barrios donde hay más homicidios y violencia en el hogar.
Lo que los datos muestran: patrones descriptivos claros y consistentes entre fuentes independientes (denuncias, encuesta y censo): la separación hurto/violencia aparece en el mapa, en los grupos de ingreso, en el entorno y en la percepción.
Lo que no muestran:
Conclusión. Preguntar “¿dónde es más inseguro Bogotá?” no tiene una sola respuesta. Depende de si contamos casos o tasas, robos o violencia, denuncias o percepción. La lección del EDA es tan metodológica como sustantiva: mirar el denominador antes de confiar en una tasa, y separar los tipos de delito antes de sumar, cambia la historia que cuentan los datos.
Datos: Secretaría Distrital de Seguridad, Convivencia y
Justicia (delitos ene–jul 2023); DANE (proyecciones de población 2023 y
Encuesta Multipropósito 2021); Datos Abiertos Bogotá (luminarias 2021).
Informe reproducible: tejer con
rmarkdown::render("Taller_Insights_UPZ.Rmd") desde la
carpeta del taller.