Accidentes de Tráfico Mortales

Accidentes de Tráfico Mortales
España
DGT
Autor/a
Afiliación

Eduardo Ortego Navazo

Universidad de Valencia

Fecha de publicación

1 de abril de 2026

Input

Se ha obtenido un conjunto de datos sobre accidentes de tráfico en España procedentes de la Dirección General de Tráfico (DGT).

El conjunto de datos original contiene información relativa a accidentes con víctimas (heridos y víctimas mortales), incluyendo variables como la fecha del accidente, el número de víctimas o el tipo de vía.

Sin embargo, el dataset presenta limitaciones importantes para su explotación en análisis espacial, ya que carece de coordenadas geográficas precisas y contiene datos dificiles de entender.

── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.0     ✔ readr     2.1.6
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.1     ✔ tibble    3.3.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.2
✔ purrr     1.2.1     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Linking to GEOS 3.13.1, GDAL 3.11.4, PROJ 9.7.0; sf_use_s2() is TRUE
library(tidygeocoder)
#install.packages("leaflet")
library(leaflet)
library(ggmap)
ℹ Google's Terms of Service: <https://mapsplatform.google.com>
  Stadia Maps' Terms of Service: <https://stadiamaps.com/terms-of-service>
  OpenStreetMap's Tile Usage Policy: <https://operations.osmfoundation.org/policies/tiles>
ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.

Adjuntando el paquete: 'ggmap'


The following object is masked from 'package:tidygeocoder':

    geocode
library(dplyr)
#install.packages("leaflet.extras")
library(leaflet.extras)
#install.packages("rio")
library(rio)
# Se cargan los datos descargados de la DGT
df <- "https://www.dgt.es/export/sites/web-DGT/.galleries/downloads/dgt-en-cifras/publicaciones/Ficheros_microdatos_de_accidentalidad_con_victimas/TABLA_ACCIDENTES_24.XLSX"
acc <- import(df)

Descripción

Tras una exploración inicial del conjunto de datos, se detectan varios problemas:

  • Ausencia de coordenadas geográficas (latitud y longitud), lo que impide su representación directa en mapas.

  • Formatos de fecha heterogéneos y no estandarizados.

  • Gran presencia de valores nulos o incompletos en diferentes variables.

  • Falta de variables temporales derivadas que permitan un análisis más detallado (día de la semana, franja horaria, etc.).

Estas limitaciones reducen significativamente el valor analítico del dataset original y pueden complicar su estudio.

Tratamiento

Con el objetivo de mejorar la calidad y utilidad del conjunto de datos, se han llevado a cabo diversas transformaciones, agrupadas en cuatro bloques principales:

Filtrado y Limpieza de Datos

Primero, filtramos los datos para obtener únicamente los accidentes de coche con víctimas mortales transcurridos 30 días desde el accidente. Escogiendo estos 30 días, añadimos las personas que no han muerto en el momento del accidente, pero sí días después a causa de este.

acc_mortales <- acc %>%
  filter(TOTAL_MU30DF > 0)

Tras esto, se procede a la limpieza de los datos, en este caso, escribiendo el nombre del día de la semana y el mes, además del tipo de vía.

acc_mortales <- acc_mortales %>%
  mutate(
    dia_semana = case_when(
      DIA_SEMANA == 1 ~ "Lunes",
      DIA_SEMANA == 2 ~ "Martes",
      DIA_SEMANA == 3 ~ "Miércoles",
      DIA_SEMANA == 4 ~ "Jueves",
      DIA_SEMANA == 5 ~ "Viernes",
      DIA_SEMANA == 6 ~ "Sábado",
      DIA_SEMANA == 7 ~ "Domingo"
    )
  )


acc_mortales <- acc_mortales %>%
  mutate(
    mes_nombre = case_when(
      MES == 1 ~ "Enero",
      MES == 2 ~ "Febrero",
      MES == 3 ~ "Marzo",
      MES == 4 ~ "Abril",
      MES == 5 ~ "Mayo",
      MES == 6 ~ "Junio",
      MES == 7 ~ "Julio",
      MES == 8 ~ "Agosto",
      MES == 9 ~ "Septiembre",
      MES == 10 ~ "Octubre",
      MES == 11 ~ "Noviembre",
      MES == 12 ~ "Diciembre"
    )
  )
      
acc_mortales <- acc_mortales %>%
  mutate(
    tipo_via = case_when(
      TIPO_VIA == 1 ~ "Autopista",
      TIPO_VIA == 2 ~ "Autopista",
      TIPO_VIA == 3 ~ "Autovía",
      TIPO_VIA == 5 ~ "Carretera convencional",
      TIPO_VIA == 6 ~ "Carretera convencional",
      TIPO_VIA == 9 ~ "Vía urbana",
      TIPO_VIA == 10 ~ "Vía urbana",
      TRUE ~ "Otro"
    )
  )

