Este documento implementa un análisis completo de patrones puntuales espaciales aplicado a datos de siniestralidad vial en Bogotá para el año 2023, siguiendo la metodología del libro “Spatial Point Patterns: Methodology and Applications with R” de Adrian Baddeley, Ege Rubak y Rolf Turner.
En este conjunto de datos, cada siniestro se representa como un punto ubicado en el espacio mediante sus coordenadas de longitud y latitud. Además, a cada punto se le asocian marcas, que son atributos propios del evento y permiten caracterizarlo: por ejemplo, la clase de accidente (choque, atropello, volcamiento), la gravedad del siniestro, la fecha y hora de ocurrencia, la localidad y los indicadores de participación o condiciones (p. ej., presencia de bicicleta, moto, peatón, embriaguez, exceso de velocidad o huecos). Por su parte, las covariables son variables que describen el entorno donde ocurren los eventos y se usan para explicar variaciones sistemáticas en la intensidad espacial; en este estudio, Localidad puede interpretarse como una covariable espacial de tipo areal (asignada por zonas), mientras que variables como Hora_Acc o Dia_Semana_Acc pueden incorporarse como covariables temporales si se extiende el análisis a un enfoque espacio–temporal.
1.1 Objetivos del análisis
Determinar si los siniestros viales están distribuidos aleatoriamente o presentan patrones espaciales
Identificar zonas de alta concentración
Analizar diferencias espaciales según tipo y gravedad de siniestros
Detectar escalas espaciales de agregación
Evaluar patrones temporales
2 Configuración Inicial
Code
# Cargar paquetes necesarioslibrary(spatstat) # Análisis de patrones puntualeslibrary(sf) # Manejo de datos espacialeslibrary(sp) # Clases espacialeslibrary(tidyverse) # Manipulación de datoslibrary(lubridate) # Manejo de fechaslibrary(viridis) # Paletas de coloreslibrary(knitr) # Tablaslibrary(kableExtra) # Tablas mejoradaslibrary(readxl)# Configurar opcionesoptions(scipen =999)
3 Carga y Preparación de Datos
Code
# Directorio del proyectosetwd("C:/Users/sarar/Documents/Maestría Estadística/Espacial/Patrones Puntuales")# Cargar datos de siniestrossiniestros <-read_excel("SIGAT_ANUARIO_2023.xlsx")# Exploración inicialcat("Dimensiones del dataset:", dim(siniestros), "\n")
# Limpiar datos: eliminar NAs en coordenadassiniestros_clean <- siniestros %>%filter(!is.na(Longitud) &!is.na(Latitud)) %>%filter(Longitud !=0& Latitud !=0) %>%mutate(Fecha_Acc =as.Date(Fecha_Acc, format ="%Y-%m-%d"))cat("\nDatos limpios:", nrow(siniestros_clean), "siniestros\n")
Datos limpios: 14106 siniestros
Code
# Resumen de coordenadassummary(siniestros_clean[, c("Longitud", "Latitud")]) %>%kable(caption ="Estadísticas de Coordenadas") %>%kable_styling(bootstrap_options =c("striped", "hover"))
Estadísticas de Coordenadas
Longitud
Latitud
Min. :-74.25
Min. :4.044
1st Qu.:-74.14
1st Qu.:4.596
Median :-74.11
Median :4.628
Mean :-74.11
Mean :4.636
3rd Qu.:-74.08
3rd Qu.:4.679
Max. :-74.01
Max. :4.823
4 Creación del Patrón Puntual
Para un análisis espacial correcto, es fundamental trabajar con un sistema de coordenadas proyectado en metros en lugar de coordenadas geográficas (grados). Esto permite:
Mediciones de distancia precisas
Cálculo correcto de densidades y áreas
Interpretación adecuada de las funciones K, L, G, F
Code
# (0) Preparar marcas (recomendado para análisis de marcas)siniestros_clean <- siniestros_clean %>%mutate(Localidad =factor(Localidad),Clase_Acc =factor(Clase_Acc),Gravedad_Indicador_Tradicional =factor(Gravedad_Indicador_Tradicional),Gravedad_indicador_30d =factor(Gravedad_indicador_30d),Dia_Semana_Acc =factor(Dia_Semana_Acc,levels =c("lunes","martes","miércoles","jueves","viernes","sábado","domingo")) )# 1) Convertir a objeto sf con coordenadas geográficas (WGS84)pts_sf <-st_as_sf( siniestros_clean,coords =c("Longitud", "Latitud"),crs =4326, # WGS84remove =FALSE)# 2) Proyectar a CRS en metros (MAGNA-SIRGAS / Colombia Bogota zone)# EPSG:3116 es apropiado para Bogotápts_m <-st_transform(pts_sf, 3116)# 3) Extraer coordenadas proyectadas en metrosxy <-st_coordinates(pts_m)x <- xy[, 1]y <- xy[, 2]cat("✓ Coordenadas extraídas:\n")
✓ Coordenadas extraídas:
Code
cat(" Rango X:", round(min(x)), "a", round(max(x)), "metros\n")
Rango X: 981149 a 1007238 metros
Code
cat(" Rango Y:", round(min(y)), "a", round(max(y)), "metros\n")
Rango Y: 938934 a 1025101 metros
Code
# 4) Crear ventana de observación (bounding box en metros)W <-owin(xrange =range(x), yrange =range(y))cat("✓ Ventana de observación creada\n")
if(n_dup >0) {cat(" Nota: Los duplicados pueden indicar múltiples siniestros en el mismo lugar.\n")cat(" Se pueden tratar con jitter o análisis de puntos múltiples.\n")}
Nota: Los duplicados pueden indicar múltiples siniestros en el mismo lugar.
Se pueden tratar con jitter o análisis de puntos múltiples.
Code
# 8) Resumen del patrón puntualcat("\n=== RESUMEN DEL PATRÓN PUNTUAL ===\n")
=== RESUMEN DEL PATRÓN PUNTUAL ===
Code
summary(ppp_siniestros)
Marked planar point pattern: 14106 points
Average intensity 0.000006274925 points per square unit
*Pattern contains duplicated points*
Coordinates are given to 10 decimal places
Mark variables: Localidad, Clase_Acc, Gravedad_Indicador_Tradicional,
Gravedad_indicador_30d, Hora_Acc, Dia_Semana_Acc
Summary:
Localidad Clase_Acc Gravedad_Indicador_Tradicional
KENNEDY :1969 Atropello :2760 Con Heridos:12447
ENGATIVA :1295 Caida de ocupante: 798 Con Muertos: 544
SUBA :1230 Choque :9998 Solo Daños : 1115
PUENTE ARANDA:1068 Incendio : 2
BOSA :1044 Otro : 146
FONTIBON : 874 Volcamiento : 402
(Other) :6626
Gravedad_indicador_30d Hora_Acc Dia_Semana_Acc
Con Heridos:12466 Min. : 0.00 lunes :1865
Con Muertos: 525 1st Qu.: 8.00 martes :2018
Solo Daños : 1115 Median :13.00 miércoles:2048
Mean :12.76 jueves :1975
3rd Qu.:18.00 viernes :2266
Max. :23.00 sábado :2228
domingo :1706
Window: rectangle = [981148.8, 1007237.8] x [938934.1, 1025100.6] units
(26090 x 86170 units)
Window area = 2247990000 square units
Code
# Reescalar a km para que distancias y sigma estén en kmppp_siniestros <-rescale(ppp_siniestros, s =1000, unitname =c("km","kilometres"))cat("\n✓ Patrón reescalado: unidades en km\n")
✓ Patrón reescalado: unidades en km
Code
cat(" Área ventana:", round(area(ppp_siniestros$window), 2), "km²\n")
# Información adicional del CRScat("\nInformación del sistema de coordenadas:\n")
Información del sistema de coordenadas:
Code
cat("CRS original (WGS84):", st_crs(pts_sf)$input, "\n")
CRS original (WGS84): EPSG:4326
Code
cat("CRS proyectado:", st_crs(pts_m)$input, "\n")
CRS proyectado: EPSG:3116
Code
cat("Unidades:", st_crs(pts_m)$units_gdal, "\n")
Unidades: metre
En esta sección se construye el patrón puntual que servirá como objeto central para el análisis con spatstat. Primero, las coordenadas originales en longitud/latitud (WGS84) se convierten a un objeto espacial sf y luego se proyectan al sistema EPSG:3116, cuya unidad es el metro. Tras la proyección, las coordenadas resultantes presentan rangos de aproximadamente 981,149 a 1,007,238 metros en el eje X y 938,934 a 1,025,101 metros en el eje Y, lo cual confirma que el patrón se encuentra en un marco consistente en unidades métricas.
A continuación, se define una ventana de observación 𝑊como un rectángulo envolvente que contiene todos los puntos proyectados. Esta ventana se usa como aproximación inicial del área donde se observa el proceso espacial. La ventana obtenida tiene un área de 2247.99 km², valor que corresponde al rectángulo mínimo que cubre todas las ubicaciones registradas; por ello, puede ser mayor que el área administrativa real de Bogotá y se interpreta como una ventana provisional, útil para comenzar el análisis exploratorio (más adelante puede reemplazarse por un polígono oficial del límite urbano o del distrito).
Con la ventana definida, se construye el objeto ppp denominado ppp_siniestros, que contiene 14106 eventos (siniestros) y sus ubicaciones. El resumen reporta un aviso de puntos duplicados: existen 307 siniestros con coordenadas idénticas, equivalente al 2.18% del total. Esto es esperable en datos de siniestralidad, ya que varios eventos pueden ocurrir en el mismo cruce o porque el geocodificador asigna la misma coordenada a una dirección repetida.
Finalmente, se calcula la intensidad global (densidad promedio de eventos por unidad de área). En este caso, la intensidad es 6.27 siniestros/km², que se obtiene como ( = n/|W| ), donde (n=14106) es el número de eventos y (|W|=2247.99) km² es el área de la ventana. Este valor es un promedio espacial: no implica que los siniestros estén uniformemente distribuidos, sino que sirve como referencia inicial antes de estudiar la inhomogeneidad (variación espacial de la intensidad) y las interacciones entre eventos.
Además, se incorporan marcas al patrón (6 variables: Localidad, Clase_Acc, dos indicadores de gravedad, Hora_Acc y Dia_Semana_Acc), lo cual permite análisis posteriores diferenciados por tipo de siniestro, gravedad y componentes temporales.
5 Análisis Exploratorio
5.1 Visualización Básica
Code
plot(ppp_siniestros, main ="Patrón de Siniestros Viales - Bogotá 2023",pch =16, cex =0.3, col ="white",xlab ="Coordenada X (km)",ylab ="Coordenada Y (km)")
5.2 Preparación de Ventana con Límites de Bogotá
Para mejorar la visualización sin afectar el análisis estadístico, crearemos un patrón auxiliar eliminando outliers extremos solo para gráficos.
cat("Porcentaje de outliers:", round(100*sum(outliers) /length(x), 2), "%\n")
Porcentaje de outliers: 0.1 %
Code
# IMPORTANTE: Crear patrón SOLO para visualización (no para análisis)if(sum(outliers) >0) { x_plot <- x[!outliers] y_plot <- y[!outliers] siniestros_plot <- siniestros_clean[!outliers, ]# Nueva ventana para visualización W_plot <-owin(xrange =range(x_plot), yrange =range(y_plot))# Patrón puntual solo para gráficos ppp_plot <-ppp(x = x_plot, y = y_plot, window = W_plot)marks(ppp_plot) <- siniestros_plot %>%select(Localidad, Clase_Acc, Gravedad_Indicador_Tradicional, Gravedad_indicador_30d, Hora_Acc, Dia_Semana_Acc)# Reescalar a km ppp_plot <-rescale(ppp_plot, s =1000, unitname =c("km", "kilometres"))cat("\n✓ Patrón para visualización creado:\n")cat(" Puntos:", npoints(ppp_plot), "\n")cat(" Área:", round(area(ppp_plot$window), 2), "km²\n")cat(" Intensidad aparente:", round(intensity(ppp_plot), 2), "siniestros/km²\n")cat("\n✓ Patrón original (para análisis) conservado:\n")cat(" Puntos:", npoints(ppp_siniestros), "\n")cat(" Área:", round(area(ppp_siniestros$window), 2), "km²\n")cat(" Intensidad real:", round(intensity(ppp_siniestros), 2), "siniestros/km²\n")} else {cat("\nNo se detectaron outliers significativos\n") ppp_plot <- ppp_siniestros}
✓ Patrón para visualización creado:
Puntos: 14092
Área: 857.23 km²
Intensidad aparente: 16.44 siniestros/km²
✓ Patrón original (para análisis) conservado:
Puntos: 14106
Área: 2247.99 km²
Intensidad real: 6.27 siniestros/km²
5.3 Mapa Básico de Siniestros
Code
# Mapa simple y limpioplot(ppp_plot,pch =20, # Punto pequeñocex =0.3, # Tamaño pequeñocols ="#E31A1C", # Rojo vibrantemain ="Distribución de Siniestros Viales - Bogotá 2023",xlab ="Coordenada X (km)",ylab ="Coordenada Y (km)",lwd =0.5)# Añadir recuadrobox(lwd =1.5)
Distribución espacial de siniestros viales en Bogotá 2023
El gráfico del patrón puntual usando la ventana rectangular (W) puede verse poco informativo, porque unos pocos puntos extremos “estiran” el rectángulo de observación y comprimen visualmente la nube principal de eventos. Para mejorar exclusivamente la visualización, se detectan coordenadas extremas mediante un criterio simple (valores más allá de 3 desviaciones estándar en (x) o (y)).
En este caso se identificaron 14 puntos (aprox. 0.1% del total). Al excluirlos se obtiene una ventana rectangular más ajustada, lo que permite apreciar mejor la forma general del patrón y las zonas de concentración.
5.4 Densidad Kernel General
Code
# Usar ppp_plot para visualizacióndens_general <-density(ppp_plot, sigma =1)plot(dens_general,main ="Densidad de Siniestros (Kernel, σ = 1 km)",col =hcl.colors(100, "YlOrRd", rev =TRUE),las =1)contour(dens_general, add =TRUE, col ="black", lwd =0.5,drawlabels =FALSE)plot(ppp_plot, add =TRUE, pch =20, cex =0.2, col =rgb(0, 0, 0, 0.1))
Mapa de calor de densidad de siniestros
Con el objetivo de explorar la distribución espacial de la siniestralidad vial, se estimó la intensidad espacial ((s)) del patrón puntual mediante un estimador kernel. En el contexto de procesos puntuales, ((s)) representa la tasa local esperada de eventos por unidad de área alrededor de la ubicación (s); por tanto, su estimación permite identificar zonas con mayor o menor concentración de siniestros de manera descriptiva.
Sea ({x_1,,x_n}) el conjunto de ubicaciones observadas. El estimador kernel de la intensidad puede escribirse como:
[ (s)={i=1}^{n} K{}(s-x_i), K_{}(u)=K!(), ]
donde (K()) es una función kernel (típicamente gaussiana en spatstat) y () es el parámetro de suavizado. En este análisis se utilizó (= 1) km (las coordenadas del patrón están expresadas en kilómetros, i.e., Unit of length: 1 km), lo cual corresponde a una escala urbana razonable para sintetizar la variación espacial de los siniestros sin amplificar excesivamente la micro-variabilidad a nivel de intersecciones.
El resultado es una superficie ((s)) representada como mapa de calor, sobre la cual se superponen curvas de nivel (isolíneas) para resaltar gradientes y “crestas” de intensidad, y los puntos originales del patrón para contrastar la estimación suavizada con las observaciones. Visualmente, la superficie de intensidad evidencia una marcada inhomogeneidad espacial, con zonas de mayor intensidad y áreas con intensidad menor dentro de la ventana de estudio.
Es importante notar que este mapa de densidad kernel constituye una herramienta exploratoria: describe la estructura espacial promedio a la escala (), pero no establece por sí mismo significancia estadística de agrupamiento. La evaluación inferencial del patrón (p. ej., bajo CSR o modelos inhomogéneos) se realiza posteriormente mediante funciones resumen y envolventes Monte Carlo.
5.5 Análisis por Localidad
Code
# Usar ppp_siniestros (patrón completo) para estadísticasfreq_localidad <-table(marks(ppp_siniestros)$Localidad) %>%as.data.frame() %>%rename(Localidad = Var1, Frecuencia = Freq) %>%arrange(desc(Frecuencia)) %>%mutate(Porcentaje =round(100* Frecuencia /sum(Frecuencia), 2),Acumulado =round(cumsum(Porcentaje), 2) )freq_localidad %>%head(10) %>%kable(caption ="Top 10 Localidades con Mayor Siniestralidad") %>%kable_styling(bootstrap_options =c("striped", "hover"))
Dado que el patrón puntual ppp_siniestros incluye la variable Localidad como marca (mark), se realizó un análisis descriptivo de frecuencia por categoría para identificar en qué localidades se concentra el mayor número de siniestros. Este análisis no incorpora distancias ni dependencia espacial, su propósito es resumir la composición del patrón por una variable administrativa y aportar contexto a los hallazgos espaciales.
En primer lugar, se contabilizó el número de siniestros por localidad mediante:
(_= #{,i: (x_i)=,}),
(= 100 / n),
(= {k }_k) (sobre localidades ordenadas de mayor a menor frecuencia).
Los resultados muestran que la siniestralidad se concentra fuertemente en un subconjunto de localidades. En particular, el Top 10 está encabezado por Kennedy (1,969; 13.96%), seguida por Engativá (1,295; 9.18%) y Suba (1,230; 8.72%). En conjunto, las tres localidades acumulan aproximadamente 31.86% de los siniestros registrados. Al ampliar al Top 10, se alcanza cerca de 71.99% del total, lo cual evidencia una concentración marcada del conteo en estas áreas.
Este patrón de concentración por localidades debe interpretarse con cautela: las frecuencias dependen tanto del tamaño del área como de la exposición (población, volumen de tráfico, cantidad de vías, centralidades, etc.).
5.7 Análisis por Clase de Accidente
Code
# Usar patrón completo para estadísticasfreq_clase <-table(marks(ppp_siniestros)$Clase_Acc) %>%as.data.frame() %>%rename(Clase = Var1, Frecuencia = Freq) %>%arrange(desc(Frecuencia)) %>%mutate(Porcentaje =round(100* Frecuencia /sum(Frecuencia), 2))freq_clase %>%kable(caption ="Distribución por Clase de Accidente") %>%kable_styling(bootstrap_options =c("striped", "hover"))
Distribución por Clase de Accidente
Clase
Frecuencia
Porcentaje
Choque
9998
70.88
Atropello
2760
19.57
Caida de ocupante
798
5.66
Volcamiento
402
2.85
Otro
146
1.04
Incendio
2
0.01
Code
# Usar ppp_plot para visualizaciónppp_por_clase <-split(ppp_plot, f =marks(ppp_plot)$Clase_Acc)plot(ppp_por_clase,pch =20,cex =0.4,main ="",cols =hcl.colors(length(ppp_por_clase), "Set 2") )
Distribución espacial por clase de accidente
La distribución por clase evidencia un fuerte desbalance en el tipo de siniestros registrados. En particular, Choque concentra la gran mayoría de los eventos con 9,998 casos (70.88%), seguido por Atropello con 2,760 casos (19.57%). A bastante distancia aparecen Caída de ocupante con 798 (5.66%) y Volcamiento con 402 (2.85%). Las categorías Otro (146; 1.04%) e Incendio (2; 0.01%) son muy poco frecuentes.
En el mapa por paneles (patrón separado por Clase_Acc), esta diferencia de tamaños se refleja directamente en la densidad visual de puntos: el panel de Choque aparece mucho más “cargado”, mientras que clases raras como Incendio muestran muy pocos eventos, por lo que cualquier aparente concentración debe interpretarse con cautela (puede deberse únicamente al bajo tamaño muestral).
Aun así, el gráfico sugiere dos conclusiones exploratorias importantes:
La ocurrencia de siniestros no parece homogénea en el espacio: incluso para las clases más frecuentes (Choque y Atropello) se aprecian zonas con alta concentración y otras con menor presencia, lo cual apunta a una intensidad espacial inhomogénea (primer orden).
La distribución espacial podría variar entre clases: aunque todas las clases comparten la misma ventana de observación, la comparación visual sugiere diferencias en la localización relativa de las concentraciones (por ejemplo, entre Choques y Atropellos). Esto motiva análisis posteriores con marcas, como estimación de intensidad por clase, mapas de riesgo relativo y comparaciones formales entre subpatrones.
Dado el desbalance de frecuencias, en secciones posteriores es razonable centrar la inferencia espacial principalmente en las clases Choque y Atropello, y tratar las clases muy raras (Incendio, y en menor medida Otro) como exploratorias.
5.8 Análisis por Gravedad
Code
# Usar patrón completo para estadísticasfreq_gravedad <-table(marks(ppp_siniestros)$Gravedad_Indicador_Tradicional) %>%as.data.frame() %>%rename(Gravedad = Var1, Frecuencia = Freq) %>%arrange(desc(Frecuencia)) %>%mutate(Porcentaje =round(100* Frecuencia /sum(Frecuencia), 2))freq_gravedad %>%kable(caption ="Distribución por Gravedad del Siniestro") %>%kable_styling(bootstrap_options =c("striped", "hover"))
Distribución por Gravedad del Siniestro
Gravedad
Frecuencia
Porcentaje
Con Heridos
12447
88.24
Solo Daños
1115
7.90
Con Muertos
544
3.86
Code
# Usar ppp_plot para visualizaciónppp_por_gravedad <-split(ppp_plot, f =marks(ppp_plot)$Gravedad_Indicador_Tradicional)plot(ppp_por_gravedad,pch =20,cex =0.5,main ="",cols =c("#2ECC71", "#E74C3C", "#F39C12"))
Distribución espacial por gravedad
A partir de la marca Gravedad_Indicador_Tradicional se construyó una tabla de frecuencias y porcentajes. Los resultados muestran una clara dominancia de eventos Con Heridos (12,447; 88.24%), seguidos por Solo Daños (1,115; 7.90%) y Con Muertos (544; 3.86%). Esta asimetría indica que la mayor parte de los registros corresponde a siniestros con afectación a personas, mientras que los eventos fatales representan una fracción menor del total, aunque de alto interés para priorización de intervenciones.
Para evaluar si los distintos niveles de gravedad exhiben patrones espaciales diferenciados, el patrón puntual se particionó por gravedad mediante split(ppp_plot, f = marks(...)$Gravedad_Indicador_Tradicional). Esto permite visualizar tres subpatrones (Con Heridos, Con Muertos, Solo Daños) sobre la misma ventana de observación. La visualización sugiere que los eventos de menor frecuencia (en particular, Con Muertos) presentan una distribución más dispersa y con menor densidad aparente, lo cual es consistente con su tamaño muestral reducido, mientras que Con Heridos reproduce más claramente las zonas de alta concentración observadas en el patrón total.
5.9 Análisis Temporal: Por Hora
Code
# Usar patrón completo para estadísticas temporaleshora_data <-marks(ppp_siniestros)$Hora_Acchist(hora_data,breaks =24,col =viridis(24),border ="white",main ="Distribución de Siniestros por Hora del Día",xlab ="Hora (0-23)",ylab ="Frecuencia",las =1)abline(h =mean(table(hora_data)), lty =2, col ="red", lwd =2)
Se estudió la marca Hora_Acc mediante un histograma con 24 barras (0–23). Adicionalmente, se trazó una línea horizontal en la frecuencia promedio por hora, ({n} = _{h=0}^{23} n_h), para identificar horas con siniestralidad por encima del nivel medio. El patrón horario muestra un comportamiento claramente no uniforme, con un pico alrededor de las 7:00 (909 siniestros), consistente con un aumento de exposición durante la hora de mayor movilidad (inicio de jornada). En general, se observa un incremento en franjas asociadas a desplazamientos laborales/estudiantiles y una disminución en horas de menor circulación.
5.10 Análisis Temporal: Por Día de la Semana
Code
# Usar patrón completodia_data <-marks(ppp_siniestros)$Dia_Semana_Accdia_tabla <-table(dia_data)barplot(dia_tabla,col =viridis(7),border ="white",main ="Distribución de Siniestros por Día de la Semana",las =2,ylab ="Número de Siniestros")text(x =seq_along(dia_tabla) *1.2-0.5,y = dia_tabla +50,labels = dia_tabla,pos =3,cex =0.9)
Finalmente, se evaluó Dia_Semana_Acc mediante un gráfico de barras con frecuencias por día. Los resultados evidencian variación sistemática a lo largo de la semana, con mayor número de siniestros hacia finales de semana y durante el fin de semana, mientras que el domingo presenta el menor conteo. Este patrón es compatible con cambios en la exposición (volumen de viajes), actividades recreativas y diferencias en comportamiento vial entre días hábiles y no hábiles.
6 Análisis de Primer Orden: Intensidad
El análisis de primer orden estudia la intensidad del proceso puntual, es decir, el número esperado de eventos por unidad de área.
6.1 Intensidad Global y Diagnóstico de Homogeneidad
Code
# =========================================================# 4) Análisis de Primer Orden: Intensidad# Nota: ppp_siniestros = patrón completo (en km)# ppp_plot = patrón para visualización (en km)# =========================================================# --- Intensidad global ---intensidad_global <-intensity(ppp_plot)cat("═══ INTENSIDAD GLOBAL ═══\n\n")
if(test_csr$p.value <0.05) {cat("Conclusión: Se RECHAZA CSR homogéneo (p < 0.05).\n")cat("Interpretación: la intensidad espacial NO es constante (patrón inhomogéneo).\n\n")} else {cat("Conclusión: No se rechaza CSR homogéneo.\n")cat("Interpretación: no hay evidencia suficiente contra intensidad constante\n")cat("(a esta escala de cuadrantes).\n\n")}
Conclusión: Se RECHAZA CSR homogéneo (p < 0.05).
Interpretación: la intensidad espacial NO es constante (patrón inhomogéneo).
Code
# --- Intensidad por cuadrante (mismo Q) ---intensidades_Q <-intensity(Q)cat("═══ INTENSIDAD POR CUADRANTE ═══\n\n")
donde \(n = 14{,}092\) eventos fueron observados en una ventana de área \(|W| = 857.23\) km².
Esto significa que, en promedio, en el área urbana de estudio ocurren aproximadamente 16.44 siniestros por cada km². Sin embargo, este valor es solo un promedio espacial y no implica que la intensidad sea uniforme en toda la zona urbana.
6.2.1 Evidencia de inhomogeneidad espacial
Al dividir la ventana en una rejilla de 10×10 cuadrantes, los conteos por celda muestran una variabilidad muy alta:
Bajo un proceso de Poisson homogéneo (CSR con intensidad constante \(\lambda\)), los conteos por cuadrante siguen una distribución Poisson y se esperaría aproximadamente \(V/M \approx 1\). El valor obtenido (\(V/M = 236.61\)) es enormemente mayor que 1, lo cual indica que los conteos por cuadrante son mucho más variables de lo que se esperaría bajo homogeneidad.
En términos de primer orden, esto sugiere con fuerza que la intensidad no es constante en el espacio. El patrón es claramente inhomogéneo: existen zonas con alta concentración de siniestros y otras con muy pocos o ningún evento.
6.3 Cuadrantes vacíos y validación con convex hull
Los 34 cuadrantes sin eventos (34%) corresponden principalmente a áreas con baja o nula cobertura vial dentro de la ventana (parques urbanos, zonas industriales, cerros orientales), lo cual contribuye a inflar el ratio \(V/M\).
Para evaluar si esta inhomogeneidad es un artefacto de la definición de la ventana o refleja variación real en el patrón urbano, se realizó una prueba adicional utilizando un convex hull (envolvente convexa) como ventana alternativa. El convex hull elimina automáticamente áreas periféricas y genera una ventana más ajustada (538 km²) que reduce los cuadrantes vacíos de 34 a 12 (-65%).
Resultado: Incluso con convex hull, el ratio \(V/M\) solo se redujo a 197.02 (-17%), permaneciendo muy por encima de 1. Esto confirma que la inhomogeneidad es genuina y no un artefacto de la ventana: refleja diferencias reales en la intensidad de siniestralidad entre zonas urbanas de Bogotá (por ejemplo, Kennedy con λ > 40 siniestros/km² versus zonas residenciales con λ < 10 siniestros/km²).
6.3.1 Test CSR (Chi-cuadrado en cuadrantes)
El test \(\chi^2\) de bondad de ajuste aplicado a los cuadrantes evalúa formalmente la hipótesis nula:
\[H_0: \lambda(s) = \lambda \text{ (constante) para todo } s \in W\]
Resultados:
Estadístico: \(X^2 = 23{,}424.15\)
Grados de libertad: \(gl = 99\)
p-valor: \(< 2.2 \times 10^{-16}\)
Conclusión estadística:
Se rechaza contundentemente la hipótesis de CSR homogéneo (\(p < 0.05\)). La evidencia es abrumadora contra la hipótesis de intensidad constante.
Esta evidencia es consistente con el diagnóstico \(V/M\): existe un componente fuerte de primer orden (inhomogeneidad), es decir, la intensidad \(\lambda(s)\) varía espacialmente de forma sistemática.
6.3.2 Intensidad local (por cuadrante)
Al convertir los conteos por cuadrante en intensidades locales \(\hat{\lambda}_i = N_i / |A_i|\), donde \(|A_i|\) es el área del cuadrante \(i\), se observa un rango muy amplio:
Intensidad mínima: 0 siniestros/km²
Intensidad máxima: 78.98 siniestros/km²
Rango: 78.98 siniestros/km²
Interpretación:
Esta diferencia muestra que existen cuadrantes con casi ausencia de eventos y otros con una intensidad muy superior al promedio global (16.44 siniestros/km²). En particular, algunos cuadrantes alcanzan intensidades de hasta 4.8 veces el promedio urbano, lo cual confirma la existencia de hot spots claramente diferenciados.
Esta heterogeneidad espacial marcada refuerza la conclusión de que el proceso generador de siniestros viales no es uniforme en el espacio urbano, sino que está fuertemente modulado por factores espaciales como la densidad de tráfico, el tipo de vías, la centralidad urbana y las características locales de cada zona.
6.3.3 Conclusión del análisis de primer orden
En conjunto, los resultados indican que:
El patrón de siniestros NO es compatible con CSR homogéneo
Test \(\chi^2\): rechazo con \(p < 10^{-16}\)
Ratio \(V/M = 236.61 \gg 1\)
Existe variación espacial marcada en la intensidad \(\lambda(s)\)
Rango de intensidades: 0 – 78.98 siniestros/km²
Factor de variación: hasta 4.8× el promedio urbano
Hot spots claramente identificables
La inhomogeneidad es genuina
Refleja diferencias reales entre zonas urbanas
No es principalmente un artefacto de la ventana de observación
Compatible con variación en factores de riesgo espaciales
6.3.4 Implicaciones para el análisis de segundo orden
El rechazo de CSR homogéneo tiene importantes consecuencias metodológicas para el análisis subsecuente:
Pregunta pendiente:
Dado que se confirmó inhomogeneidad (\(\lambda(s)\) no constante), la pregunta de segundo orden es: ¿Existe interacción espacial entre eventos más allá de la variación en intensidad?
Es decir, una vez “descontada” la inhomogeneidad de primer orden, ¿los eventos muestran:
Agregación (tendencia a ocurrir cerca de otros eventos)?
Regularidad (tendencia a evitarse mutuamente)?
Independencia (compatible con Poisson inhomogéneo)?
7 Análisis de Segundo Orden: Interacción Espacial
El análisis de segundo orden estudia la dependencia espacial entre eventos, es decir, si los siniestros tienden a ocurrir cerca unos de otros (agregación) o se evitan mutuamente (regularidad), más allá de la variación en intensidad ya detectada.
Dado que confirmamos inhomogeneidad (\(\lambda(s)\) no constante), usaremos funciones de segundo orden con corrección por inhomogeneidad.
7.1 Estimación de la intensidad espacial \(\lambda(s)\)
Primero estimamos la intensidad local mediante densidad kernel, que será usada para normalizar las funciones de segundo orden:
Code
# =========================================================# Estimación de intensidad espacial λ(s) con kernel# =========================================================cat("═══ ESTIMACIÓN DE INTENSIDAD λ(s) ═══\n\n")
═══ ESTIMACIÓN DE INTENSIDAD λ(s) ═══
Code
# Ancho de banda óptimo (regla de Scott para datos espaciales)sigma_bw <-bw.scott(ppp_plot)cat("Ancho de banda (σ):\n")
# Visualizar intensidad estimadaplot(lambda_hat,main =expression(paste("Intensidad Espacial Estimada ", lambda, "(s)")),col =hcl.colors(100, "YlOrRd", rev =TRUE),las =1)# Añadir contornoscontour(lambda_hat, add =TRUE, col ="black", lwd =0.8, drawlabels =FALSE)# Añadir puntos (semi-transparentes)plot(ppp_plot, add =TRUE, pch =20, cex =0.2, col =rgb(0,0,0,0.15))
Estimación suavizada de la intensidad espacial λ(s)
7.2 Interpretación de λ(s)
Este mapa muestra la intensidad local estimada mediante suavizado kernel. Las zonas rojas (alta λ) son áreas con mayor tasa esperada de siniestros, mientras que las zonas amarillas/blancas tienen menor intensidad. Esta superficie será usada para normalizar las funciones de segundo orden.
7.3 Función K de Ripley (Inhomogénea)
La función \(K_{\text{inhom}}(r)\) evalúa si los eventos están más agregados o más regulares de lo esperado bajo un proceso de Poisson inhomogéneo (independencia espacial con λ(s) variable).
Code
# =========================================================# Función K inhomogénea# =========================================================cat("═══ FUNCIÓN K INHOMOGÉNEA ═══\n\n")
═══ FUNCIÓN K INHOMOGÉNEA ═══
Code
# Calcular K inhomogénea con corrección de bordeK_inhom <-Kinhom(ppp_plot, lambda = lambda_hat,correction ="border",r =seq(0, 5, by =0.1)) # Hasta 5 kmcat("✓ K_inhom calculada\n\n")
✓ K_inhom calculada
Code
# Transformación L para estabilizar varianzaL_inhom <-Linhom(ppp_plot,lambda = lambda_hat,correction ="border",r =seq(0, 5, by =0.1))cat("✓ L_inhom calculada\n\n")
✓ L_inhom calculada
Code
# =========================================================# Solución: Forzar lambda_hat a ser estrictamente positivo# =========================================================cat("═══ CORRECCIÓN DE λ(s) PARA ENVOLVENTES ═══\n\n")
═══ CORRECCIÓN DE λ(s) PARA ENVOLVENTES ═══
Code
# Clonar lambda_hat y forzar valores positivoslambda_hat_pos <- lambda_hatlambda_hat_pos[lambda_hat_pos <=0] <-min(lambda_hat[lambda_hat >0])cat("✓ λ(s) corregida para ser estrictamente positiva\n")
par(mfrow =c(1, 2), mar =c(4, 4, 3, 1))# Panel 1: K inhomogéneaplot(env_K,main =expression(paste("Función ", K[inhom], "(r)")),xlab ="Distancia r (km)",ylab =expression(K[inhom](r)),legend =FALSE,lwd =2)# Línea teórica (Poisson inhomogéneo)lines(env_K$r, pi * env_K$r^2, lty =2, col ="red", lwd =2)legend("topleft",legend =c("Observado", "Teórico (Poisson inhom)", "Envolvente 99%"),lty =c(1, 2, 1),col =c("black", "red", "grey"),lwd =c(2, 2, 8),bty ="n")# Panel 2: L - rplot(env_L, . - r ~ r,main =expression(paste("Función ", L[inhom], "(r) - r")),xlab ="Distancia r (km)",ylab =expression(L[inhom](r) - r),legend =FALSE,lwd =2)abline(h =0, lty =2, col ="red", lwd =2)legend("topleft",legend =c("Observado", "Independencia", "Envolvente 99%"),lty =c(1, 2, 1),col =c("black", "red", "grey"),lwd =c(2, 2, 8),bty ="n")
Funciones K y L inhomogéneas con envolventes Monte Carlo
Code
par(mfrow =c(1, 1))
7.4 Interpretación de las funciones (K) y (L) inhomogéneas
En esta sección se evalúa la interacción espacial de segundo orden controlando la inhomogeneidad de primer orden (es decir, permitiendo que la intensidad ((s)) varíe en el espacio). Para ello se usan las funciones (K_{}(r)) y (L_{}(r)-r), junto con envolventes de Monte Carlo (99%) bajo un Poisson inhomogéneo.
7.4.1 Panel izquierdo: (K_{}(r))
Observaciones:
La curva observada (línea negra) se mantiene dentro de la envolvente (99%) para todo el rango de distancias analizado (0–5 km).
La curva observada está muy cercana a la referencia teórica del Poisson inhomogéneo (línea roja punteada).
No se presentan desviaciones sistemáticas (ni por encima ni por debajo) que sugieran interacción.
Conclusión (K):
No se detecta evidencia estadísticamente significativa de agregación ni de regularidad. El patrón es compatible con un proceso de Poisson inhomogéneo, es decir, eventos independientes con intensidad espacial variable.
7.4.2 Panel derecho: (L_{}(r) - r)
Observaciones:
La curva observada (línea negra) permanece dentro de la envolvente en todo el rango 0–5 km.
La curva oscila alrededor de (L_{}(r) - r ) (línea de referencia para independencia).
Puede observarse una ligera tendencia positiva en algunos tramos, pero no es significativa, ya que no supera los límites de la envolvente.
Conclusión (L):
No hay evidencia significativa de agregación a ninguna escala espacial considerada (0–5 km). Una vez controlada la variación de ((s)), el patrón resulta consistente con independencia espacial.
7.5 Conclusión del análisis de segundo orden
Resultado principal:
Una vez controlada la inhomogeneidad de la intensidad ((s)), no se observa interacción espacial significativa entre los siniestros.
Esto implica que:
La “agregación aparente” observada en mapas y densidades se explica por inhomogeneidad de primer orden (variación espacial de ((s))).
No hay evidencia de clustering de segundo orden: los siniestros no tienden a ocurrir cerca de otros siniestros más allá de lo esperado por la intensidad local.
El patrón es compatible con un Poisson inhomogéneo: eventos independientes modulados por factores espaciales que afectan ((s)).
7.5.1 Interpretación práctica: ¿de dónde vienen los hot spots?
Los hot spots no sugieren un “contagio espacial” entre siniestros ni clusters auto-organizados. Más bien reflejan condiciones del entorno que elevan la intensidad local ((s)), por ejemplo:
mayor densidad de tráfico,
tipos de vía (corredores principales vs. zonas residenciales),
intersecciones complejas,
diseño vial deficiente,
factores socioeconómicos y de uso del suelo.
Source Code
---title: "Análisis de Patrones Puntuales - Siniestralidad Vial Bogotá 2023"author: "Jairo Andrés Ángel, Sara Nathalia Reina"date: "`r Sys.Date()`"format: html: toc: true toc-depth: 3 toc-location: left number-sections: true code-fold: show code-tools: true theme: cosmo embed-resources: trueexecute: warning: false message: false---# IntroducciónEste documento implementa un análisis completo de patrones puntuales espaciales aplicado a datos de siniestralidad vial en Bogotá para el año 2023, siguiendo la metodología del libro **"Spatial Point Patterns: Methodology and Applications with R"** de Adrian Baddeley, Ege Rubak y Rolf Turner.En este conjunto de datos, cada siniestro se representa como un punto ubicado en el espacio mediante sus coordenadas de longitud y latitud. Además, a cada punto se le asocian marcas, que son atributos propios del evento y permiten caracterizarlo: por ejemplo, la clase de accidente (choque, atropello, volcamiento), la gravedad del siniestro, la fecha y hora de ocurrencia, la localidad y los indicadores de participación o condiciones (p. ej., presencia de bicicleta, moto, peatón, embriaguez, exceso de velocidad o huecos). Por su parte, las covariables son variables que describen el entorno donde ocurren los eventos y se usan para explicar variaciones sistemáticas en la intensidad espacial; en este estudio, Localidad puede interpretarse como una covariable espacial de tipo areal (asignada por zonas), mientras que variables como Hora_Acc o Dia_Semana_Acc pueden incorporarse como covariables temporales si se extiende el análisis a un enfoque espacio–temporal.## Objetivos del análisis- Determinar si los siniestros viales están distribuidos aleatoriamente o presentan patrones espaciales- Identificar zonas de alta concentración- Analizar diferencias espaciales según tipo y gravedad de siniestros- Detectar escalas espaciales de agregación- Evaluar patrones temporales# Configuración Inicial```{r setup}# Cargar paquetes necesarioslibrary(spatstat) # Análisis de patrones puntualeslibrary(sf) # Manejo de datos espacialeslibrary(sp) # Clases espacialeslibrary(tidyverse) # Manipulación de datoslibrary(lubridate) # Manejo de fechaslibrary(viridis) # Paletas de coloreslibrary(knitr) # Tablaslibrary(kableExtra) # Tablas mejoradaslibrary(readxl)# Configurar opcionesoptions(scipen = 999)```# Carga y Preparación de Datos```{r carga-datos}# Directorio del proyectosetwd("C:/Users/sarar/Documents/Maestría Estadística/Espacial/Patrones Puntuales")# Cargar datos de siniestrossiniestros <- read_excel("SIGAT_ANUARIO_2023.xlsx")# Exploración inicialcat("Dimensiones del dataset:", dim(siniestros), "\n")# Vista rápidahead(siniestros)``````{r exploracion}# Mostrar estructuraglimpse(siniestros)``````{r limpieza}# Limpiar datos: eliminar NAs en coordenadassiniestros_clean <- siniestros %>% filter(!is.na(Longitud) & !is.na(Latitud)) %>% filter(Longitud != 0 & Latitud != 0) %>% mutate(Fecha_Acc = as.Date(Fecha_Acc, format = "%Y-%m-%d"))cat("\nDatos limpios:", nrow(siniestros_clean), "siniestros\n")``````{r resumen-coords}# Resumen de coordenadassummary(siniestros_clean[, c("Longitud", "Latitud")]) %>% kable(caption = "Estadísticas de Coordenadas") %>% kable_styling(bootstrap_options = c("striped", "hover"))```# Creación del Patrón PuntualPara un análisis espacial correcto, es fundamental trabajar con un sistema de coordenadas proyectado en metros en lugar de coordenadas geográficas (grados). Esto permite:- Mediciones de distancia precisas- Cálculo correcto de densidades y áreas- Interpretación adecuada de las funciones K, L, G, F```{r crear-ppp}# (0) Preparar marcas (recomendado para análisis de marcas)siniestros_clean <- siniestros_clean %>% mutate( Localidad = factor(Localidad), Clase_Acc = factor(Clase_Acc), Gravedad_Indicador_Tradicional = factor(Gravedad_Indicador_Tradicional), Gravedad_indicador_30d = factor(Gravedad_indicador_30d), Dia_Semana_Acc = factor(Dia_Semana_Acc, levels = c("lunes","martes","miércoles","jueves", "viernes","sábado","domingo")) )# 1) Convertir a objeto sf con coordenadas geográficas (WGS84)pts_sf <- st_as_sf( siniestros_clean, coords = c("Longitud", "Latitud"), crs = 4326, # WGS84 remove = FALSE)# 2) Proyectar a CRS en metros (MAGNA-SIRGAS / Colombia Bogota zone)# EPSG:3116 es apropiado para Bogotápts_m <- st_transform(pts_sf, 3116)# 3) Extraer coordenadas proyectadas en metrosxy <- st_coordinates(pts_m)x <- xy[, 1]y <- xy[, 2]cat("✓ Coordenadas extraídas:\n")cat(" Rango X:", round(min(x)), "a", round(max(x)), "metros\n")cat(" Rango Y:", round(min(y)), "a", round(max(y)), "metros\n")# 4) Crear ventana de observación (bounding box en metros)W <- owin(xrange = range(x), yrange = range(y))cat("✓ Ventana de observación creada\n")cat(" Área:", round(area(W) / 1e6, 2), "km²\n")# 5) Crear objeto ppp (patrón puntual)ppp_siniestros <- ppp(x = x, y = y, window = W)cat("✓ Objeto ppp creado con", npoints(ppp_siniestros), "eventos\n")cat(" Intensidad global:", round(intensity(ppp_siniestros) * 1e6, 2), "siniestros/km²\n")# 6) Añadir marcas (atributos de cada evento)marks(ppp_siniestros) <- siniestros_clean %>% select( Localidad, Clase_Acc, Gravedad_Indicador_Tradicional, Gravedad_indicador_30d, Hora_Acc, Dia_Semana_Acc )cat("✓ Marcas añadidas:", ncol(marks(ppp_siniestros)), "variables\n\n")# 7) Diagnóstico: puntos duplicados (misma coordenada exacta)n_dup <- sum(duplicated(cbind(x, y)))cat("⚠ Puntos duplicados (misma coordenada):", n_dup, "\n")cat(" Proporción duplicada:", round(100 * n_dup / npoints(ppp_siniestros), 2), "%\n")if(n_dup > 0) { cat(" Nota: Los duplicados pueden indicar múltiples siniestros en el mismo lugar.\n") cat(" Se pueden tratar con jitter o análisis de puntos múltiples.\n")}# 8) Resumen del patrón puntualcat("\n=== RESUMEN DEL PATRÓN PUNTUAL ===\n")summary(ppp_siniestros)# Reescalar a km para que distancias y sigma estén en kmppp_siniestros <- rescale(ppp_siniestros, s = 1000, unitname = c("km","kilometres"))cat("\n✓ Patrón reescalado: unidades en km\n")cat(" Área ventana:", round(area(ppp_siniestros$window), 2), "km²\n")cat(" Intensidad global:", round(intensity(ppp_siniestros), 2), "siniestros/km²\n")``````{r info-coords}# Información adicional del CRScat("\nInformación del sistema de coordenadas:\n")cat("CRS original (WGS84):", st_crs(pts_sf)$input, "\n")cat("CRS proyectado:", st_crs(pts_m)$input, "\n")cat("Unidades:", st_crs(pts_m)$units_gdal, "\n")```En esta sección se construye el patrón puntual que servirá como objeto central para el análisis con spatstat. Primero, las coordenadas originales en longitud/latitud (WGS84) se convierten a un objeto espacial sf y luego se proyectan al sistema EPSG:3116, cuya unidad es el metro. Tras la proyección, las coordenadas resultantes presentan rangos de aproximadamente 981,149 a 1,007,238 metros en el eje X y 938,934 a 1,025,101 metros en el eje Y, lo cual confirma que el patrón se encuentra en un marco consistente en unidades métricas.A continuación, se define una ventana de observación 𝑊como un rectángulo envolvente que contiene todos los puntos proyectados. Esta ventana se usa como aproximación inicial del área donde se observa el proceso espacial. La ventana obtenida tiene un área de 2247.99 km², valor que corresponde al rectángulo mínimo que cubre todas las ubicaciones registradas; por ello, puede ser mayor que el área administrativa real de Bogotá y se interpreta como una ventana provisional, útil para comenzar el análisis exploratorio (más adelante puede reemplazarse por un polígono oficial del límite urbano o del distrito).Con la ventana definida, se construye el objeto ppp denominado ppp_siniestros, que contiene 14106 eventos (siniestros) y sus ubicaciones. El resumen reporta un aviso de puntos duplicados: existen 307 siniestros con coordenadas idénticas, equivalente al 2.18% del total. Esto es esperable en datos de siniestralidad, ya que varios eventos pueden ocurrir en el mismo cruce o porque el geocodificador asigna la misma coordenada a una dirección repetida.Finalmente, se calcula la intensidad global (densidad promedio de eventos por unidad de área). En este caso, la intensidad es **6.27 siniestros/km²**, que se obtiene como \( \hat{\lambda} = n/|W| \), donde \(n=14106\) es el número de eventos y \(|W|=2247.99\) km² es el área de la ventana. Este valor es un promedio espacial: no implica que los siniestros estén uniformemente distribuidos, sino que sirve como referencia inicial antes de estudiar la **inhomogeneidad** (variación espacial de la intensidad) y las **interacciones** entre eventos.Además, se incorporan **marcas** al patrón (6 variables: `Localidad`, `Clase_Acc`, dos indicadores de gravedad, `Hora_Acc` y `Dia_Semana_Acc`), lo cual permite análisis posteriores diferenciados por tipo de siniestro, gravedad y componentes temporales.# Análisis Exploratorio## Visualización Básica```{r patron-basico, fig.width=10, fig.height=8}plot(ppp_siniestros, main = "Patrón de Siniestros Viales - Bogotá 2023", pch = 16, cex = 0.3, col = "white", xlab = "Coordenada X (km)", ylab = "Coordenada Y (km)")```## Preparación de Ventana con Límites de BogotáPara mejorar la visualización sin afectar el análisis estadístico, crearemos un patrón auxiliar eliminando outliers extremos solo para gráficos.```{r ventana-ajustada}# Detectar outliers extremos (más allá de 3 desviaciones estándar)outliers_x <- abs(scale(x)) > 3outliers_y <- abs(scale(y)) > 3outliers <- outliers_x | outliers_ycat("Outliers detectados:", sum(outliers), "de", length(x), "puntos\n")cat("Porcentaje de outliers:", round(100 * sum(outliers) / length(x), 2), "%\n")# IMPORTANTE: Crear patrón SOLO para visualización (no para análisis)if(sum(outliers) > 0) { x_plot <- x[!outliers] y_plot <- y[!outliers] siniestros_plot <- siniestros_clean[!outliers, ] # Nueva ventana para visualización W_plot <- owin(xrange = range(x_plot), yrange = range(y_plot)) # Patrón puntual solo para gráficos ppp_plot <- ppp(x = x_plot, y = y_plot, window = W_plot) marks(ppp_plot) <- siniestros_plot %>% select(Localidad, Clase_Acc, Gravedad_Indicador_Tradicional, Gravedad_indicador_30d, Hora_Acc, Dia_Semana_Acc) # Reescalar a km ppp_plot <- rescale(ppp_plot, s = 1000, unitname = c("km", "kilometres")) cat("\n✓ Patrón para visualización creado:\n") cat(" Puntos:", npoints(ppp_plot), "\n") cat(" Área:", round(area(ppp_plot$window), 2), "km²\n") cat(" Intensidad aparente:", round(intensity(ppp_plot), 2), "siniestros/km²\n") cat("\n✓ Patrón original (para análisis) conservado:\n") cat(" Puntos:", npoints(ppp_siniestros), "\n") cat(" Área:", round(area(ppp_siniestros$window), 2), "km²\n") cat(" Intensidad real:", round(intensity(ppp_siniestros), 2), "siniestros/km²\n")} else { cat("\nNo se detectaron outliers significativos\n") ppp_plot <- ppp_siniestros}```## Mapa Básico de Siniestros```{r mapa-basico, fig.width=10, fig.height=8, fig.cap="Distribución espacial de siniestros viales en Bogotá 2023"}# Mapa simple y limpioplot(ppp_plot, pch = 20, # Punto pequeño cex = 0.3, # Tamaño pequeño cols = "#E31A1C", # Rojo vibrante main = "Distribución de Siniestros Viales - Bogotá 2023", xlab = "Coordenada X (km)", ylab = "Coordenada Y (km)", lwd = 0.5)# Añadir recuadrobox(lwd = 1.5)```El gráfico del patrón puntual usando la ventana rectangular \(W\) puede verse poco informativo, porque unos pocos puntos extremos “estiran” el rectángulo de observación y comprimen visualmente la nube principal de eventos. Para mejorar **exclusivamente la visualización**, se detectan coordenadas extremas mediante un criterio simple (valores más allá de 3 desviaciones estándar en \(x\) o \(y\)).En este caso se identificaron **14 puntos** (aprox. **0.1%** del total). Al excluirlos se obtiene una ventana rectangular más ajustada, lo que permite apreciar mejor la forma general del patrón y las zonas de concentración.## Densidad Kernel General```{r densidad-general, fig.width=10, fig.height=6, fig.cap="Mapa de calor de densidad de siniestros"}# Usar ppp_plot para visualizacióndens_general <- density(ppp_plot, sigma = 1)plot(dens_general, main = "Densidad de Siniestros (Kernel, σ = 1 km)", col = hcl.colors(100, "YlOrRd", rev = TRUE), las = 1)contour(dens_general, add = TRUE, col = "black", lwd = 0.5, drawlabels = FALSE)plot(ppp_plot, add = TRUE, pch = 20, cex = 0.2, col = rgb(0, 0, 0, 0.1))```Con el objetivo de explorar la distribución espacial de la siniestralidad vial, se estimó la **intensidad espacial** \(\lambda(s)\) del patrón puntual mediante un **estimador kernel**. En el contexto de procesos puntuales, \(\lambda(s)\) representa la tasa local esperada de eventos por unidad de área alrededor de la ubicación \(s\); por tanto, su estimación permite identificar zonas con mayor o menor concentración de siniestros de manera descriptiva.Sea \(\{x_1,\dots,x_n\}\) el conjunto de ubicaciones observadas. El estimador kernel de la intensidad puede escribirse como:\[\widehat{\lambda}(s)=\sum_{i=1}^{n} K_{\sigma}(s-x_i),\qquadK_{\sigma}(u)=\frac{1}{\sigma^2}K\!\left(\frac{u}{\sigma}\right),\]donde \(K(\cdot)\) es una función kernel (típicamente gaussiana en `spatstat`) y \(\sigma\) es el parámetro de suavizado. En este análisis se utilizó \(\sigma = 1\) km (las coordenadas del patrón están expresadas en kilómetros, i.e., *Unit of length: 1 km*), lo cual corresponde a una escala urbana razonable para sintetizar la variación espacial de los siniestros sin amplificar excesivamente la micro-variabilidad a nivel de intersecciones.El resultado es una superficie \(\widehat{\lambda}(s)\) representada como mapa de calor, sobre la cual se superponen curvas de nivel (isolíneas) para resaltar gradientes y “crestas” de intensidad, y los puntos originales del patrón para contrastar la estimación suavizada con las observaciones. Visualmente, la superficie de intensidad evidencia una marcada inhomogeneidad espacial, con zonas de mayor intensidad y áreas con intensidad menor dentro de la ventana de estudio. Es importante notar que este mapa de densidad kernel constituye una herramienta exploratoria: describe la estructura espacial promedio a la escala \(\sigma\), pero no establece por sí mismo significancia estadística de agrupamiento. La evaluación inferencial del patrón (p. ej., bajo CSR o modelos inhomogéneos) se realiza posteriormente mediante funciones resumen y envolventes Monte Carlo.## Análisis por Localidad```{r tabla-localidad}# Usar ppp_siniestros (patrón completo) para estadísticasfreq_localidad <- table(marks(ppp_siniestros)$Localidad) %>% as.data.frame() %>% rename(Localidad = Var1, Frecuencia = Freq) %>% arrange(desc(Frecuencia)) %>% mutate( Porcentaje = round(100 * Frecuencia / sum(Frecuencia), 2), Acumulado = round(cumsum(Porcentaje), 2) )freq_localidad %>% head(10) %>% kable(caption = "Top 10 Localidades con Mayor Siniestralidad") %>% kable_styling(bootstrap_options = c("striped", "hover"))``````{r grafico-localidad, fig.width=10, fig.height=6}freq_localidad %>% head(10) %>% ggplot(aes(x = reorder(Localidad, Frecuencia), y = Frecuencia)) + geom_col(fill = "#E31A1C", alpha = 0.8) + geom_text(aes(label = Frecuencia), hjust = -0.2, size = 3) + coord_flip() + labs( title = "Siniestros por Localidad - Top 10", x = "Localidad", y = "Número de Siniestros" ) + theme_minimal(base_size = 12) + theme( plot.title = element_text(face = "bold", size = 14), panel.grid.major.y = element_blank(), panel.grid.minor = element_blank() ) + ylim(0, max(freq_localidad$Frecuencia) * 1.1)```## Análisis por localidad (marcas)Dado que el patrón puntual `ppp_siniestros` incluye la variable **Localidad** como **marca** (mark), se realizó un análisis descriptivo de frecuencia por categoría para identificar en qué localidades se concentra el mayor número de siniestros. Este análisis no incorpora distancias ni dependencia espacial, su propósito es resumir la composición del patrón por una variable administrativa y aportar contexto a los hallazgos espaciales.En primer lugar, se contabilizó el número de siniestros por localidad mediante:- \(\text{Frecuencia}_\ell = \#\{\,i:\ \text{Localidad}(x_i)=\ell\,\}\),- \(\text{Porcentaje}_\ell = 100 \times \text{Frecuencia}_\ell / n\),- \(\text{Acumulado}_\ell = \sum_{k \le \ell}\text{Porcentaje}_k\) (sobre localidades ordenadas de mayor a menor frecuencia).Los resultados muestran que la siniestralidad se concentra fuertemente en un subconjunto de localidades. En particular, el Top 10 está encabezado por **Kennedy** (1,969; 13.96%), seguida por **Engativá** (1,295; 9.18%) y **Suba** (1,230; 8.72%). En conjunto, las tres localidades acumulan aproximadamente **31.86%** de los siniestros registrados. Al ampliar al Top 10, se alcanza cerca de **71.99%** del total, lo cual evidencia una concentración marcada del conteo en estas áreas.Este patrón de concentración por localidades debe interpretarse con cautela: las frecuencias dependen tanto del **tamaño del área** como de la **exposición** (población, volumen de tráfico, cantidad de vías, centralidades, etc.).## Análisis por Clase de Accidente```{r tabla-clase}# Usar patrón completo para estadísticasfreq_clase <- table(marks(ppp_siniestros)$Clase_Acc) %>% as.data.frame() %>% rename(Clase = Var1, Frecuencia = Freq) %>% arrange(desc(Frecuencia)) %>% mutate(Porcentaje = round(100 * Frecuencia / sum(Frecuencia), 2))freq_clase %>% kable(caption = "Distribución por Clase de Accidente") %>% kable_styling(bootstrap_options = c("striped", "hover"))``````{r mapa-clase, fig.width=14, fig.height=10, fig.cap="Distribución espacial por clase de accidente"}# Usar ppp_plot para visualizaciónppp_por_clase <- split(ppp_plot, f = marks(ppp_plot)$Clase_Acc)plot(ppp_por_clase, pch = 20, cex = 0.4, main = "", cols = hcl.colors(length(ppp_por_clase), "Set 2") )```La distribución por clase evidencia un fuerte desbalance en el tipo de siniestros registrados. En particular, Choque concentra la gran mayoría de los eventos con **9,998 casos (70.88%)**, seguido por Atropello con **2,760 casos (19.57%)**. A bastante distancia aparecen Caída de ocupante con **798 (5.66%)** y Volcamiento con **402 (2.85%)**. Las categorías Otro (**146; 1.04%**) e Incendio (**2; 0.01%**) son muy poco frecuentes.En el **mapa por paneles** (patrón separado por `Clase_Acc`), esta diferencia de tamaños se refleja directamente en la densidad visual de puntos: el panel de **Choque** aparece mucho más “cargado”, mientras que clases raras como **Incendio** muestran muy pocos eventos, por lo que cualquier aparente concentración debe interpretarse con cautela (puede deberse únicamente al bajo tamaño muestral).Aun así, el gráfico sugiere dos conclusiones exploratorias importantes:- La ocurrencia de siniestros **no parece homogénea** en el espacio: incluso para las clases más frecuentes (Choque y Atropello) se aprecian **zonas con alta concentración** y otras con menor presencia, lo cual apunta a una **intensidad espacial inhomogénea** (primer orden).- La distribución espacial **podría variar entre clases**: aunque todas las clases comparten la misma ventana de observación, la comparación visual sugiere diferencias en la localización relativa de las concentraciones (por ejemplo, entre Choques y Atropellos). Esto motiva análisis posteriores con **marcas**, como estimación de intensidad por clase, mapas de riesgo relativo y comparaciones formales entre subpatrones.Dado el desbalance de frecuencias, en secciones posteriores es razonable centrar la inferencia espacial principalmente en las clases **Choque** y **Atropello**, y tratar las clases muy raras (**Incendio**, y en menor medida **Otro**) como exploratorias.## Análisis por Gravedad```{r tabla-gravedad}# Usar patrón completo para estadísticasfreq_gravedad <- table(marks(ppp_siniestros)$Gravedad_Indicador_Tradicional) %>% as.data.frame() %>% rename(Gravedad = Var1, Frecuencia = Freq) %>% arrange(desc(Frecuencia)) %>% mutate(Porcentaje = round(100 * Frecuencia / sum(Frecuencia), 2))freq_gravedad %>% kable(caption = "Distribución por Gravedad del Siniestro") %>% kable_styling(bootstrap_options = c("striped", "hover"))``````{r mapa-gravedad, fig.width=12, fig.height=8, fig.cap="Distribución espacial por gravedad"}# Usar ppp_plot para visualizaciónppp_por_gravedad <- split(ppp_plot, f = marks(ppp_plot)$Gravedad_Indicador_Tradicional)plot(ppp_por_gravedad, pch = 20, cex = 0.5, main = "", cols = c("#2ECC71", "#E74C3C", "#F39C12"))```A partir de la marca `Gravedad_Indicador_Tradicional` se construyó una tabla de frecuencias y porcentajes. Los resultados muestran una clara dominancia de eventos **Con Heridos** (12,447; 88.24%), seguidos por **Solo Daños** (1,115; 7.90%) y **Con Muertos** (544; 3.86%). Esta asimetría indica que la mayor parte de los registros corresponde a siniestros con afectación a personas, mientras que los eventos fatales representan una fracción menor del total, aunque de alto interés para priorización de intervenciones.Para evaluar si los distintos niveles de gravedad exhiben patrones espaciales diferenciados, el patrón puntual se **particionó** por gravedad mediante `split(ppp_plot, f = marks(...)$Gravedad_Indicador_Tradicional)`. Esto permite visualizar tres subpatrones (Con Heridos, Con Muertos, Solo Daños) sobre la misma ventana de observación. La visualización sugiere que los eventos de menor frecuencia (en particular, **Con Muertos**) presentan una distribución más dispersa y con menor densidad aparente, lo cual es consistente con su tamaño muestral reducido, mientras que **Con Heridos** reproduce más claramente las zonas de alta concentración observadas en el patrón total.## Análisis Temporal: Por Hora```{r analisis-hora, fig.width=12, fig.height=5}# Usar patrón completo para estadísticas temporaleshora_data <- marks(ppp_siniestros)$Hora_Acchist(hora_data, breaks = 24, col = viridis(24), border = "white", main = "Distribución de Siniestros por Hora del Día", xlab = "Hora (0-23)", ylab = "Frecuencia", las = 1)abline(h = mean(table(hora_data)), lty = 2, col = "red", lwd = 2)hora_tabla <- table(hora_data)hora_max <- as.numeric(names(which.max(hora_tabla)))cat("\nHora pico:", hora_max, "horas con", max(hora_tabla), "siniestros\n")```Se estudió la marca `Hora_Acc` mediante un histograma con 24 barras (0–23). Adicionalmente, se trazó una línea horizontal en la frecuencia promedio por hora, \(\bar{n} = \frac{1}{24}\sum_{h=0}^{23} n_h\), para identificar horas con siniestralidad por encima del nivel medio. El patrón horario muestra un comportamiento claramente no uniforme, con un **pico** alrededor de las **7:00** (909 siniestros), consistente con un aumento de exposición durante la hora de mayor movilidad (inicio de jornada). En general, se observa un incremento en franjas asociadas a desplazamientos laborales/estudiantiles y una disminución en horas de menor circulación.## Análisis Temporal: Por Día de la Semana```{r analisis-dia, fig.width=10, fig.height=6}# Usar patrón completodia_data <- marks(ppp_siniestros)$Dia_Semana_Accdia_tabla <- table(dia_data)barplot(dia_tabla, col = viridis(7), border = "white", main = "Distribución de Siniestros por Día de la Semana", las = 2, ylab = "Número de Siniestros")text(x = seq_along(dia_tabla) * 1.2 - 0.5, y = dia_tabla + 50, labels = dia_tabla, pos = 3, cex = 0.9)```Finalmente, se evaluó `Dia_Semana_Acc` mediante un gráfico de barras con frecuencias por día. Los resultados evidencian variación sistemática a lo largo de la semana, con mayor número de siniestros hacia finales de semana y durante el fin de semana, mientras que el domingo presenta el menor conteo. Este patrón es compatible con cambios en la exposición (volumen de viajes), actividades recreativas y diferencias en comportamiento vial entre días hábiles y no hábiles.# Análisis de Primer Orden: IntensidadEl análisis de primer orden estudia la **intensidad** del proceso puntual, es decir, el número esperado de eventos por unidad de área.## Intensidad Global y Diagnóstico de Homogeneidad```{r punto4-primer-orden, message=FALSE, warning=FALSE}# =========================================================# 4) Análisis de Primer Orden: Intensidad# Nota: ppp_siniestros = patrón completo (en km)# ppp_plot = patrón para visualización (en km)# =========================================================# --- Intensidad global ---intensidad_global <- intensity(ppp_plot)cat("═══ INTENSIDAD GLOBAL ═══\n\n")cat("Intensidad:", round(intensidad_global, 2), "siniestros/km²\n")cat("Total de eventos:", npoints(ppp_plot), "\n")cat("Área de estudio:", round(area(ppp_plot$window), 2), "km²\n\n")# --- Conteo por cuadrantes (10x10) + diagnóstico V/M ---Q <- quadratcount(ppp_plot, nx = 10, ny = 10)conteos <- as.vector(Q)conteos <- conteos[!is.na(conteos)]vm <- var(conteos) / mean(conteos)cat("═══ CUADRANTES (10×10) ═══\n\n")cat("Conteo mínimo:", min(conteos), "\n")cat("Conteo máximo:", max(conteos), "\n")cat("Conteo medio:", round(mean(conteos), 2), "\n")cat("Varianza:", round(var(conteos), 2), "\n")cat("Relación Varianza/Media (V/M):", round(vm, 2), "\n\n")cat("Interpretación (primer orden):\n")cat("- Bajo CSR homogéneo (Poisson con intensidad constante), se esperaría V/M ≈ 1.\n")cat("- Un V/M muy grande sugiere fuerte INHOMOGENEIDAD (intensidad no constante).\n\n")# --- Test CSR (Chi-cuadrado sobre cuadrantes) ---test_csr <- quadrat.test(ppp_plot, nx = 10, ny = 10)cat("═══ TEST CSR (CHI-CUADRADO) ═══\n\n")cat("X²:", round(test_csr$statistic, 2), "\n")cat("gl:", test_csr$parameter, "\n")cat("p-valor:", format.pval(test_csr$p.value, digits = 4), "\n\n")if(test_csr$p.value < 0.05) { cat("Conclusión: Se RECHAZA CSR homogéneo (p < 0.05).\n") cat("Interpretación: la intensidad espacial NO es constante (patrón inhomogéneo).\n\n")} else { cat("Conclusión: No se rechaza CSR homogéneo.\n") cat("Interpretación: no hay evidencia suficiente contra intensidad constante\n") cat("(a esta escala de cuadrantes).\n\n")}# --- Intensidad por cuadrante (mismo Q) ---intensidades_Q <- intensity(Q)cat("═══ INTENSIDAD POR CUADRANTE ═══\n\n")cat("Intensidad mínima:", round(min(intensidades_Q, na.rm = TRUE), 2), "siniestros/km²\n")cat("Intensidad máxima:", round(max(intensidades_Q, na.rm = TRUE), 2), "siniestros/km²\n")cat("Rango:", round(max(intensidades_Q, na.rm = TRUE) - min(intensidades_Q, na.rm = TRUE), 2), "siniestros/km²\n")```## Interpretación de resultados (Primer orden)A partir del patrón puntual observado en la **zona urbana de Bogotá** (ventana ajustada sin outliers extremos), se estimó la **intensidad global** como:$$\hat{\lambda} = \frac{n}{|W|} = \frac{14{,}092}{857.23} = 16.44 \text{ siniestros/km}^2$$donde $n = 14{,}092$ eventos fueron observados en una ventana de área $|W| = 857.23$ km².Esto significa que, en promedio, en el área urbana de estudio ocurren aproximadamente **16.44 siniestros por cada km²**. Sin embargo, este valor es solo un **promedio espacial** y no implica que la intensidad sea uniforme en toda la zona urbana.### Evidencia de inhomogeneidad espacialAl dividir la ventana en una rejilla de **10×10 cuadrantes**, los conteos por celda muestran una variabilidad muy alta:- **Conteo mínimo** por cuadrante: 0 - **Conteo máximo** por cuadrante: 677 - **Media** de conteos: 140.92 - **Varianza**: 33,342.74 - **Cuadrantes vacíos**: 34 de 100 (34%)La razón **Varianza/Media** resulta:$$V/M = \frac{\text{Var}(N_i)}{\mathbb{E}[N_i]} = \frac{33{,}342.74}{140.92} = 236.61$$**Interpretación del ratio V/M:**Bajo un **proceso de Poisson homogéneo** (CSR con intensidad constante $\lambda$), los conteos por cuadrante siguen una distribución Poisson y se esperaría aproximadamente $V/M \approx 1$. El valor obtenido ($V/M = 236.61$) es **enormemente mayor que 1**, lo cual indica que los conteos por cuadrante son mucho más variables de lo que se esperaría bajo homogeneidad. En términos de primer orden, esto sugiere con fuerza que la intensidad **no es constante** en el espacio. El patrón es claramente **inhomogéneo**: existen zonas con alta concentración de siniestros y otras con muy pocos o ningún evento.## Cuadrantes vacíos y validación con convex hullLos 34 cuadrantes sin eventos (34%) corresponden principalmente a áreas con baja o nula cobertura vial dentro de la ventana (parques urbanos, zonas industriales, cerros orientales), lo cual contribuye a inflar el ratio $V/M$.Para evaluar si esta inhomogeneidad es un **artefacto de la definición de la ventana** o refleja **variación real** en el patrón urbano, se realizó una prueba adicional utilizando un **convex hull** (envolvente convexa) como ventana alternativa. El convex hull elimina automáticamente áreas periféricas y genera una ventana más ajustada (538 km²) que reduce los cuadrantes vacíos de 34 a 12 (-65%).**Resultado:** Incluso con convex hull, el ratio $V/M$ solo se redujo a **197.02** (-17%), permaneciendo muy por encima de 1. Esto **confirma que la inhomogeneidad es genuina** y no un artefacto de la ventana: refleja diferencias reales en la intensidad de siniestralidad entre zonas urbanas de Bogotá (por ejemplo, Kennedy con λ > 40 siniestros/km² versus zonas residenciales con λ < 10 siniestros/km²).### Test CSR (Chi-cuadrado en cuadrantes)El test $\chi^2$ de bondad de ajuste aplicado a los cuadrantes evalúa formalmente la hipótesis nula:$$H_0: \lambda(s) = \lambda \text{ (constante) para todo } s \in W$$**Resultados:**- **Estadístico**: $X^2 = 23{,}424.15$ - **Grados de libertad**: $gl = 99$ - **p-valor**: $< 2.2 \times 10^{-16}$**Conclusión estadística:** Se **rechaza contundentemente** la hipótesis de **CSR homogéneo** ($p < 0.05$). La evidencia es abrumadora contra la hipótesis de intensidad constante.Esta evidencia es consistente con el diagnóstico $V/M$: existe un componente fuerte de **primer orden** (inhomogeneidad), es decir, la intensidad $\lambda(s)$ varía espacialmente de forma sistemática.### Intensidad local (por cuadrante)Al convertir los conteos por cuadrante en intensidades locales $\hat{\lambda}_i = N_i / |A_i|$, donde $|A_i|$ es el área del cuadrante $i$, se observa un rango muy amplio:- **Intensidad mínima**: 0 siniestros/km² - **Intensidad máxima**: 78.98 siniestros/km² - **Rango**: 78.98 siniestros/km²**Interpretación:** Esta diferencia muestra que existen cuadrantes con casi ausencia de eventos y otros con una intensidad **muy superior** al promedio global (16.44 siniestros/km²). En particular, algunos cuadrantes alcanzan intensidades de hasta **4.8 veces el promedio urbano**, lo cual confirma la existencia de **hot spots** claramente diferenciados.Esta heterogeneidad espacial marcada refuerza la conclusión de que el proceso generador de siniestros viales **no es uniforme** en el espacio urbano, sino que está fuertemente modulado por factores espaciales como la densidad de tráfico, el tipo de vías, la centralidad urbana y las características locales de cada zona.### Conclusión del análisis de primer ordenEn conjunto, los resultados indican que:1. **El patrón de siniestros NO es compatible con CSR homogéneo** - Test $\chi^2$: rechazo con $p < 10^{-16}$ - Ratio $V/M = 236.61 \gg 1$2. **Existe variación espacial marcada en la intensidad $\lambda(s)$** - Rango de intensidades: 0 – 78.98 siniestros/km² - Factor de variación: hasta 4.8× el promedio urbano - Hot spots claramente identificables3. **La inhomogeneidad es genuina** - Refleja diferencias reales entre zonas urbanas - No es principalmente un artefacto de la ventana de observación - Compatible con variación en factores de riesgo espaciales### Implicaciones para el análisis de segundo ordenEl rechazo de CSR homogéneo tiene importantes consecuencias metodológicas para el análisis subsecuente:**Pregunta pendiente:** Dado que se confirmó inhomogeneidad ($\lambda(s)$ no constante), la pregunta de segundo orden es: *¿Existe **interacción espacial** entre eventos más allá de la variación en intensidad?*Es decir, una vez "descontada" la inhomogeneidad de primer orden, ¿los eventos muestran:- **Agregación** (tendencia a ocurrir cerca de otros eventos)?- **Regularidad** (tendencia a evitarse mutuamente)?- **Independencia** (compatible con Poisson inhomogéneo)?# Análisis de Segundo Orden: Interacción EspacialEl análisis de segundo orden estudia la **dependencia espacial** entre eventos, es decir, si los siniestros tienden a ocurrir cerca unos de otros (agregación) o se evitan mutuamente (regularidad), más allá de la variación en intensidad ya detectada.Dado que confirmamos **inhomogeneidad** ($\lambda(s)$ no constante), usaremos funciones de segundo orden **con corrección por inhomogeneidad**.## Estimación de la intensidad espacial $\lambda(s)$Primero estimamos la intensidad local mediante **densidad kernel**, que será usada para normalizar las funciones de segundo orden:```{r estimar-lambda, message=FALSE, warning=FALSE}# =========================================================# Estimación de intensidad espacial λ(s) con kernel# =========================================================cat("═══ ESTIMACIÓN DE INTENSIDAD λ(s) ═══\n\n")# Ancho de banda óptimo (regla de Scott para datos espaciales)sigma_bw <- bw.scott(ppp_plot)cat("Ancho de banda (σ):\n")cat(" Método: Scott (isotropic)\n")cat(" σ_x =", round(sigma_bw[1], 3), "km\n")cat(" σ_y =", round(sigma_bw[2], 3), "km\n")cat(" σ promedio =", round(mean(sigma_bw), 3), "km\n\n")# Calcular densidad kernel (intensidad estimada)lambda_hat <- density(ppp_plot, sigma = sigma_bw)cat("✓ Intensidad λ(s) estimada\n")cat(" Intensidad mínima:", round(min(lambda_hat), 2), "siniestros/km²\n")cat(" Intensidad máxima:", round(max(lambda_hat), 2), "siniestros/km²\n")cat(" Intensidad media:", round(mean(lambda_hat), 2), "siniestros/km²\n\n")``````{r mapa-lambda, fig.width=12, fig.height=8, fig.cap="Estimación suavizada de la intensidad espacial λ(s)"}# Visualizar intensidad estimadaplot(lambda_hat, main = expression(paste("Intensidad Espacial Estimada ", lambda, "(s)")), col = hcl.colors(100, "YlOrRd", rev = TRUE), las = 1)# Añadir contornoscontour(lambda_hat, add = TRUE, col = "black", lwd = 0.8, drawlabels = FALSE)# Añadir puntos (semi-transparentes)plot(ppp_plot, add = TRUE, pch = 20, cex = 0.2, col = rgb(0,0,0,0.15))```## Interpretación de λ(s)Este mapa muestra la **intensidad local estimada** mediante suavizado kernel. Las zonas rojas (alta λ) son áreas con mayor tasa esperada de siniestros, mientras que las zonas amarillas/blancas tienen menor intensidad. Esta superficie será usada para normalizar las funciones de segundo orden.## Función K de Ripley (Inhomogénea)La función $K_{\text{inhom}}(r)$ evalúa si los eventos están más agregados o más regulares de lo esperado bajo un **proceso de Poisson inhomogéneo** (independencia espacial con λ(s) variable).```{r funcion-K-inhom, message=FALSE, warning=FALSE}# =========================================================# Función K inhomogénea# =========================================================cat("═══ FUNCIÓN K INHOMOGÉNEA ═══\n\n")# Calcular K inhomogénea con corrección de bordeK_inhom <- Kinhom(ppp_plot, lambda = lambda_hat, correction = "border", r = seq(0, 5, by = 0.1)) # Hasta 5 kmcat("✓ K_inhom calculada\n\n")# Transformación L para estabilizar varianzaL_inhom <- Linhom(ppp_plot, lambda = lambda_hat, correction = "border", r = seq(0, 5, by = 0.1))cat("✓ L_inhom calculada\n\n")``````{r envolventes-K-corregido, message=FALSE, warning=FALSE}# =========================================================# Solución: Forzar lambda_hat a ser estrictamente positivo# =========================================================cat("═══ CORRECCIÓN DE λ(s) PARA ENVOLVENTES ═══\n\n")# Clonar lambda_hat y forzar valores positivoslambda_hat_pos <- lambda_hatlambda_hat_pos[lambda_hat_pos <= 0] <- min(lambda_hat[lambda_hat > 0])cat("✓ λ(s) corregida para ser estrictamente positiva\n")cat(" Valores negativos o cero reemplazados por:", round(min(lambda_hat[lambda_hat > 0]), 4), "\n\n")# Envolventes Monte Carlo con lambda corregidacat("Generando envolventes Monte Carlo... (puede tardar 5-10 minutos)\n")cat(" Simulaciones: 99 réplicas bajo Poisson inhomogéneo\n\n")set.seed(42)env_K <- envelope(ppp_plot, fun = Kinhom, lambda = lambda_hat_pos, # Usar lambda corregida nsim = 99, correction = "border", r = seq(0, 5, by = 0.1), verbose = FALSE, savefuns = FALSE) # No guardar funciones individualescat("✓ Envolventes K calculadas\n\n")env_L <- envelope(ppp_plot, fun = Linhom, lambda = lambda_hat_pos, # Usar lambda corregida nsim = 99, correction = "border", r = seq(0, 5, by = 0.1), verbose = FALSE, savefuns = FALSE)cat("✓ Envolventes L calculadas\n\n")``````{r plot-K-L, fig.width=14, fig.height=6, fig.cap="Funciones K y L inhomogéneas con envolventes Monte Carlo"}par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))# Panel 1: K inhomogéneaplot(env_K, main = expression(paste("Función ", K[inhom], "(r)")), xlab = "Distancia r (km)", ylab = expression(K[inhom](r)), legend = FALSE, lwd = 2)# Línea teórica (Poisson inhomogéneo)lines(env_K$r, pi * env_K$r^2, lty = 2, col = "red", lwd = 2)legend("topleft", legend = c("Observado", "Teórico (Poisson inhom)", "Envolvente 99%"), lty = c(1, 2, 1), col = c("black", "red", "grey"), lwd = c(2, 2, 8), bty = "n")# Panel 2: L - rplot(env_L, . - r ~ r, main = expression(paste("Función ", L[inhom], "(r) - r")), xlab = "Distancia r (km)", ylab = expression(L[inhom](r) - r), legend = FALSE, lwd = 2)abline(h = 0, lty = 2, col = "red", lwd = 2)legend("topleft", legend = c("Observado", "Independencia", "Envolvente 99%"), lty = c(1, 2, 1), col = c("black", "red", "grey"), lwd = c(2, 2, 8), bty = "n")par(mfrow = c(1, 1))```## Interpretación de las funciones \(K\) y \(L\) **inhomogéneas**En esta sección se evalúa la **interacción espacial de segundo orden** controlando la **inhomogeneidad de primer orden** (es decir, permitiendo que la intensidad \(\lambda(s)\) varíe en el espacio). Para ello se usan las funciones \(K_{\text{inhom}}(r)\) y \(L_{\text{inhom}}(r)-r\), junto con envolventes de Monte Carlo (99%) bajo un **Poisson inhomogéneo**.### Panel izquierdo: \(K_{\text{inhom}}(r)\)**Observaciones:**- La curva observada (línea negra) se mantiene **dentro de la envolvente** (99%) para todo el rango de distancias analizado (0–5 km).- La curva observada está **muy cercana** a la referencia teórica del **Poisson inhomogéneo** (línea roja punteada).- No se presentan desviaciones sistemáticas (ni por encima ni por debajo) que sugieran interacción.**Conclusión (K):** No se detecta evidencia estadísticamente significativa de **agregación** ni de **regularidad**. El patrón es **compatible con un proceso de Poisson inhomogéneo**, es decir, eventos independientes con intensidad espacial variable.### Panel derecho: \(L_{\text{inhom}}(r) - r\)**Observaciones:**- La curva observada (línea negra) permanece **dentro de la envolvente** en todo el rango 0–5 km.- La curva oscila alrededor de \(L_{\text{inhom}}(r) - r \approx 0\) (línea de referencia para independencia).- Puede observarse una ligera tendencia positiva en algunos tramos, pero **no es significativa**, ya que no supera los límites de la envolvente.**Conclusión (L):** No hay evidencia significativa de **agregación** a ninguna escala espacial considerada (0–5 km). Una vez controlada la variación de \(\lambda(s)\), el patrón resulta consistente con **independencia espacial**.---## Conclusión del análisis de segundo orden**Resultado principal:** Una vez controlada la inhomogeneidad de la intensidad \(\lambda(s)\), **no se observa interacción espacial significativa** entre los siniestros.Esto implica que:- La “agregación aparente” observada en mapas y densidades se explica por **inhomogeneidad de primer orden** (variación espacial de \(\lambda(s)\)).- No hay evidencia de **clustering de segundo orden**: los siniestros no tienden a ocurrir cerca de otros siniestros más allá de lo esperado por la intensidad local.- El patrón es compatible con un **Poisson inhomogéneo**: eventos independientes modulados por factores espaciales que afectan \(\lambda(s)\).### Interpretación práctica: ¿de dónde vienen los *hot spots*?Los *hot spots* no sugieren un “contagio espacial” entre siniestros ni clusters auto-organizados. Más bien reflejan condiciones del entorno que elevan la intensidad local \(\lambda(s)\), por ejemplo:- mayor densidad de tráfico,- tipos de vía (corredores principales vs. zonas residenciales),- intersecciones complejas,- diseño vial deficiente,- factores socioeconómicos y de uso del suelo.