INFORMACIÓN GEOGRÁFICA Y MAPAS

Llegó el momento de visualizar información en un mapa. Pero antes de empezar, ¿A que nos referimos cuando hablamos de Sistemas de Información Geográfica o SIG? Bueno, nos referimos a las herramientas informáticas que nos permiten ubicar y analizar un conjunto de datos en lugares específicos del territorio (georreferenciar).

Para poder hacer uso de los SIG necesitamos contar con información geográfica en nuestro dataset, es decir, que además de la información que ya vimos que puede haber en un dataset tradicional, se sume un componente espacial en cada registro (partido, barrio, manzana, calle o directamente las coordenadas X e Y).

Las herramientas que nos permitirán visualizar toda esta información serán los mapas, que son nada más y nada menos que representaciones planas, reducidas y simplificadas de la tierra que nos dan la posibilidad cruzar y relacionar datos en el espacio. Es decir que, mantienen una relación ordenada en el traspaso de puntos ubicados en la superficie curva de la tierra a puntos ubicados en la superficie plana de los mapas. Esto es posible a partir del uso de sistemas de coordenadas proyectadas.

En R hay varios paquetes de funciones que nos permiten manipular este tipo de información, entre los que se encuentra sf, que lo aprenderemos hoy. Para comenzar a utilizarlo vamos a tener que instalarlo y luego activarlo con library() al igual que lo veníamos haciendo con tidyverse:

#install.packages(tidyverse)
library(tidyverse)
#install.packages(sf)
library(sf)

Como verán, activamos los 2 paquetes porque ambos presentan funciones que son necesarias a la hora de mapear información.

Analizar datos espaciales

Arranquemos cargando nuestro dataset espacial o shape con las geometrías correspondientes a todos los partidos de AMBA.

Recomendación: Al descargar el shape deberán moverlo de la carpeta “Descargas” a la carpeta del Proyecto donde estan trabajando.

Para poder cargar nuestros datos espaciales en formato geoJSON utilizaremos la función st_read() de la siguiente forma:

partidos_amba <- st_read("partidos_amba.geojson", stringsAsFactors = TRUE)
## Reading layer `partidos_amba' from data source `C:\Users\27356214477\Documents\FEPP-CDDPC-CLASE-1\partidos_amba.geojson' using driver `GeoJSON'
## Simple feature collection with 48 features and 3 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -59.3392 ymin: -35.23893 xmax: -57.70946 ymax: -34.23007
## Geodetic CRS:  WGS 84

Veamos que información contiene:

summary(partidos_amba)
##              nombre   provincia    area_km2               geometry 
##  Almirante Brown: 1   CABA:15   Min.   :  6.30   MULTIPOLYGON :48  
##  Avellaneda     : 1   GBA :33   1st Qu.: 17.72   epsg:4326    : 0  
##  Berazategui    : 1             Median : 55.91   +proj=long...: 0  
##  Berisso        : 1             Mean   :140.59                     
##  Comuna 1       : 1             3rd Qu.:177.07                     
##  Comuna 10      : 1             Max.   :889.49                     
##  (Other)        :42

Las primeras 3 columnas presentan el nombre del partido, la provincia y el área en km2. Hasta acá son datos muy similares a las que ya veníamos encontrando en los dataset tradicionales; sin embargo, aparece una 4ta columna llamada “geometry” que hasta ahora no la habíamos visto y es donde se aloja la geometría de cada uno de los registros. La información de este campo es la que hace que el dataset sea espacial.

Si queda alguna duda, podemos utilizar la función class() para ver con que tipo de datos estamos trabajando:

class(partidos_amba)
## [1] "sf"         "data.frame"

Efectivamente partidos_amba, es un dataset espacial u objeto del tipo “sf”, que hace referencia a “simple features” por estar compuesto de geometrías bidimensionales (polígono, punto, línea, multipunto, multilínea, etc.).

Para poder visualizar toda esta información plasmada en un mapa vamos a utilizar nuevamente ggplot() pero como en esta oportunidad queremos sumar capas geográficas (sf) trabajaremos con geom_sf(). Veamos un ejemplo:

ggplot(partidos_amba)+
  geom_sf()

Como habrán notado, la lógica en la estructura del chunk que utilizamos para hacer este mapa es la misma del capítulo anterior, donde dentro del ggplot() asignamos el dataset y luego sumamos la capa a graficar/mapear (en este caso un objeto sf).