Además para poder realizar el geocoding de una manera más efectiva, se pasan los códigos que contienen las prvincias a sus nombres:

acc_mortales <- acc_mortales %>%
  mutate(
    provincia_nombre = case_when(
      COD_PROVINCIA == 1  ~ "Álava",
      COD_PROVINCIA == 2  ~ "Albacete",
      COD_PROVINCIA == 3  ~ "Alicante",
      COD_PROVINCIA == 4  ~ "Almería",
      COD_PROVINCIA == 5  ~ "Ávila",
      COD_PROVINCIA == 6  ~ "Badajoz",
      COD_PROVINCIA == 7  ~ "Islas Baleares",
      COD_PROVINCIA == 8  ~ "Barcelona",
      COD_PROVINCIA == 9  ~ "Burgos",
      COD_PROVINCIA == 10 ~ "Cáceres",
      COD_PROVINCIA == 11 ~ "Cádiz",
      COD_PROVINCIA == 12 ~ "Castellón",
      COD_PROVINCIA == 13 ~ "Ciudad Real",
      COD_PROVINCIA == 14 ~ "Córdoba",
      COD_PROVINCIA == 15 ~ "A Coruña",
      COD_PROVINCIA == 16 ~ "Cuenca",
      COD_PROVINCIA == 17 ~ "Girona",
      COD_PROVINCIA == 18 ~ "Granada",
      COD_PROVINCIA == 19 ~ "Guadalajara",
      COD_PROVINCIA == 20 ~ "Gipuzkoa",
      COD_PROVINCIA == 21 ~ "Huelva",
      COD_PROVINCIA == 22 ~ "Huesca",
      COD_PROVINCIA == 23 ~ "Jaén",
      COD_PROVINCIA == 24 ~ "León",
      COD_PROVINCIA == 25 ~ "Lleida",
      COD_PROVINCIA == 26 ~ "La Rioja",
      COD_PROVINCIA == 27 ~ "Lugo",
      COD_PROVINCIA == 28 ~ "Madrid",
      COD_PROVINCIA == 29 ~ "Málaga",
      COD_PROVINCIA == 30 ~ "Murcia",
      COD_PROVINCIA == 31 ~ "Navarra",
      COD_PROVINCIA == 32 ~ "Ourense",
      COD_PROVINCIA == 33 ~ "Asturias",
      COD_PROVINCIA == 34 ~ "Palencia",
      COD_PROVINCIA == 35 ~ "Las Palmas",
      COD_PROVINCIA == 36 ~ "Pontevedra",
      COD_PROVINCIA == 37 ~ "Salamanca",
      COD_PROVINCIA == 38 ~ "Santa Cruz de Tenerife",
      COD_PROVINCIA == 39 ~ "Cantabria",
      COD_PROVINCIA == 40 ~ "Segovia",
      COD_PROVINCIA == 41 ~ "Sevilla",
      COD_PROVINCIA == 42 ~ "Soria",
      COD_PROVINCIA == 43 ~ "Tarragona",
      COD_PROVINCIA == 44 ~ "Teruel",
      COD_PROVINCIA == 45 ~ "Toledo",
      COD_PROVINCIA == 46 ~ "Valencia",
      COD_PROVINCIA == 47 ~ "Valladolid",
      COD_PROVINCIA == 48 ~ "Bizkaia",
      COD_PROVINCIA == 49 ~ "Zamora",
      COD_PROVINCIA == 50 ~ "Zaragoza",
      COD_PROVINCIA == 51 ~ "Ceuta",
      COD_PROVINCIA == 52 ~ "Melilla",
      TRUE ~ "Desconocido" 
    )
  )

Y AHORA EMPIEZA LA TRANSFORMACIÓN DE VERDAD

Se eliminan las filas con datos faltantes en la provincia, año, mes o día de la semana para evitar errores en su análisis. Además se crea una variable fecha bien estandarizada.

acc_mortales <- acc_mortales %>%
  drop_na(provincia_nombre, ANYO, MES, DIA_SEMANA)



acc_mortales <- acc_mortales %>%
  mutate(
    fecha = make_date(ANYO, MES),
    año = ANYO
  )

Tras esto, se crean dos nuevas variables, la primera para diferenciar si los accidentes tuvieron lugar entre semana o en fin de semana y la segunda para saber en qué etapa del día se produjeron los accidentes, por la mañana, por la tarde o por la noche:

