libs <- c("tidyverse", "sf", "mapSpain", "osmdata", "ggspatial", "leaflet", "prettymapr", "prettydoc","spatstat", "sp", "sf","splancs","dplyr","tidygeocoder","knitr")
installed_libs <- libs %in% rownames(installed.packages())
if (any(installed_libs == F))
{install.packages(libs[!installed_libs])}
invisible(lapply(libs, library, character.only = T))
rm(libs, installed_libs)
Accidentalidad del municipio de Madrid
Input
Los datos empleados en este informe provienen de “https://datos.gob.es/es/catalogo”, un portal de datos abiertos impulsado por la secretaría de estado de digitalización e inteligencia artificial. En concreto estamos trabajando con los accidentes de tráfico de la ciudad de Madrid en el año 2023, se incluye un registro por persona implicada en el accidente. Cada registro cuenta con:
“num_expediente” ; “fecha” ; “hora” ; “localizacion” ; “numero” ; “cod_distrito” ; “tipo_accidente” ; “estado_meteorológico” ; “tipo_vehiculo” ; “tipo_persona” ; “rango_edad” ; “sexo” ; “cod_lesividad” ; “lesividad” ; “coordenada_x_utm” ; “coordenada_y_utm” ; “positiva_alcohol” ; “positiva_droga”.
Se pueden descargar los datos desde el siguiente enlace: https://datos.madrid.es/egob/catalogo/300228-28-accidentes-trafico-detalle.csv
A continuación se muestra información de interés:
Fecha de creación: 04/06/2018
Fecha de revisión: 21/03/2024
Frecuencia de actualización: Mensual
Idioma: Español
Licencia de uso: https://datos.madrid.es/egob/catalogo/aviso-legal
Los datos están disponibles en formato CSV y XLS.
En nuestro caso hemos trabajado con los datos en formato .csv.
Descripcion
Cargamos las librerias necesarias:
Cargamos los datos:
datos <- read.csv(file = "data/2023_Accidentalidad.csv", sep = ";")
Como nuestro objetivo es representar los accidentes, nos quedamos con las variables necesarias para dicha tarea:
datos_filtrados <- datos[, c("num_expediente", "localizacion","numero", "coordenada_x_utm","coordenada_y_utm")]
Cada observación del objeto “datos_filtrados” se corresponde con una persona implicada en un mismo accidente, por lo que vamos a agrupar a todos los implicados en un mismo accidente para reducir el tamaño de los datos de cara a poder representarlos:
datos_agrupados <- aggregate(. ~ num_expediente, data = datos_filtrados, FUN = function(x) x[1])
Ahora, convertimos a formato numérico las variables “coordenada_x_utm” y “coordenada_y_utm”
datos_agrupados$coordenada_x_utm <- as.numeric(datos_agrupados$coordenada_x_utm)
Warning: NAs introducidos por coerción
datos_agrupados$coordenada_y_utm <- as.numeric(datos_agrupados$coordenada_y_utm)
Warning: NAs introducidos por coerción
Como vemos se nos generan valores nulos (NA), lo que supone un impedimento a la hora de poder representar los datos. En concreto, las observaciones que corresponden con estos valores son:
[1] 19559 19794 19801 19873
[1] 19559 19794 19801 19873
kable(datos_agrupados[c(19559,19794,19801,19873),], caption = "Observaciones con coordenadas NA", align = "c")
num_expediente | localizacion | numero | coordenada_x_utm | coordenada_y_utm | |
---|---|---|---|---|---|
19559 | 2023S037825 | CALL. ATOCHA, 112 | 112 | NA | NA |
19794 | 2023S038704 | PASARELA DE LA PRINCESA | +00500I | NA | NA |
19801 | 2023S038728 | AVDA. PALOMERAS / CALL. PUERTO DEL PICO | 150 | NA | NA |
19873 | 2023S038882 | AUTOV. M-30, 07NC40 | 07NC40 | NA | NA |
Con esto sabemos que esas observaciones tienen valores nulos en sus coordenadas.
Tratamiento
Para resolver esto, nos quedamos con un dataframe con los datos erróneos:
Seguidamente, mediante google maps, obtenemos las coordenadas exactas de las observaciones faltantes:
coordenadas <- data.frame(
lon = c(-3.6934083011307823,
-3.697221725741146,
-3.649032006107653),
lat = c(40.409664499236506,
40.38828814833728,
40.38577512287311)
)
La observación 19873 correspondiente a un accidente en la M-30 tiene un formato de dirección que no se puede identificar, por lo que prescindiremos de ella, debido a que es una observación entre 20672, los resultados sobre posible enfoques de análisis de los datos no se verían afectados. Su eliminación se hará tras añadir las coordenadas a las demás observaciones.
Creamos un objeto sf con el dataframe de las coordenadas
coords_sf <- st_as_sf(coordenadas, coords = c("lon", "lat"), crs = 4326)
Definimos el sistemas de coordenadas resultante (UTM)
crs_utm <- st_crs(32630)
Transformamos las coordenadas obtenidas anteriormente a formato UTM
coords_utm <- st_transform(coords_sf, crs_utm)
Aislamos las coordenadas UTM para tenerlas más controladas
coordenadas_utm <- st_coordinates(coords_utm)
Separamos las coordenadas individualmente
coordenadas_utm_x <- coordenadas_utm[, 1]
coordenadas_utm_y <- coordenadas_utm[, 2]
Creamos un dataframe con las coordenadas definitivas
coordenadas_utm_df <- data.frame(coordenada_x_utm = coordenadas_utm_x, coordenada_y_utm = coordenadas_utm_y)
Añadimos las coordenadas a un nuevo objeto igual a “datos_agrupados”
datos_agrupados_correctos <- datos_agrupados
datos_agrupados_correctos[19559, 'coordenada_x_utm'] <- coordenadas_utm_df[1,"coordenada_x_utm"]
datos_agrupados_correctos[19794, 'coordenada_x_utm'] <- coordenadas_utm_df[2,"coordenada_x_utm"]
datos_agrupados_correctos[19801, 'coordenada_x_utm'] <- coordenadas_utm_df[3,"coordenada_x_utm"]
datos_agrupados_correctos[19559, 'coordenada_y_utm'] <- coordenadas_utm_df[1,"coordenada_y_utm"]
datos_agrupados_correctos[19794, 'coordenada_y_utm'] <- coordenadas_utm_df[2,"coordenada_y_utm"]
datos_agrupados_correctos[19801, 'coordenada_y_utm'] <- coordenadas_utm_df[3,"coordenada_y_utm"]
Eliminamos la observación que habíamos comentado
datos_agrupados_correctos <- datos_agrupados_correctos[!is.na(datos_agrupados_correctos$coordenada_x_utm), ]
Representamos los datos
datos_agrupados_correctos_sf <- sf::st_as_sf(datos_agrupados_correctos,
coords = c("coordenada_x_utm",
"coordenada_y_utm"), crs = 25830)
sf1 <- sf::st_transform(datos_agrupados_correctos_sf, crs = 4326)
sf1 %>%
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::addCircles(popup = ~localizacion)
Tras representar los datos, vemos que un punto nos aparece fuera del municipio de Madrid, vamos a tratar de detectar qué accidente ha sido:
madrid <- mapSpain::esp_get_munic() %>% filter(cpro=="28" & cmun == "079")
madrid <- sf::st_transform(madrid, crs=4326)
datos_agrupados_correctos_sf <- st_transform(datos_agrupados_correctos_sf, crs = st_crs(madrid))
pts_in <- sf::st_intersection(datos_agrupados_correctos_sf,madrid)
pts_out <- sf::st_difference(datos_agrupados_correctos_sf,madrid)
kable(pts_out, caption = "Puntos marcados fuera de Madrid", align = "c")
num_expediente | localizacion | numero | codauto | ine.ccaa.name | cpro | ine.prov.name | cmun | name | LAU_CODE | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|
852 | 2023S001247 | CTRA. MEDIODIA / CMNO. VIEJO DEL CURA | 4 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.654725 40.51292) |
1334 | 2023S002142 | CALL. PORTICO DE LA GLORIA / COMUNICACION | 2 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.662682 40.51917) |
2646 | 2023S005326 | AVDA. LOS ROSALES, 409 | 409 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.659836 40.32799) |
3052 | 2023S006218 | CTRA. MEDIODIA, 6 | 6 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.653736 40.51216) |
4260 | 2023S009719 | AVDA. CASA QUEMADA, 2 | 2 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.836651 40.47566) |
6299 | 2023S016698 | CTRA. M-616, KILOMETRO 3 | +00500E | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.68213 40.55311) |
6485 | 2023S017160 | INCORPORACIÓN A M45 DIRECCIÓN VALENCIA | 9 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.554614 40.41155) |
8519 | 2023S021243 | CTRA. VICALVARO A COSLADA / AVDA. MARCONI | 18 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.576832 40.42856) |
9084 | 2023S022269 | CTRA. CARABANCHEL A ARAVACA / M502 | 2 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.77857 40.39956) |
9453 | 2023S022935 | AUTOV. A-4, +01200E; PK 4,700 DE A-4 ENTRADA A MADRID | +01200E | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.691517 40.31985) |
10502 | 2023S024414 | AEROP. TERMINAL T-4, 0 | 0 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-7.488744 0) |
10730 | 2023S024695 | CALL. MAESTRO ARBOS, 13 | 13 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.764819 40.35924) |
11581 | 2023S025851 | AVDA. MARCONI / CTRA. VICALVARO A LA ESTACION DE O’DONNELL | 1 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.576844 40.42859) |
11979 | 2023S026398 | CALL. JULIO CARO BAROJA / CTRA. MEDIODIA | 2 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.648282 40.5105) |
12466 | 2023S027158 | CTRA. VICALVARO A LA ESTACION DE O’DONNELL / AVDA. MARCONI | 137D | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.576718 40.42944) |
13146 | 2023S028201 | AVDA. MARCONI / CALL. CAMPEZO | 14 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.576861 40.42858) |
15213 | 2023S031205 | CALL. CEA BERMUDEZ, 57 | 57 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-7.488744 0) |
16822 | 2023S033647 | CTRA. ZARZON, 6 | 6 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.780395 40.41454) |
16870 | 2023S033711 | CTRA. ACCESO A LA ESTACION DE O’DONNELL / AVDA. MARCONI | 10 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.576828 40.42863) |
17917 | 2023S035359 | AUTOV. A-4, KM 5 ENTRADA MADRID | +01200E | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.691967 40.32044) |
19635 | 2023S038002 | CALL. CAMPEZO / AVDA. MARCONI | 16 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-3.576842 40.42863) |
Como vemos, todos los puntos que en teoría están fuera de la superficie de madrid realmente tienen su ubicacíon en el própio municipio, vamos a ver qué está sucediendo.
leaflet() %>%
addTiles() %>%
addPolygons(data = madrid, color = "red", weight = 5) %>%
addCircles(data = pts_out, popup = ~localizacion) %>%
setView(lat=40.4380986,
lng=-3.8443446,
zoom=10)
Como vemos, casi todos los puntos se encuentran en el borde de la superficie por lo que realmente sí que están bien ubicados, aunque se considera que estan fuera del municipio.
Conociendo esto solo tenemos que corregir 2 puntos que comparten las mismas coordenadas:
puntos_perdidos <- pts_out[c(11, 17), ]
kable(puntos_perdidos, caption = "Puntos marcados fuera de Madrid", align = "c")
num_expediente | localizacion | numero | codauto | ine.ccaa.name | cpro | ine.prov.name | cmun | name | LAU_CODE | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|
10502 | 2023S024414 | AEROP. TERMINAL T-4, 0 | 0 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-7.488744 0) |
15213 | 2023S031205 | CALL. CEA BERMUDEZ, 57 | 57 | 13 | Madrid, Comunidad de | 28 | Madrid | 079 | Madrid | 28079 | POINT (-7.488744 0) |
leaflet() %>%
addTiles() %>%
addCircles(data = puntos_perdidos, popup = ~localizacion)
Para solucionar esto, volveremos a obtener las coordenadas precisas de las ubicaciones y las transformaremos a UTM como hemos hecho anteriormente:
coordenadas2 <- data.frame(
lon = c(-3.5983969,
-3.7179432),
lat = c(40.4957946,
40.4390803)
)
coords_sf2 <- st_as_sf(coordenadas2, coords = c("lon", "lat"), crs = 4326)
coords_utm2 <- st_transform(coords_sf2, crs_utm)
coordenadas_utm2 <- st_coordinates(coords_utm2)
coordenadas_utm_x2 <- coordenadas_utm2[, 1]
coordenadas_utm_y2 <- coordenadas_utm2[, 2]
coordenadas_utm_df2 <- data.frame(coordenada_x_utm = coordenadas_utm_x2, coordenada_y_utm = coordenadas_utm_y2)
datos_agrupados_correctos[10502, 'coordenada_x_utm'] <- coordenadas_utm_df2[1,"coordenada_x_utm"]
datos_agrupados_correctos[15213, 'coordenada_x_utm'] <- coordenadas_utm_df2[2,"coordenada_x_utm"]
datos_agrupados_correctos[10502, 'coordenada_y_utm'] <- coordenadas_utm_df2[1,"coordenada_y_utm"]
datos_agrupados_correctos[15213, 'coordenada_y_utm'] <- coordenadas_utm_df2[2,"coordenada_y_utm"]
Output
Finalmente, tras todo este trabajo de depuración de datos, obtenemos el dataframe “accidentes_madrid_2023” que contiene 20671 observaciones correspondientes a cada accidente del año 2023 en el municipio de Madrid, preparado para ser usado con diversas metodologías de análisis como por ejemplo comprobar la existecia de CRS, la búsqueda de patrones entre otros.
Aquí representamos los datos:
accidentes_madrid_2023 <- sf::st_as_sf(datos_agrupados_correctos,
coords = c("coordenada_x_utm",
"coordenada_y_utm"), crs = 25830)
sf1 <- sf::st_transform(accidentes_madrid_2023, crs = 4326)
sf1 %>%
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::addCircles(popup = ~localizacion)
Por último guardamos los datos
sf::st_write(accidentes_madrid_2023, "accidentes_madrid_2023.gpkg")
Proyecto de Innovación Educativa Emergente (PIEE-2737007)