En el mapa podemos ver como las geometrías de cada registro del dataset espacial son polígonos que representan los partidos de AMBA. Probemos agregar algún atributo estético aes():

ggplot(partidos_amba)+
  geom_sf(aes(fill=area_km2))

Ya tenemos nuestro primer mapa coroplético. Probemos ajustando algunas cuestiones estéticas: cambiemos la paleta (y su escala) y el color del borde de los polígonos.

ggplot(partidos_amba)+
  geom_sf(aes(fill=area_km2), color="white")+
  scale_fill_viridis_c(breaks=c(0,250,500,750,1000))

Pero nuestro shape tiene poca información no? No podemos hacer muchos análisis si solo contamos con la variable de superficie, así que seamos proactivos y agreguemos más datos!

Cruzar datos tradicionales y espaciales

Es muy común que a la hora de trabajar con datos no encontremos toda la información que necesitamos en un solo dataset. Frente a esto, lo que se hace es unir/cruzar datos provenientes de diferentes fuentes de información. En este apartado veremos como realizar estos cruces entre datos tradicionales y espaciales.

Para poder unir 2 set de datos ambos tienen que tener algo en común (alguna columna como por ejemplo un ID), sino sería imposible que R entienda que tiene que unir con qué. Por lo tanto, antes de unirlos podríamos necesitar manipularlos y realizarles diferentes tipos de transformaciones que nos permitan llegar a generar esta variable en común.

Por ejemplo en nuestro caso, si queremos unir algún dato a nuestro dataset espacial de partidos, deberíamos tener un valor único por cada nombre de partido (que funciona como el ID de mi dataset espacial). Probemos esto con algunos datos del Censo 2010:

partidos_censo2010 <- read.csv("partidos_censo2010.csv", stringsAsFactors = TRUE)

Veamos que tenemos:

summary(partidos_censo2010)
##      codigo                 nombre      pob_2010          viv_2010     
##  Min.   :2001   Almirante Brown: 1   Min.   :  56729   Min.   : 19287  
##  1st Qu.:2013   Avellaneda     : 1   1st Qu.: 174013   1st Qu.: 64864  
##  Median :6319   Berazategui    : 1   Median : 223281   Median : 93389  
##  Mean   :5075   Berisso        : 1   Mean   : 301108   Mean   :104104  
##  3rd Qu.:6544   Comuna 1       : 1   3rd Qu.: 340723   3rd Qu.:123359  
##  Max.   :6861   Comuna 10      : 1   Max.   :1775816   Max.   :447306  
##                 (Other)        :42                                     
##     hog_2010     
##  Min.   : 17116  
##  1st Qu.: 59537  
##  Median : 81055  
##  Mean   : 95612  
##  3rd Qu.:109566  
##  Max.   :484909  
## 

Bien, es un dataset tradicional con 5 variables o columnas: codigo, nombre, población 2010, cantidad de viviendas 2010 y cantidad de hogares 2010. Podemos analizar un poco los valores y ver por ejemplo que, en promedio los partidos de AMBA tienen 301108 habitantes, y que el más poblado tiene 1775816 y el menos poblado 56729.

Pero el detalle más importante es que hay una columna llamada “nombre” que tiene el nombre de nuestros partidos, y eso es una buena noticia ya que ella será quien nos permitirá unir ambos dataset (el espacial y el tradicional). Para esto utilizaremos la función llamada left_join():

partidos_amba <- left_join(partidos_amba, partidos_censo2010, by="nombre")

Revisemos el resultado!

summary(partidos_amba)
##              nombre   provincia    area_km2          codigo    
##  Almirante Brown: 1   CABA:15   Min.   :  6.30   Min.   :2001  
##  Avellaneda     : 1   GBA :33   1st Qu.: 17.72   1st Qu.:2013  
##  Berazategui    : 1             Median : 55.91   Median :6319  
##  Berisso        : 1             Mean   :140.59   Mean   :5075  
##  Comuna 1       : 1             3rd Qu.:177.07   3rd Qu.:6544  
##  Comuna 10      : 1             Max.   :889.49   Max.   :6861  
##  (Other)        :42                                            
##     pob_2010          viv_2010         hog_2010               geometry 
##  Min.   :  56729   Min.   : 19287   Min.   : 17116   MULTIPOLYGON :48  
##  1st Qu.: 174013   1st Qu.: 64864   1st Qu.: 59537   epsg:4326    : 0  
##  Median : 223281   Median : 93389   Median : 81055   +proj=long...: 0  
##  Mean   : 301108   Mean   :104104   Mean   : 95612                     
##  3rd Qu.: 340723   3rd Qu.:123359   3rd Qu.:109566                     
##  Max.   :1775816   Max.   :447306   Max.   :484909                     
## 

