Gráficos en mapas con R feature image Photo Credit: Patricio Moracho

Gráficos en mapas con R

16 mins

A raíz de esta pregunta en StackOverflow se me dió por investigar las posibilidades que brinda Leaflet en R. La idea es jugar con alguna estadística y hacer un “mapa de calor” sobre un mapa geográfico. Decidí arrancar con una muy triste y lamentablemente instalada que es, la estadística de los femicidios.

Los datos a graficar

En datos.jus.gob.ar publican una muy completa lista de casos de femicidios en la República Argentina, con hechos desde el año 2008 y con una actualización mensual. Lo que lo primero que vamos a ser es buscar el enlace en la página al último archivo e importarlo a nuestro entorno R.

library(rgdal)
library(leaflet)
library(dplyr)
library(magrittr)

# Descarga del registro de femicidio
url <- "http://datos.jus.gob.ar/dataset/27bb9b2c-521b-406c-bdf9-98110ef73f34/resource/583cec9a-3022-4234-8b32-17692a267aac/download/registro-de-femicidios-20200109.csv"
file <- file.path(".", "data",basename(url))
download.file(url, file)


# Carga el Csv a un data.frame
femicidios <- read.table(file = file, header = TRUE, sep = ',', stringsAsFactors = FALSE)


glimpse(femicidios)

Observations: 1,325
Variables: 10
$ caso_numero              <int> 310, 402, 237, 565, 594, 703, 38, 31, 44, 14, 18, 8, 35, 36, 13, 20, 22, 
$ victima_edad             <int> 47, 7, 18, 46, 27, 42, 22, 41, NA, NA, 44, 45, 32, 10, 18, 34, 33, 33, 40
$ victima_identidad_genero <chr> "MUJER", "HOMBRE", "MUJER", "MUJER", "MUJER", "MUJER", "MUJER", "MUJER", 
$ victima_tipo             <chr> "PRINCIPAL", "VINCULADO", "PRINCIPAL", "PRINCIPAL", "PRINCIPAL", "PRINCIP…
$ hecho_provincia          <chr> "Río Negro", "Córdoba", "Santa Fe", "", "Neuquén", "Buenos Aires", "Entre
$ hecho_modalidad_comisiva <chr> "GOLPES", "ACUCHILLAMIENTO", "DISPARO DE BALA", "QUEMADURAS", "DISPARO DE…
$ hecho_fecha              <chr> "2014-11-20", "2015-08-04", "2014-11-02", "2016-01-22", "2016-01-24", "20
$ fuente_tipo              <chr> "MEDIO COMUNICACIÓN", "MEDIO COMUNICACIÓN", "MEDIO COMUNICACIÓN", "MEDIO …
$ fuente_link              <chr> "http://www.diariouno.com.ar/policiales/una-mendocina-fue-asesinada-su-pa
$ hecho_provincia_id       <int> 62, 14, 82, NA, 58, 6, 30, 14, 66, 6, 2, 50, 18, 30, 6, NA, 14, 22, 14, 1

Lo que me interesa hacer, es “mapear” estos datos con la provincia/estado en dónde ocurrió el crimen, para esto lo primer que hay que hacer es normalizar los nombres de las provincias ya que pareciera ser que no son muy rigurosos con estos datos, a veces cargan como Santa Fé y otras como Santa Fe, asimismo hay que unificar los nombres con el del archivo de polígonos geograficos que vamos a usar para que cada provincia tenga el mismo nombre. Bueno esta “normalización” la haremos así:

# Estandarización de los nombres de provincias
femicidios$hecho_provincia <- gsub('Ciudad Autónoma de Bs.As.', 'Ciudad de Buenos Aires', femicidios$hecho_provincia)
femicidios$hecho_provincia <- gsub('Entre Rios', 'Entre Ríos', femicidios$hecho_provincia)
femicidios$hecho_provincia <- gsub('Santa Fé', 'Santa Fe', femicidios$hecho_provincia)
femicidios$hecho_provincia <- gsub('Tucuman', 'Tucumán', femicidios$hecho_provincia)
femicidios$hecho_provincia <- gsub('Neuquen', 'Neuquén', femicidios$hecho_provincia)
femicidios_x_provincia <- as.data.frame(table(femicidios$hecho_provincia), stringsAsFactors = FALSE)

El mapa geográfico

Como ya comenté vamos a usar Leaflet para armar el mapa y colorearlo luego. Para poder usar esta librería, si no la tenemos instalada, simplemente con un install.packages("leaflet") alcanza. Además necesitaremos algún que otro paquete, detallando:

  • leaflet: Es la librería que generara el mapa para incorporar en una vista HTM. install.packages("leaflet")
  • rgdal: Unos “bindings” a la librería de abstracción de datos geográficos de Frank Warmerdam. install.packages("rgdal")

Bien, para empezar necesitamos obtener un shapefile esto es básicamente (entre otras cosas) una definición de polígonos que nos va permitir establecer las áreas de un mapa que corresponden a cada estado/provincia. Esta información la podemos obtener por ejemplo de biogeo, vamos a usar los “Global Administrative Boundaries (GADM)”, que justamente definen los contornos de los estados en cada país. Vamos a buscar puntualmente el de Argentina.

# Descarga del Shapefile de Argentina
tmp <- tempdir()
url <- "http://biogeo.ucdavis.edu/data/diva/adm/ARG_adm.zip"
file <- file.path(".", "data",basename(url))
download.file(url, file)
unzip(file, exdir = tmp)
argentina <- readOGR(dsn = tmp, layer = "ARG_adm1", use_iconv=TRUE, encoding='UTF-8', stringsAsFactors=FALSE)

Básicamente descargamos el archivo a una carpeta temporal y lo descomprimimos. Hay que analizar el contenido para ubicar el “layer”, que no es más que un clásico archivo DBF, analizándolo pude detectar que se trataba de “ARG_adm1” el que contenía las definiciones de los 24 estados del país. Lo siguiente es usar readOGR una función del paquete rgdal que simplemente transforma el archivo DBF en un vector de datos geo-espaciales.

Combinando datos y geografía

En esta instancia tenemos datos estadísticos en nuestro data.farme femicidios_x_provincia y tenemos la información geográfica en un vector del tipo SpatialPolygonsDataFrame llamado argentina. Ambos vectores comparten una dato común que es el nombre de la provincia, que aprovecharemos entonces para combinar los mismos.

Para combinar las dos fuentes de información primero debemos normalizar el nombre del campo de provincia, para que luego un “merge” nos funcione fácilmente, para esto simplemente a femcidios modificamos la columna “lugar_hecho” por “NAME_1”, con este simple cambio ya podemos combinar la información. Importante: el merge común de R no me funciono del todo bien ya que el resultado final modificaba el orden de los datos, lo que producía que al graficar las provincias salieran mal identificadas. Por lo que optamos por la función join de plyr que es bastante más poderosa.


# Renombrar las columnas para hacer merge
colnames(femicidios_x_provincia) <- c("NAME_1", "Femicidios")
# Merge de los datos
argentina@data <- left_join(argentina@data,femicidios_x_provincia, by = c("NAME_1"))

head(argentina@data[c(1,2,3,4,5,11)])

  ID_0 ISO    NAME_0 ID_1                 NAME_1 femPob
1   12 ARG Argentina    1           Buenos Aires  22223
2   12 ARG Argentina    2                Córdoba  14442
3   12 ARG Argentina    3              Catamarca  12969
4   12 ARG Argentina    4                  Chaco  11964
5   12 ARG Argentina    5                 Chubut  12867
6   12 ARG Argentina    6 Ciudad de Buenos Aires  36247

Por último, la parte linda y divertida de todo esto:

pal <- colorQuantile("YlGn", NULL, n = 5)
state_popup <- paste0("<strong>Estado: </strong>", 
                      argentina$NAME_1, 
                      "<br><strong>Femicidios: </strong>", 
                      argentina$Femicidios)

leaflet(data = argentina) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(fillColor = ~pal(femicidios_x_provincia$Femicidios), 
              fillOpacity = 0.8, 
              color = "#BDBDC3", 
              weight = 1, 
              popup = state_popup)

Y obtenemos algo como esto:

ejemplo1

Este gráfico tiene un problema, hay una variable “oculta” que puede inducir al error, lo que ocurre que nos puede hacer suponer, que iertas provincias están más complicadas en cuanto al nivel de femicidios (son las del tono verde más oscuro), pero lo cierto es que tambien los niveles de población son distintos en cada una. Por lejos Buenos Aires es la de mayor población, por lo que el número de femicidios sería bueno ponderarlo en función a esto. La idea ahora sería simplemente tomar el número duro de femicidios y dividirlo por algún indicador con respecto al nivel de población femenina.

Vamos a ir a buscar los datos del último censo de población del año 2010, el Indec maneja unas proyecciones de población por género y provincia que nos van a ser muy útiles, las vamos a descargar de este, los datos son básicamente una planilla Excel nada amigable para hacer trabajarla, por lo que ya la procesé para que sea más cómoda, se puede descargar desde aquí enlace.

url <- "https://raw.githubusercontent.com/pmoracho/R/master/femicidios.ar/data/poblacion.csv"
file <- file.path(".", "data", basename(url))
download.file(url, file)
poblacion <- read.csv(file, stringsAsFactors = FALSE)

Con esto, tenemos un data.frame como el siguiente:

> glimpse(poblacion)

Observations: 744
Variables: 5
$ year    <dbl> 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 
$ total   <chr> "3028481", "3033639", "3038860", "3044076", "3049229", "3054267", "3059122", "3063728", "3…
$ varones <chr> "1405566", "1409835", "1414105", "1418339", "1422507", "1426582", "1430531", "1434323", "1
$ mujeres <chr> "1622915", "1623804", "1624755", "1625737", "1626722", "1627685", "1628591", "1629405", "1
$ cod_prv <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 

Ahora, teniendo la proyección de mujeres del año 2017, lo que hacemos es agregar todos los datos al shapefile original

# Merge de los datos
nyear = 2020
argentina@data %>% 
  left_join(poblacion %>% 
              filter(year == nyear) %>% 
              select(provincia, mujeres), by= c("NAME_1" = "provincia")) %>% 
  mutate(femPob = round(mujeres/Femicidios)) %>% 
  select(-mujeres) -> argentina@data  

Cambiamos un poco el estilo de gráfico y gráficamos en función de: argentina$femPob, es decir los femicidios en función de la cantidad de mujeres

pal <- colorQuantile(palette = "Blues", domain = NULL, n = 5, reverse = TRUE)
state_popup <- paste0("<strong>Provincia: </strong>", 
                      argentina$NAME_1, 
                      "<br><strong>1 femicidio cada </strong>", 
                      argentina$femPob,
                      " <strong>mujeres</strong>")

leaflet(data = argentina) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(fillColor = ~pal(femPob), 
              fillOpacity = 0.8, 
              color = "#BDBDC3", 
              weight = 1, 
              popup = state_popup)

Y aquí el resultado final:

ejemplo2

Ahora si podemos ver realmente en que provincias los femicidios son más significativos..

La descarga final de los scripts puede hacerse desde aquí


@pmoracho QR code
https://pmoracho.github.io/blog/2017/05/11/Graficos-con-mapas-en-R/
11-May-17
BY-NC-SA 4.0 https://pmoracho.github.io/disclosure
https://pmoracho.github.io/blog/2017/05/11/Graficos-con-mapas-en-R/