Llegó el momento de visualizar en un mapa toda la información que estuvimos analizando en las clases anteriores. Para esto, vamos a trabajar con la información geográfica que contiene nuestro dataset y vamos sumar otras fuentes de datos geográficos.
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
:
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.
Ahora si ya estamos en condiciones de conocer y analizar datos espaciales. Para esto, volvamos a cargar nuestros datos de Properati:
datos_amba <- read.csv("data/amba_properati2021.csv")
summary(datos_amba)
## created_on provincia partido rooms
## Min. :202105 Length:19279 Length:19279 Min. : 1.000
## 1st Qu.:202105 Class :character Class :character 1st Qu.: 2.000
## Median :202105 Mode :character Mode :character Median : 3.000
## Mean :202105 Mean : 3.201
## 3rd Qu.:202106 3rd Qu.: 4.000
## Max. :202106 Max. :10.000
## surface_total surface_covered price currency
## Min. : 14.0 Min. : 14.00 Min. : 10000 Length:19279
## 1st Qu.: 51.0 1st Qu.: 46.00 1st Qu.: 75000 Class :character
## Median : 77.0 Median : 67.00 Median : 129900 Mode :character
## Mean : 118.5 Mean : 95.28 Mean : 190626
## 3rd Qu.: 140.0 3rd Qu.:114.00 3rd Qu.: 220000
## Max. :4400.0 Max. :890.00 Max. :2950000
## title property_type operation_type lat
## Length:19279 Length:19279 Length:19279 Min. :-35.12
## Class :character Class :character Class :character 1st Qu.:-34.64
## Mode :character Mode :character Mode :character Median :-34.60
## Mean :-34.61
## 3rd Qu.:-34.56
## Max. :-34.15
## lon
## Min. :-59.02
## 1st Qu.:-58.53
## Median :-58.46
## Mean :-58.46
## 3rd Qu.:-58.40
## Max. :-57.85
Y utilizando lo aprendido en la clase anterior, hagamos un primer gráfico de puntos con geom_point()
asignando la variable longitud (lon) en el eje X y la latitud (lat) en el Y:
ggplot(datos_amba) +
geom_point(aes(x=lon, y=lat, color=provincia))
Con mucha imaginación uno puede darse cuenta que los datos tienen la forma de AMBA, pero está faltando información que nos facilite la lectura del mismo. Para esto sumemos un dataset más a nuestro proyecto que en este caso, será un dataset espacial en formato geoJSON con las geometrías correspondientes a todos los partidos de AMBA. Pueden descargarlo en el siguiente link.
Recomendación: Al igual que con el csv que trabajamos en la clase anterior, al descargar el shape deberán moverlo de la carpeta “Descargas” a la carpeta llamada “data” dentro del Proyecto donde están trabajando.
Para poder cargar nuestros datos espaciales en formato shp utilizaremos la función st_read()
de la siguiente forma:
partidos_amba <- st_read("data/partidos_amba.geojson")
## Reading layer `partidos_amba' from data source
## `C:\Users\27356214477\Desktop\PROPERATI_2022\data\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:
head(partidos_amba)
## Simple feature collection with 6 features and 3 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -59.05579 ymin: -34.91331 xmax: -58.27953 ymax: -34.26732
## Geodetic CRS: WGS 84
## nombre provincia area_km2 geometry
## 1 Avellaneda GBA 57.25 MULTIPOLYGON (((-58.33444 -...
## 2 Tigre GBA 381.99 MULTIPOLYGON (((-58.5167 -3...
## 3 Pilar GBA 382.95 MULTIPOLYGON (((-58.90312 -...
## 4 Moreno GBA 186.36 MULTIPOLYGON (((-58.82401 -...
## 5 Merlo GBA 173.97 MULTIPOLYGON (((-58.72917 -...
## 6 La Matanza GBA 328.26 MULTIPOLYGON (((-58.52885 -...
Las primeras 3 columnas presentan el nombre del partido, la provincia y el área en km2. Estos 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(datos_amba)
## [1] "data.frame"
Tal como lo imaginábamos, datos_amba es un simple dataframe.
class(partidos_amba)
## [1] "sf" "data.frame"
Pero 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 de la clase 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))
Y ahora si queremos visualizar en un mismo mapa el dataset espacial (partidos_amba) y el dataset tradicional (datos_amba) tenemos que tener en cuenta 2 cosas:
Cuando utilizamos 2 capas (en este caso geom_sf()
y geom_point()
), tenemos que asignarle a cada una el dataset dentro de la capa y ggplot()
queda “vacío”.
Las capas se grafican en el mismo orden que se escribe el código, es decir que la que agregamos primero (en este caso partidos_amba) será el fondo de la que agreguemos luego (datos_amba).
Veamos esto en detalle:
ggplot()+
geom_sf(data=partidos_amba)+
geom_point(data=datos_amba, aes(x=lon, y=lat, color=provincia))
En el mapa anterior hay tantos puntos que nos resulta muy difícil poder encontrar patrones que nos permitan entender donde se concentran más cantidad de puntos y donde menos. En estos casos podemos recurrir a los mapas de densidad de puntos que nos ayudarán a encontrar los “hot spots”.
En R hay varias formas de mapear esto, pero hoy optaremos por geom_bin2d()
, que divide el plano en una grilla y cuenta la cantidad de puntos que aparecen en cada celda.
ggplot()+
geom_sf(data=partidos_amba)+
geom_bin2d(data = datos_amba, aes(x = lon, y = lat))
Ahora si nos encontramos con un hot spot que antes no veíamos. Se ve muy claro como en la zona de Palermo se concentran la mayor cantidad de propiedades publicadas en Mayo y Junio 2021 en Properati.
Mejoremos la visualización agregando etiquetas, cambiando el color, agregando transparencia y ajustando el tamaño de los bins (celdas):
ggplot()+
geom_sf(data=partidos_amba)+
geom_bin2d(data = datos_amba, aes(x = lon, y = lat), alpha=0.75, bins=50)+
labs(title="Densidad de publicaciones",
fill="Cantidad")+
scale_fill_viridis_c()
¿Y cómo hacemos si queremos ver este mapa con un “zoom” solo de CABA?
Tenemos que filtrar ambas capas así:
ggplot()+
geom_sf(data=filter(partidos_amba, provincia=="CABA"))+
geom_bin2d(data = filter(datos_amba, provincia=="CABA"), aes(x = lon, y = lat), alpha=0.75)+
labs(title="Densidad de publicaciones",
fill="Cantidad")+
scale_fill_viridis_c()
Viendo el mapa anterior nos cambia un poco la percepción y ya no parece ser solo la Comuna 14 y alrededores donde están la mayor densidad de propiedades, sino que se ve una zona con gran densidad en la Comuna 6 (Caballito).
Pero bueno, a pesar de haber llegado a un mapa que nos permitió sacar muchas conclusiones, aún estamos trabajando con 2 dataset separados que no se han unido. ¡Veamos como cruzar y unificar todos estos 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 deberemos 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).
Trabajemos con las propiedades en venta y manipulemos un poco el dataset tradicional para que podamos unirlos:
datos_amba_venta <- datos_amba %>%
filter(operation_type=="Venta") %>%
group_by(partido) %>%
summarise(cantidad=n(),
valor_m2=mean(price/surface_covered))
head(datos_amba_venta)
## # A tibble: 6 x 3
## partido cantidad valor_m2
## <chr> <int> <dbl>
## 1 Almirante Brown 55 1486.
## 2 Avellaneda 222 1375.
## 3 Berazategui 96 1530.
## 4 Berisso 3 807.
## 5 Cañuelas 10 1123.
## 6 Comuna 1 792 3002.
Bien, ahora que tenemos un dataset de 50 observaciones/registros (partidos) donde para cada partido hay 2 columnas con valores asociados (cantidad y valor del m2), ya estamos en condiciones de hacer una unión con el dataset espacial.
Para esto utilizaremos la función left_join()
:
partidos_amba <- left_join(partidos_amba, datos_amba_venta, by=c("nombre"="partido"))
head(partidos_amba)
## Simple feature collection with 6 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -59.05579 ymin: -34.91331 xmax: -58.27953 ymax: -34.26732
## Geodetic CRS: WGS 84
## nombre provincia area_km2 cantidad valor_m2
## 1 Avellaneda GBA 57.25 222 1374.824
## 2 Tigre GBA 381.99 756 2222.003
## 3 Pilar GBA 382.95 614 1459.873
## 4 Moreno GBA 186.36 141 1168.330
## 5 Merlo GBA 173.97 73 1173.515
## 6 La Matanza GBA 328.26 335 1432.773
## geometry
## 1 MULTIPOLYGON (((-58.33444 -...
## 2 MULTIPOLYGON (((-58.5167 -3...
## 3 MULTIPOLYGON (((-58.90312 -...
## 4 MULTIPOLYGON (((-58.82401 -...
## 5 MULTIPOLYGON (((-58.72917 -...
## 6 MULTIPOLYGON (((-58.52885 -...
Tal como esperábamos, se agregaron 2 nuevas columnas: cantidad y valor_m2.
Veamos esto en un mapa:
ggplot(partidos_amba)+
geom_sf(aes(fill=valor_m2))+
scale_fill_distiller(palette = "Spectral")
Al igual que en los gráficos, en los mapas pueden agregarse etiquetas (título, subtítulo, etc) y cambiar el aspecto (theme):
ggplot()+
geom_sf(data=partidos_amba, aes(fill=valor_m2), color=NA) +
labs(title = "Valor del m2 en Venta por Partido",
subtitle = "AMBA - Mayo y Junio 2021",
fill = "USD/m2",
caption= "Fuente: Properati") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
Ahora veamos como se distribuye espacialmente la otra variable: cantidad de propiedades publicadas para venta.
ggplot()+
geom_sf(data=partidos_amba, aes(fill=cantidad), color=NA) +
labs(title = "Cantidad de publicaciones de Venta por Partido",
subtitle = "AMBA - Mayo y Junio 2021",
fill = "Cantidad",
caption= "Fuente: Properati") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
En el mapa se ve como la Comuna 14 tiene el valor más alto, sin embargo, para que estos datos sean comparables entre todos los partidos, es necesario que calculemos una densidad relacionando la cantidad de propiedades y la superficie (km2) de cada uno:
ggplot()+
geom_sf(data=partidos_amba, aes(fill=cantidad/area_km2), color=NA) +
labs(title = "Densidad de publicaciones de Venta por Partido",
subtitle = "AMBA - Mayo y Junio 2021",
fill = "Densidad",
caption= "Fuente: Properati") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
El mapa cambia bastante, ahora todos los partidos de AMBA son color azul, es decir que tienen baja densidad y los de CABA se ven coloreados. Hagamos un zoom en CABA y veamos esto en detalle:
ggplot()+
geom_sf(data=filter(partidos_amba, provincia=="CABA"), aes(fill=cantidad/area_km2), color=NA) +
labs(title = "Densidad de publicaciones de Venta por Comuna",
subtitle = "CABA - Mayo y Junio 2021",
fill = "Densidad",
caption= "Fuente: Properati") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
Podemos ver que aparece la Comuna 2 como la más densa seguida por la 14 y la 6. Sin embargo hay varias que tienen una densidad mayor a 60 publicaciones por km2. Veamos cuáles son:
ggplot()+
geom_sf(data=filter(partidos_amba, provincia=="CABA" & cantidad/area_km2>=60), aes(fill=cantidad/area_km2), color=NA) +
geom_sf_label(data=filter(partidos_amba, provincia=="CABA" & cantidad/area_km2>=60), aes(label = nombre), size=2) +
labs(title = "Comunas con más de 60 publicaciones por km2",
subtitle = "CABA - Mayo y Junio 2021",
fill = "Densidad",
caption= "Fuente: Properati") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
Y por último agreguémosle el mapa de fondo así las comunas no quedan flotando:
ggplot()+
geom_sf(data=filter(partidos_amba, provincia=="CABA")) +
geom_sf(data=filter(partidos_amba, provincia=="CABA" & cantidad/area_km2>=60), aes(fill=cantidad/area_km2)) +
geom_sf_label(data=filter(partidos_amba, provincia=="CABA" & cantidad/area_km2>=60), aes(label = nombre), size=2) +
labs(title = "Cantidad de publicaciones de Venta por Comuna",
subtitle = "CABA - Mayo y Junio 2021",
fill = "Cantidad",
x="",
y="",
caption= "Fuente: Properati") +
scale_fill_distiller(palette = "Spectral") +
theme_light()
Variables Categóricas en el Mapa
Hasta acá todos los mapas que desarrollamos tomaron color a partir de una variable numérica (cantidad o valor del m2), pero ¿Qué pasa si lo que queremos mapear es una variable categórica, cómo por ejemplo la tipología que más aparece publicada por partido?
Para resolver esta incógnita debemos generar una nueva variable que contenga la información y pueda unirse a nuestro dataset espacial partidos_amba:
datos_amba_tipologia <- datos_amba %>%
group_by(partido, property_type) %>%
summarise(cant_max=n()) %>%
filter(cant_max==max(cant_max))
head(datos_amba_tipologia)
## # A tibble: 6 x 3
## # Groups: partido [6]
## partido property_type cant_max
## <chr> <chr> <int>
## 1 Almirante Brown Casa 42
## 2 Avellaneda Departamento 146
## 3 Berazategui Casa 70
## 4 Berisso Casa 4
## 5 Cañuelas Casa 10
## 6 Comuna 1 Departamento 981
partidos_amba <- partidos_amba %>%
left_join(datos_amba_tipologia, by=c("nombre"="partido"))
head(partidos_amba)
## Simple feature collection with 6 features and 7 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -59.05579 ymin: -34.91331 xmax: -58.27953 ymax: -34.26732
## Geodetic CRS: WGS 84
## nombre provincia area_km2 cantidad valor_m2 property_type cant_max
## 1 Avellaneda GBA 57.25 222 1374.824 Departamento 146
## 2 Tigre GBA 381.99 756 2222.003 Departamento 448
## 3 Pilar GBA 382.95 614 1459.873 Casa 498
## 4 Moreno GBA 186.36 141 1168.330 Casa 113
## 5 Merlo GBA 173.97 73 1173.515 Casa 48
## 6 La Matanza GBA 328.26 335 1432.773 Departamento 162
## geometry
## 1 MULTIPOLYGON (((-58.33444 -...
## 2 MULTIPOLYGON (((-58.5167 -3...
## 3 MULTIPOLYGON (((-58.90312 -...
## 4 MULTIPOLYGON (((-58.82401 -...
## 5 MULTIPOLYGON (((-58.72917 -...
## 6 MULTIPOLYGON (((-58.52885 -...
Efectivamente, se sumaron 2 nuevas columnas: property_type y cant_max. Ahora si, mapiemos por la tipología que más aparece:
ggplot(partidos_amba)+
geom_sf(aes(fill=property_type))
Y modifiquemos su estética como ya aprendimos:
ggplot(partidos_amba)+
geom_sf(aes(fill=property_type), color="black") +
labs(title = "Tipología que predomina en las publicaciones por Partido",
subtitle = "AMBA - Mayo y Junio 2021",
fill = "Tipología",
caption= "Fuente: Properati") +
scale_fill_manual(values=c("darkseagreen1", "cyan4"))+
theme_light()
Esto es todo por hoy! Ahora les toca practicar a uds!