Este documento analiza el patrón espacial de los atropellos en la Región Metropolitana de Chile (datos de CONASET, 2018–2022) en tres niveles complementarios:
| Nivel | Pregunta que responde | Técnica |
|---|---|---|
| (A) Descriptivo | ¿Dónde se concentran los eventos? | Densidad de kernel (KDE) |
| (B) Inferencial | ¿El agrupamiento es real o azar? | Función K / L de Ripley |
| (C) Particional | ¿Qué grupos discretos existen? | DBSCAN / HDBSCAN |
Advertencia de interpretación (importante). Trabajamos con puntos crudos, sin normalizar por exposición (flujo peatonal o vehicular). Por lo tanto, una “zona caliente” indica dónde ocurren más atropellos en términos absolutos, no necesariamente dónde son más probables por persona o por vehículo. Un hot-spot puede reflejar simplemente mayor actividad urbana.
Cómo leer este documento. Cada técnica abre con una idea intuitiva y una guía de lectura del gráfico, y cierra con un ejercicio propuesto. El menú lateral (TOC) funciona como índice; el botón Code permite mostrar u ocultar el código de cada bloque.
spatstat (puntos + ventana de estudio).Ejecuta el siguiente bloque una sola vez antes de compilar (Knit) el documento, para asegurarte de tener todos los paquetes instalados. Está como
eval=FALSEpara que la compilación no intente instalarlos cada vez.
paquetes <- c("here", "readxl", "sf", "tidyverse", "mapview", "ggspatial",
"prettymapr", "rosm", # requeridos por annotation_map_tile()
"spatstat", "stars", "FNN", "dbscan", "raster")
faltantes <- paquetes[!paquetes %in% rownames(installed.packages())]
if (length(faltantes) > 0) install.packages(faltantes, dependencies = TRUE)Cargamos raster antes que
tidyverse para que dplyr::select() y
dplyr::filter() no queden enmascaradas.
library(sf)
library(mapview)
library(ggspatial)
library(spatstat)
library(stars)
library(FNN)
library(raster)
library(tidyverse) # se carga al final: dplyr gana los conflictos de nombresParámetros centralizados (evita “números mágicos” repartidos por el código):
CRS_UTM <- 32719 # UTM 19S, métrico, adecuado para distancias en la RM
SEED <- 2025 # semilla para reproducir las simulaciones
N_SIM <- 39 # simulaciones para las envolventes (más = más lento)
N_SUB_ENV <- 2000 # submuestra para la envolvente (rapidez)
EPS_DBSCAN <- 150 # radio (m) para DBSCAN; se justifica con el gráfico kNN
EPS_DBSCAN2 <- 500 # radio (m) del segundo modelo
MINPTS_2 <- 30 # mínimo de puntos del segundo modelo
MINPTS_HDB <- 40 # mínimo de puntos para HDBSCAN
set.seed(SEED)Construimos la ruta con here::here() (buena práctica:
trabajar dentro de un Proyecto de RStudio, sin setwd() ni
rutas absolutas). Los datos pueden descargarse en https://mapas-conaset.opendata.arcgis.com/.
# Buscamos el .RDS en ubicaciones habituales. Añade/ajusta tu ruta si hace falta.
candidatas <- c(
here::here("datos", "consolidado_atropellos.RDS"), # <proyecto>/datos/
here::here("consolidado_atropellos.RDS"), # raíz del proyecto
"consolidado_atropellos.RDS", # junto al .Rmd
file.path(getwd(), "consolidado_atropellos.RDS")
)
ruta_datos <- candidatas[file.exists(candidatas)][1]
if (is.na(ruta_datos)) {
stop("No encontré 'consolidado_atropellos.RDS'. Rutas probadas:\n",
paste(" -", candidatas, collapse = "\n"),
"\n\nColoca el archivo en una de esas carpetas (lo más limpio: ",
"una carpeta 'datos/' en la raíz del proyecto) o define 'ruta_datos' ",
"a mano. Carpeta de trabajo actual (getwd): ", getwd())
}
message("Leyendo datos desde: ", ruta_datos)
siniestros_rm <- readRDS(ruta_datos)
class(siniestros_rm)## [1] "sf" "data.frame"
## Coordinate Reference System:
## User input: WGS 84 / Pseudo-Mercator
## wkt:
## PROJCRS["WGS 84 / Pseudo-Mercator",
## BASEGEOGCRS["WGS 84",
## ENSEMBLE["World Geodetic System 1984 ensemble",
## MEMBER["World Geodetic System 1984 (Transit)"],
## MEMBER["World Geodetic System 1984 (G730)"],
## MEMBER["World Geodetic System 1984 (G873)"],
## MEMBER["World Geodetic System 1984 (G1150)"],
## MEMBER["World Geodetic System 1984 (G1674)"],
## MEMBER["World Geodetic System 1984 (G1762)"],
## MEMBER["World Geodetic System 1984 (G2139)"],
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]],
## ENSEMBLEACCURACY[2.0]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## ID["EPSG",4326]],
## CONVERSION["Popular Visualisation Pseudo-Mercator",
## METHOD["Popular Visualisation Pseudo Mercator",
## ID["EPSG",1024]],
## PARAMETER["Latitude of natural origin",0,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8801]],
## PARAMETER["Longitude of natural origin",0,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8802]],
## PARAMETER["False easting",0,
## LENGTHUNIT["metre",1],
## ID["EPSG",8806]],
## PARAMETER["False northing",0,
## LENGTHUNIT["metre",1],
## ID["EPSG",8807]]],
## CS[Cartesian,2],
## AXIS["easting (X)",east,
## ORDER[1],
## LENGTHUNIT["metre",1]],
## AXIS["northing (Y)",north,
## ORDER[2],
## LENGTHUNIT["metre",1]],
## USAGE[
## SCOPE["Web mapping and visualisation."],
## AREA["World between 85.06°S and 85.06°N."],
## BBOX[-85.06,-180,85.06,180]],
## ID["EPSG",3857]]
Primero observamos los datos en bruto sobre un mapa base de OpenStreetMap.
ggplot(data = siniestros_rm) +
annotation_map_tile(type = "osm", zoom = 12) +
geom_sf(inherit.aes = FALSE, color = "darkblue", size = 0.5, alpha = 0.6) +
labs(
title = "Atropellos en la Región Metropolitana (2018–2022)",
subtitle = "Datos crudos de localización (sin normalizar por exposición)",
caption = "Fuente: CONASET"
) +
theme_minimal()Versión interactiva (se incrusta como mapa navegable en el HTML):