acc_mortales <- acc_mortales %>%
  mutate(
    tipo_dia = ifelse(dia_semana %in% c("Sábado", "Domingo"),
                      "Fin de semana", "Laboral"),
    
    franja_horaria = case_when(
      HORA >= 0 & HORA < 6 ~ "Noche",
      HORA >= 6 & HORA < 13 ~ "Mañana",
      HORA >= 13 & HORA < 20 ~ "Tarde",
      HORA >= 20 & HORA < 24 ~ "Noche",
      
    )
  )

Tras esto se crea una dirección para poder estudiarlo, eliminando todos los NA que se encuentren. Además se eliminan las variables innecesarias.

acc_mortales <- acc_mortales %>%
  mutate(
    direccion = paste(provincia_nombre, "Spain"),
    direccion = as.character(direccion)
  )

# Eliminar NA
acc_mortales <- acc_mortales %>%
  filter(!is.na(direccion))


acc_geo_bien <- acc_mortales %>% select(-c(4,8,9,10, 13:20, 22:73))

SIN EMBARGO, PARA REALIZAR GEOCODING, NECESITAMOS EL NOMBRE DE LOS MUNICIPIOS.

Para esto obtenemos un dataframe de la INE donde aparecen el código de esos municipios y sus nombres, saltandonos la primera línea ya que contiene información en blanco.

municipios <- import("https://www.ine.es/daco/daco42/codmun/codmun18/18codmun.xlsx",  skip = 1)

Con esto, corregimos los datos para que aparezcan en el mismo formato que la DGT.

municipios <- municipios %>%
  mutate(
    COD_MUNICIPIO = as.numeric(CMUN),
    COD_PROV = as.numeric(CPRO),
    
    COD_MUN_COMPLETO = paste0(
      COD_PROV,
      sprintf("%03d", COD_MUNICIPIO)
    )
  )


municipios <- municipios %>%
  mutate(COD_MUNICIPIO = str_pad(COD_MUN_COMPLETO, width = 5, side = "left", pad = "0"))

Se unen estos datos a nuestro dataframe para unir el código del municipio a su nombre. Además eliminamos NA para evitar errores y limpiamos datos innecesarios/duplicados.

Finalmente se crea la dirección completa.

acc_geo_bien_2 <- acc_geo_bien %>%
  left_join(
    municipios,
    by = "COD_MUNICIPIO"
  )


# Eliminar NA
acc_geo_bien_2 <- acc_geo_bien_2 %>%
  filter(!is.na(NOMBRE))

## Y LIMPIAMOS DATOS INNECESARIOS
acc_geo_bien2 <- acc_geo_bien_2 %>% select(-c(19,20,21,22,24,25))


#### SE CREA DIRECCION COMPLETA

acc_geo_bien2 <- acc_geo_bien2 %>%
  mutate(
    direccion = paste(NOMBRE, provincia_nombre, "Spain"),
    direccion = as.character(direccion)
  )

Tras esto, se geocodifica para poder obtener buenos resultados:

acc_final <- acc_geo_bien2 %>%
  tidygeocoder::geocode(
    address = direccion,
    method = "osm",
    lat = lat,
    long = lon
  )

Tras geocodificar, podemos ver los resultados en forma de gráficas.

Primero una gráfica con todos los accidentes situados geográficamente en España y datos importantes sobre ellos como el dia de la semana, número de muestros, etc.:

leaflet(acc_final) %>%
  addTiles() %>%
  addCircleMarkers(
    ~lon, ~lat,
    radius = ~log(TOTAL_MU30DF) * 3,
    color = "red",
    stroke = FALSE,
    fillOpacity = 0.6,
    popup = ~paste(
      "Direccion:", direccion,
      "<br>Fallecidos:", TOTAL_MU30DF,
      "<br>Día:", dia_semana
    )
  )

En segundo lugar, un mapa de España con la densidad de accidentes en las diferentes zonas:

heat_data <- acc_final %>%
  group_by(lat, lon) %>%
  summarise(muertes = sum(TOTAL_MU30DF), .groups = "drop")

leaflet(heat_data) %>%
  addTiles() %>%
  addHeatmap(
    lng = ~lon,
    lat = ~lat,
    intensity = ~muertes,
    blur = 1,
    radius = 15
  )

Output

writexl::write_xlsx(acc_final, "acc_final.xlsx")

Con esto, obtenemos unos datos mejorados sobre los accidentes de tráfico con accidentes mortales en España en el año 2024. El dataset final incluye:

  • Coordenadas geográficas (latitud y longitud)
  • Variables temporales bien estandarizadas
  • Datos limpios y consistentes

Este nuevo conjunto de datos permite, como hemos visto, su visualización en mapas, análisis de patrones espaciales de accidentes mortales y estudio de su evolución temporal.

El/los fichero(s) generados con este procedimiento/técnica/metodología se puede(n) descargar de aquí.



Proyecto de Innovación Educativa Emergente (PIEE-3898312)