Más sobre ciencia de datos: cienciadedatos.net

Introducción


En este documento se muestra cómo crear mapas en R empleando ggplot2. A diferencia de otros aproximaciones, en las que el mapa es una imagen sobre la que se superponen elementos, en este caso todo el mapa se crea empleando las geometrías de ggplot2. Además, una vez creado el gráfico, puede convertirse en interactivo con el paquete plotly.

A modo de ejemplo, se representan todos los municipios de España (la península y Baleares) y se colorea en función del número de habitantes del censo 2019.

Librerías


Las librerías utilizadas a lo largo del documento son:

library(tidyverse)
library(stringr)
library(rgdal)
library(rgeos)
library(plotly)



Mapa


Para crear un mapa es necesario disponer de las coordenadas que definen los límites de cada elemento, en este caso los municipios. Uno de los estándares para almacenar y compartir elementos geográficos y los atributos asociados a ellos es el formato Shapefile (.shp).

Se pueden descargar los ficheros .shp con todos los municipios españoles en las páginas del © Centro Nacional de Información Geográfica (CNIG) y del © Instituto Geográfico Nacional de España.

Datos adicionales


El código de identificación de las provincias y comunidades autónomas de España puede encontrarse en la página del INE. Instituto Nacional de Estadística.

El número de habitantes a nivel de comunidad autónoma, provincia y municipio, desde 1999 a 2020 puede descargarse de la página del padrón oficial del INE. Es recomendable descargarse todos los años en un único archivo .zip link.

Lectura de datos .shp


Los ficheros .shp pueden leerse en R con la función readOGR() del paquete rgdal.

# Lectura del fichero .shp
mapa <- rgdal::readOGR(
          paste0("./datos/lineas_limite/recintos_municipales_inspire_peninbal_etrs89/",
                 "recintos_municipales_inspire_peninbal_etrs89.shp")
        )

El objeto devuelto por readOGR() es una clase S4 por lo que sus elementos (llamados slots) son accesibles mediante el operador @. En su interior contiene, entre otros elementos, las coordenadas de cada polígono @polygons e información adicional sobre el mapa @data.

# Nombre de los slots del objeto mapa
slotNames(mapa)
## [1] "data"        "polygons"    "plotOrder"   "bbox"        "proj4string"



Conversión de formato


Una vez que el fichero se han leído, para poder emplear las magníficas funcionalidades gráficas de ggplot2, se tiene que convertir a formato dataframe o tibble. Esta conversión puede hacerse con la función fortify() del paquete fortify. El argumento región es el identificador único empleado para asociar cada punto a un polígono. Con la función names() se pueden ver cuales son las opciones disponibles.

names(mapa)
## [1] "INSPIREID"  "COUNTRY"    "NATLEV"     "NATLEVNAME" "NATCODE"   
## [6] "NAMEUNIT"   "CODNUT1"    "CODNUT2"    "CODNUT3"
mapa_df <- fortify(model = mapa, region = "NATCODE")
mapa_df %>% head()



Metadatos


Además de las coordenadas, dentro del objeto SpatialPolygonsDataFrame hay información adicional sobre el mapa.

info_municipios <- mapa@data
info_municipios %>% head()

La variable NAMEUNIT es el nombre de la unidad, en este caso el nombre del municipio. La variable NATCODE es un código de identificación que sigue la siguiente estructura:

  • NATCODE: 34074000000
  • 34: País
  • 07: Comunidad Autónoma (Castilla y León)
  • 40: Provincia (Segovia)
  • 00000: Código Municipio

Se crean las columnas pais, c_autonoma, provincia y municipio a partir del NATCODE.

info_municipios <- info_municipios %>%
                    mutate(
                      pais       = str_sub(string = NATCODE, start = 1, end = 2),
                      c_autonoma = str_sub(string = NATCODE, start = 3, end = 4),
                      provincia  = str_sub(string = NATCODE, start = 5, end = 6),
                      municipio  = str_sub(string = NATCODE, start = 7, end = -1)
                    ) %>%
                    rename(nombre_municipio = NAMEUNIT)

# Se seleccionan las columnas de interés
info_municipios <- info_municipios %>%
                   select(
                     NATCODE, nombre_municipio, c_autonoma, provincia, municipio
                   )

Para poder unir la información de los municipios con el dataframe creado mediante fortify, es necesario un id común. En este caso, como se ha indicado region = "NATCODE" esta variable es el id común. Si no se hubiese especificado región, el id se corresponde con el orden de las filas (cuando se crea el dataframe con fortify(), el id se inicia en 0).

# Se añade la información de los municipios
mapa_df <- mapa_df %>%
           left_join(info_municipios, by = c("id" = "NATCODE"))



Gráfico


Cuantos más puntos (coordenadas) se emplean para crear el mapa, mayor es su resolución, pero también mayor el tiempo necesario para crear. Es este caso se reduce la resolución empleando únicamente 1 de cada 5 puntos.

# Se eliminan puntos (se reduce la resolución)
mapa_df <- mapa_df %>%
           slice(seq(1, nrow(mapa_df), 5))
mapa_df %>%
ggplot(aes(x = long, y = lat, group = group)) +
  geom_polygon(color = "gray20", fill = "white") + 
  coord_map("mercator") +
  theme_bw() +
  theme(
    axis.text.x = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks =  element_blank(),
    axis.title = element_blank(),
    panel.border = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank()
  )



Color


Al tratarse de un gráfico ggplot, se puede colorear cada elemento en función de cualquiera de las columnas del dataframe que contiene los datos del mapa.

Color por comunidad autónoma

mapa_df %>%
ggplot(aes(x = long, y = lat, group = group, fill = c_autonoma)) +
  geom_polygon(color = "black") +
  coord_map("mercator") +
  labs(title = "Municipios de España (peninsula y Baleares)",
       subtitle = "Color por comunidad autónoma") +
  theme_bw() +
  theme(
    legend.position = "none",
    axis.text.x = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks =  element_blank(),
    axis.title = element_blank(),
    panel.border = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank()
  )