library(sf)
library(spdep)
library(ggplot2)
library(dplyr)
library(leaflet)
library(htmltools)
library(lubridate)
library(dplyr)
library(sf)
library(readxl)
library(foreign)
library(tigris)
library(rgeoda)
library(RColorBrewer)
library(viridis)
library(ggplot2)
library(tmap)
library(sf)
library(sp)
library(spatialreg)
library(stargazer)
library(plm)
library(splm)
library(pspatreg)
library(regclass)
library(mctest)
library(lmtest)
library(spData)
library(mapview)
library(naniar)
library(dlookr)
library(caret)
library(e1071)
library(SparseM)
library(Metrics)
library(randomForest)
library(rpart.plot)
library(insight)
library(jtools)
library(xgboost)
library(DiagrammeR)
library(effects)
library(sf)
library(purrr)
library(geosphere)
library(gmapsdistance)
library(dplyr)
library(stringr)
library(knitr)
library(leaflet.extras)
library(tidyr)
library(prophet)
if (!require("pacman")) install.packages("pacman")
pacman::p_load(
sf, # Datos espaciales
spdep, # Autocorrelación espacial
ggplot2, # Visualización
dbscan, # Detección de clústers
dplyr # Manipulación de datos
)
if (!require("geosphere")) install.packages("geosphere")
if (!require("gmapsdistance"))install.packages("gmapsdistance")
datos <- read_excel("C:\\Users\\mari0\\OneDrive\\Documents\\R Studio\\8 semestre\\Evidencia\\BASE MUNICIPAL_ACCIDENTES DE TRANSITO GEORREFERENCIADOS_2023.xlsx")
geojson_data <- st_read("Transporte.geojson") %>%
st_transform(32614) %>% # Convertir a UTM Zone 14N (Monterrey)
filter(!st_is_empty(.)) # Eliminar geometrías vacías
## Reading layer `Transporte' from data source
## `C:\Users\mari0\OneDrive\Documents\R Studio\8 semestre\Evidencia\Transporte.geojson'
## using driver `GeoJSON'
## Simple feature collection with 74329 features and 4 fields
## Geometry type: LINESTRING
## Dimension: XY
## Bounding box: xmin: -100.7508 ymin: 25.3483 xmax: -99.7609 ymax: 26.13538
## Geodetic CRS: WGS 84
La información de transporte proviene de la encuesta origen-destino del 2019 que el gobierno del estado de Nuevo León realizó como parte del Programa Integral de Movilidad Urbana Sustentable (PIMUS) del Área Metropolitana de Monterrey. Transconsult fue la empresa consultora responsable del levantamiento de la encuesta.
# Desactivar geometría esférica (evita errores)
sf::sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
# Crear variable numérica para análisis
geojson_data <- geojson_data %>%
mutate(
peso_transporte = case_when(
Transporte %in% c("Autómovil", "TPUB", "Taxi") ~ 3,
Transporte %in% c("Transporte de Personal", "Transporte escolar") ~ 2,
TRUE ~ 1 # Para bicicletas, caminando y otros
)
)
# Obtener coordenadas de los centroides
coords <- st_coordinates(st_centroid(geojson_data))
## Warning: st_centroid assumes attributes are constant over geometries
# Calcular vecinos en un radio de 800 metros
nb <- dnearneigh(coords, d1 = 0, d2 = 800)
# Identificar y eliminar puntos aislados (sin vecinos)
aislados <- which(card(nb) == 0)
if(length(aislados) > 0){
geojson_data <- geojson_data[-aislados, ]
coords <- st_coordinates(st_centroid(geojson_data))
nb <- dnearneigh(coords, d1 = 0, d2 = 800)
}
## Warning: st_centroid assumes attributes are constant over geometries
# Crear matriz de pesos espaciales
pesos <- nb2listw(nb, style = "B") # Pesos binarios
# Calcular hotspots (Getis-Ord Gi*)
geojson_data$gi_score <- localG(
x = geojson_data$peso_transporte,
listw = pesos,
zero.policy = FALSE
)
# Visualización de resultados
# Convertir LINESTRING a puntos (extremos de cada línea)
puntos_hotspots <- st_cast(geojson_data, "POINT") %>%
st_transform(4326) # Convertir a WGS84
## Warning in st_cast.sf(geojson_data, "POINT"): repeating attributes for all
## sub-geometries for which they may not be constant
# Convertir los scores a numérico
geojson_data$gi_score_numeric <- as.numeric(geojson_data$gi_score)
# Convertir a puntos y filtrar
puntos_significativos <- geojson_data %>%
st_cast("POINT") %>%
st_transform(4326) %>%
filter(gi_score_numeric > quantile(gi_score_numeric, 0.97)) # Top 3%
## Warning in st_cast.sf(., "POINT"): repeating attributes for all sub-geometries
## for which they may not be constant
# Paleta de color para valores extremos
pal <- colorNumeric(
palette = "Reds",
domain = c(2.5, max(puntos_significativos$gi_score_numeric, na.rm = TRUE)),
na.color = "transparent"
)
# Mapa final
leaflet(puntos_significativos) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
radius = ~sqrt(gi_score_numeric)*2, # Tamaño proporcional al score
color = ~pal(gi_score_numeric),
fillOpacity = 0.7,
stroke = FALSE,
popup = ~paste("Score:", round(gi_score_numeric, 2))
) %>%
addLegend(
position = "bottomright",
pal = pal,
values = ~gi_score_numeric,
title = "Hotspots<br>(Top 3%)"
) %>%
setView(lng = -100.3, lat = 25.67, zoom = 12)
# Ver distribución de scores
ggplot(geojson_data, aes(x = gi_score_numeric)) +
geom_histogram(bins = 50) +
geom_vline(xintercept = quantile(geojson_data$gi_score_numeric, 0.95), color = "red") +
labs(title = "Distribución de Gi* Scores", subtitle = "Línea roja = percentil 95%")
La distribución es asimétrica con sesgo negativo: la mayor parte de los valores Gi* se concentran entre -10 y +8, con una larga cola hacia la izquierda (valores muy negativos).
Esto indica que la mayoría de los trayectos no presentan concentración significativa (Gi* cercano a 0), pero sí hay algunos con clara dispersión (Gi* muy negativo).
La línea roja marca el percentil 95, es decir, el umbral a partir del cual se considera que un Gi* score está en el 5% superior, lo cual indica un hotspot estadísticamente significativo.
# Convertir HoraOri a formato hora y luego a horas decimales
puntos_hora <- geojson_data %>%
st_cast("POINT") %>%
st_transform(4326) %>%
mutate(
hora_decimal = period_to_seconds(hms(HoraOri)) / 3600, # Convertir a horas con decimales
franja_horaria = cut(
hora_decimal,
breaks = c(0, 6, 9, 12, 15, 18, 21, 24),
labels = c("Madrugada", "Mañana-Pico", "Mediodía", "Tarde", "Tarde-Pico", "Noche-Temprana", "Noche-Tardia"),
right = FALSE,
include.lowest = TRUE
)
) %>%
filter(!is.na(hora_decimal)) # Eliminar registros sin hora válida
## Warning in st_cast.sf(., "POINT"): repeating attributes for all sub-geometries
## for which they may not be constant
# Verificar
summary(puntos_hora$hora_decimal)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 5.00 8.00 12.50 12.26 16.00 22.83
table(puntos_hora$franja_horaria, useNA = "always")
##
## Madrugada Mañana-Pico Mediodía Tarde Tarde-Pico
## 4098 43006 18450 35028 25250
## Noche-Temprana Noche-Tardia <NA>
## 19708 3034 0
# Paleta de colores para franjas
pal_hora <- colorFactor(
palette = c("#1a2b5c", "#f7aa00", "#a4d4ae", "#ff6b6b", "#c81d25", "#5e17eb", "#2e2e2e"),
domain = puntos_hora$franja_horaria
)
# Crear mapa
leaflet(puntos_hora) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
radius = ~ifelse(gi_score_numeric > 2, 6, 3),
color = ~pal_hora(franja_horaria),
fillOpacity = 0.7,
popup = ~paste(
"<strong>Hora:</strong> ", HoraOri, "<br>",
"<strong>Franja:</strong> ", franja_horaria, "<br>",
"<strong>Score:</strong> ", round(gi_score_numeric, 2)
)
) %>%
addLegend(
position = "bottomright",
pal = pal_hora,
values = ~franja_horaria,
title = "Franjas Horarias"
) %>%
setView(lng = -100.3, lat = 25.67, zoom = 12)