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.
Arranquemos descargando de este link 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 `amba_partidos' from data source
## `C:\Users\27356214477\Desktop\DIPLO_CDDPC\partidos_amba.geojson'
## using driver `GeoJSON'
## Simple feature collection with 48 features and 2 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 area_km2 geometry
## Almirante Brown: 1 Min. : 6.30 MULTIPOLYGON :48
## Avellaneda : 1 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!
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 que pueden descargar desde este link:
partidos_censo2010 <- read.csv("partidos_censo2010.csv", stringsAsFactors = TRUE)
Veamos que tenemos:
summary(partidos_censo2010)
## codigo nombre provincia pob_2010
## Min. :2001 Almirante Brown: 1 CABA:15 Min. : 56729
## 1st Qu.:2013 Avellaneda : 1 GBA :33 1st Qu.: 174013
## Median :6319 Berazategui : 1 Median : 223281
## Mean :5075 Berisso : 1 Mean : 301108
## 3rd Qu.:6544 Comuna 1 : 1 3rd Qu.: 340723
## Max. :6861 Comuna 10 : 1 Max. :1775816
## (Other) :42
## viv_2010 hog_2010
## Min. : 19287 Min. : 17116
## 1st Qu.: 64864 1st Qu.: 59537
## Median : 93389 Median : 81055
## Mean :104104 Mean : 95612
## 3rd Qu.:123359 3rd Qu.:109566
## Max. :447306 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 area_km2 codigo provincia
## Almirante Brown: 1 Min. : 6.30 Min. :2001 CABA:15
## Avellaneda : 1 1st Qu.: 17.72 1st Qu.:2013 GBA :33
## 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 area_km2 codigo provincia
## Almirante Brown: 1 Min. : 6.30 Min. :2001 CABA:15
## Avellaneda : 1 1st Qu.: 17.72 1st Qu.:2013 GBA :33
## 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()
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 que están en este link:
lineas_ffcc <- st_read("lineas_ffcc.geojson")
## Reading layer `lineas_ffcc' from data source
## `C:\Users\27356214477\Desktop\DIPLO_CDDPC\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()
Si bien hasta aquí hemos hecho mapas bastante completos, nunca está de más agregarles un fondo que nos ayude a interpretar mejor el contexto. Para lograr esto utilizaremos la librería ggmap() que cómo a todas las librerías, primero debemos instalarla y luego activarla:
#install.packages(ggmap)
library(ggmap)
Para poder obtener un mapa de fondo con ggmap, primero tengo que delimintar cuál es mi “bounding box”, que hace referencia a los límites de un cuadro en el cual se encuentran todos mis datos. Esto lo haremos de la siguiente forma:
bbox_amba <- as.numeric(st_bbox(partidos_amba))
Una vez que ya tenemos el bbox, vamos a usar get_stamenmap() para descargar de internet el mapa. En este caso utilizaremos un mapa de tipo “toner-lite” pero hay otras opciones que pueden verse aquí: http://maps.stamen.com/
mapa_amba <- get_stamenmap(bbox = bbox_amba,
maptype = "toner-lite",
zoom=10)
Con ggmap()
veamos que nos hemos descargado:
ggmap(mapa_amba)
Ahora podemos reutilizar el código de alguno de los mapas anteriores, simplemente reemplazando la primera línea, donde iniciamos un objeto ggplot por una línea que llame a nuestro mapa base con ggmap()
:
ggmap(mapa_amba)+
geom_sf(data=partidos_amba, aes(fill=pob_2010/area_km2), color=NA, alpha=0.8, inherit.aes = FALSE)+
labs(title = "Población por Partido",
subtitle = "Censo 2010",
fill = "Población/km2",
caption= "Fuente: INDEC") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
Además de cambiar la primer línea de código, nótese que agregamos un inherit.aes=FALSE dentro del geom_sf()
para anular la estética predeterminada del objeto ggmap.
Listo! Ya tenemos un mapa con fondo y con información que generamos nosotros a partir de un cruce de 2 bases de datos.
Ahora les toca practicar a uds!