Efectivamente, se sumaron 4 nuevas columnas a mi dataset espacial: codigo, pob_2010, viv_2010 y hog_2010. ¿Qué esperamos? ¡Veamos alguna de ellas en un mapa!

ggplot(partidos_amba)+
  geom_sf(aes(fill=pob_2010), color="white")+
  scale_fill_viridis_c()

Cómo veran, hay un partido que resalta ya que es el más poblado: La Matanza. Pero no nos quedemos con esa idea, y para que estos datos sean comparables entre todos los partidos, es necesario que calculemos una densidad relacionando la cantidad de habitantes y la superficie (km2) de cada uno. También aprovechemos y mejoremos la estética agregando etiquetas (título, subtítulo, etc) y cambiando el aspecto (theme):

ggplot(partidos_amba)+
  geom_sf(aes(fill=pob_2010/area_km2), color=NA)+
    labs(title = "Población por Partido",
         subtitle = "Censo 2010",
         fill = "Población/km2",
         caption= "Fuente: INDEC") +
  scale_fill_distiller(palette = "Spectral") +
  theme_light()

Ahora el panorama cambió un poco y vemos como algunas comunas de CABA son las más densas. ¿Cómo hacemos si queremos ver este mapa con un “zoom” solo de CABA?

Tenemos que filtrar el dataset de la siguiente forma:

ggplot(partidos_amba %>% filter(provincia=="CABA"))+
  geom_sf(aes(fill=pob_2010/area_km2), color=NA)+
    labs(title = "Población por Partido",
         subtitle = "Censo 2010",
         fill = "Población/km2",
         caption= "Fuente: INDEC") +
  scale_fill_viridis_c(option = "plasma", direction=-1) +
  theme_light()

Ahora probemos con otra variable: cantidad de viviendas por km2.

ggplot(partidos_amba %>% filter(provincia=="CABA"))+
  geom_sf(aes(fill=viv_2010/area_km2), color=NA)+
    labs(title = "Viviendas por Partido",
         subtitle = "Censo 2010",
         fill = "Vivienda/km2",
         caption= "Fuente: INDEC") +
  scale_fill_viridis_c(option = "plasma", direction=-1) +
  theme_light()

Pongamos algunas etiquetas para poder diferenciar las comunas rápidamente!

ggplot(partidos_amba %>% filter(provincia=="CABA"))+
  geom_sf(aes(fill=viv_2010/area_km2), color=NA)+
  geom_sf_text(aes(label = nombre), size=2) +
    labs(title = "Densidad de Viviendas por Partido",
         subtitle = "Censo 2010",
         fill = "Vivienda/km2",
         caption= "Fuente: INDEC") +
  scale_fill_viridis_c(option = "plasma", direction=-1) +
  theme_light()
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Podemos ver que aparece la Comuna 6 como la más densa seguida por la 5. Sin embargo hay las densidades van de 4000 a 12000 habitantes por km2. Veamos cuáles son las más densas, por ejemplo las que tienen más de 8000 habitantes por km2:

ggplot(partidos_amba %>% filter(provincia=="CABA" & viv_2010/area_km2>=8000))+
  geom_sf(aes(fill=viv_2010/area_km2), color=NA)+
  geom_sf_text(aes(label = nombre), size=2) +
    labs(title = "Densidad de viviendas por Partido",
         subtitle = "Partidos con más de 8000 viviendas por km2.",
         fill = "Vivienda/km2",
         caption= "Fuente: Censo 2010. INDEC") +
  scale_fill_viridis_c(option = "plasma", direction=-1) +
  theme_light()
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Y por último agreguémosle el mapa de fondo así las comunas no quedan flotando:

ggplot()+
  geom_sf(data=partidos_amba %>% filter(provincia=="CABA"))+
  geom_sf(data=partidos_amba %>% filter(provincia=="CABA" & viv_2010/area_km2>=8000), aes(fill=viv_2010/area_km2))+
  geom_sf_text(data=partidos_amba %>% filter(provincia=="CABA" & viv_2010/area_km2>=8000), aes(label = nombre), size=2) +
    labs(title = "Densidad de viviendas por Partido",
         subtitle = "Partidos con más de 8000 viviendas por km2.",
         fill = "Vivienda/km2",
         caption= "Fuente: Censo 2010. INDEC") +
  scale_fill_viridis_c(option = "plasma", direction=-1) +
  theme_light()
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Variables Categóricas en el Mapa

Hasta acá todos los mapas que desarrollamos tomaron color a partir de una variable numérica (cantidad o densidad por km2), pero ¿Qué pasa si lo que queremos mapear es una variable categórica, cómo por ejemplo diferenciar aquellas que tienen más de 300000 habitantes por km2 de las que tienen menos?

Para resolver esta incógnita debemos generar una nueva variable que contenga la información:

partidos_amba <- partidos_amba %>%
  mutate(CATEGORIA=as.factor(if_else(pob_2010>=300000, "MAYOR DE 300K", "MENOR DE 300K")))
summary(partidos_amba)
##              nombre   provincia    area_km2          codigo    
##  Almirante Brown: 1   CABA:15   Min.   :  6.30   Min.   :2001  
##  Avellaneda     : 1   GBA :33   1st Qu.: 17.72   1st Qu.:2013  
##  Berazategui    : 1             Median : 55.91   Median :6319  
##  Berisso        : 1             Mean   :140.59   Mean   :5075  
##  Comuna 1       : 1             3rd Qu.:177.07   3rd Qu.:6544  
##  Comuna 10      : 1             Max.   :889.49   Max.   :6861  
##  (Other)        :42                                            
##     pob_2010          viv_2010         hog_2010               geometry 
##  Min.   :  56729   Min.   : 19287   Min.   : 17116   MULTIPOLYGON :48  
##  1st Qu.: 174013   1st Qu.: 64864   1st Qu.: 59537   epsg:4326    : 0  
##  Median : 223281   Median : 93389   Median : 81055   +proj=long...: 0  
##  Mean   : 301108   Mean   :104104   Mean   : 95612                     
##  3rd Qu.: 340723   3rd Qu.:123359   3rd Qu.:109566                     
##  Max.   :1775816   Max.   :447306   Max.   :484909                     
##                                                                        
##          CATEGORIA 
##  MAYOR DE 300K:17  
##  MENOR DE 300K:31  
##                    
##                    
##                    
##                    
## 

Excelente ya tenemos nuestra variable categórica que nos indica que 17 partidos tienen más de 300000 habitantes y 31 tienen menos. A mapear!

ggplot(partidos_amba)+
  geom_sf(aes(fill=CATEGORIA))

Nada mal no? Modifiquemos su estética como ya aprendimos:

ggplot(partidos_amba)+
  geom_sf(aes(fill=CATEGORIA), color="black") +
    labs(title = "Población por Partido",
         subtitle = "Censo 2010",
         fill = "Categoría",
         caption= "Fuente: INDEC") +
  scale_fill_manual(values=c("darkseagreen1", "cyan4"))+
  theme_light()

Agregar geometrías extra

Muchas veces nos vamos a encontrar con que queremos sumar más shapes o capas a nuestro mapa. ¿Cómo lo hacemos? Fácil! agregando más geom_sf() al ggplot. Veamos un ejemplo y carguemos el geoJSON de las líneas de FFCC:

lineas_ffcc <- st_read("lineas_ffcc.geojson")
## Reading layer `lineas_ffcc' from data source `C:\Users\27356214477\Documents\FEPP-CDDPC-CLASE-1\lineas_ffcc.geojson' using driver `GeoJSON'
## Simple feature collection with 23 features and 5 fields
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: -59.2763 ymin: -34.98144 xmax: -57.94964 ymax: -34.2941
## Geodetic CRS:  WGS 84
ggplot()+
  geom_sf(data=partidos_amba) +
  geom_sf(data=lineas_ffcc)

Ahora mejoremos un poco la estética:

ggplot()+
  geom_sf(data=partidos_amba) +
  geom_sf(data=lineas_ffcc, aes(color=as.factor(LINEA)), size=1) +
    labs(title = "Líneas de FFCC",
         subtitle = "AMBA",
         color = "Línea",
         caption= "Fuente: Min de Transporte") +
  theme_light()

Y listo! Ahora les toca practicar a uds!