El siguiente trabajo estudia la oferta de propiedades a lo largo del año 2020. Consta de tres secciones, la definición de objetivios, el desarrollo y la presentación de los resultados.
El trabajo solicitado se muestra en la sección “Presentación”, la sección de desarrollo es solamente para mostrar los pasos que llevaron al resultado en el caso que deba ser evaluado (puede saltearse completamente)
Se traen el dataset del 2020 que fue previamente descargado. Los datos están en formato SHP.
prop_2020 <- read_sf("2020/210129_Deptos_Vta_Anual_2020.shp")
#Filtro una serie de datos que no tiene indicado barrio y cuyos geometry estan mal especificados.
prop_2020 <- prop_2020 %>% filter(!is.na(Barrio))
Lo primero que podemos notar del dataset es que las propiedadfes aparecen varias veces. En la páginas de CABA, la metadata publicada es diferente a la que viene. Por tal motivo conviene agrupar por la ubicación (geometry) que es un identificador mas unívoco. Ahora el tema es ver como agrupar.
Para fines prácticos vamos a deshacernos de algunas columnas sobre las que no tenemos información para simplificar el set de datos: (PropiedadS, cotización,Direccion)
prop_2020 <- select(prop_2020, -c(PropiedadS, Cotizacion,Direccion))
El problema es que la geometria es un vector y no nos va a servir para el groupby, por tal motivo vamos a convertirlo en texto solo a fines de agrupar
prop_2020 <- prop_2020 %>%
mutate(geometryID = as.character(geometry),
geometryID = ifelse(is.na(geometry), "NA", geometry))
Ahora, como puede haber diferencias entre los valores del resto de los campos (por ejemplo, si fue publicada la oferta con diferentes precios para una misma ubicación), establecemos una agrupación y sumarizamos los otros campos por criterios adecuados.
#OBS: Para las pruebas uso 200 valores por tema de tiempos
prop_2020_summarized <- prop_2020 %>% group_by(geometryID,Comunas,Barrio,Trimestre) %>% summarise(
# prop_2020_summarized <- prop_2020[0:2000,] %>% group_by(geometryID,Comunas,Barrio,Trimestre) %>% summarise(
Dolares=sum(Dolares),
Pesos=sum(Pesos),
DolaresM2=mean(DolaresM2),
PesosM2=mean(PesosM2),
#Por las dudas que haya discrpancias tomo el máximo publicado
Ambientes=max(Ambientes),
MetrosOfertados = sum(Dolares/DolaresM2),
Ofertas = n()
)
#Guardamos los datos para la posteridad
#st_write(prop_2020_summarized, "data/prop_2020_summarized.shp")
##test <- prop_2020 <- read_sf("data/prop_2020_summarized.shp")
Con esto ya debemos tener un dataset bastante más prolijo que podemos mapear
ggplot() +
geom_sf(data= prop_2020_summarized,
aes(color = DolaresM2))+
scale_color_continuous(trans='log10', low="blue", high="white")+
labs(title = "Oferta de propiedades 2020 en CABA",
subtitle = "Valor del M2 en dolares",
color = "Dolares/M2") +
theme_void()
Claramente aún no podemos hacer nada útil con esta visualización. Probablemente por poca dispersión de datos en cuanto a los valores en m2 y los outliers importantes que seguramente estiran la escala hacia arriba:
Agrupamos por barrios:
radios <- read_sf("https://bitsandbricks.github.io/data/CABA_rc.geojson")
comunas <- radios %>% group_by(COMUNA) %>% summarise(POBLACION = sum(POBLACION, na.rm = TRUE))
comunas <- comunas %>% mutate(COMUNA = as.numeric(COMUNA))
prop_comunas <- st_drop_geometry(prop_2020_summarized) %>% group_by(Comunas) %>% summarise(DolaresM2 = mean(DolaresM2)) %>% left_join(comunas, by = c("Comunas" = "COMUNA"))
ggplot(prop_comunas) +
geom_sf(aes(geometry=geometry, fill = DolaresM2)) +theme_void()
Agrupamos por barrio:
barrios <- radios %>% group_by(BARRIO) %>% summarise(POBLACION = sum(POBLACION, na.rm = TRUE))
prop_barrio <- st_drop_geometry(prop_2020_summarized) %>% group_by(Barrio) %>% summarise(DolaresM2 = mean(DolaresM2)) %>% left_join(barrios, by = c("Barrio" = "BARRIO"))
ggplot(prop_barrio) +
geom_sf(aes(geometry=geometry, fill = DolaresM2)) +theme_void()
Este mapa tiene algunos problemas que abordamos a continuación.
Veamos como hay algunos Barrios que no son reconocidos por el dataset que refiere a barrios (radios). El primer problema que detectamos con una simple inspección +es que hay varios barrios que tienen nombres complejos.
prop_barrio %>% filter(str_detect(Barrio,"ESTE"))
## # A tibble: 2 x 4
## Barrio DolaresM2 POBLACION geometry
## <chr> <dbl> <dbl> <GEOMETRYCOLLECTION [°]>
## 1 BARRACAS ESTE 2412. NA GEOMETRYCOLLECTION EMPTY
## 2 BARRACAS OESTE 1760. NA GEOMETRYCOLLECTION EMPTY
Para poder mapear correctamente tenemos que homologar los nombres eliminando los terminos “Norte”, “Sur”, “Este”, “Oeste”. Para tal fin vamos a limpiar los datos en el set prop_2020_summarized
prop_2020_summarized_clean <- prop_2020_summarized
prop_2020_summarized_clean$Barrio <- prop_2020_summarized_clean$Barrio %>%
gsub("NORTE",'',.) %>%
gsub("SUR",'',.) %>%
gsub("OESTE",'',.) %>%
gsub("ESTE",'',.)
El segundo problema que podemos ver con una consulta es que no coinciden los nombres de los barrios en los datasets joineados. Tenemos que continuar homologando. Realizamos una sencilla operación de conjuntos, a nuestro set de datos le restamos todos los barrios que tenemos en nuestro set de referencia de barrios. Lo que encontramos así son los barrios que no podemos ubicar.
Obtenemos las listas de barrios de ambos sets y listamos los que tomaremos como referencia
Barrios_prop <- as.character(unique(unlist(prop_2020_summarized_clean$Barrio)))
Barrios_radios <- as.character(unique(unlist(radios$BARRIO)))
Barrios_radios
## [1] "RETIRO" "SAN NICOLAS" "PUERTO MADERO"
## [4] "MONSERRAT" "CONSTITUCION" "SAN TELMO"
## [7] "VILLA REAL" "FLORESTA" "VELEZ SARSFIELD"
## [10] "VILLA LURO" "VERSALLES" "MONTE CASTRO"
## [13] "VILLA DEVOTO" "VILLA DEL PARQUE" "VILLA SANTA RITA"
## [16] "VILLA GRAL. MITRE" "SAAVEDRA" "VILLA URQUIZA"
## [19] "VILLA PUEYRREDON" "COGHLAN" "NUÑEZ"
## [22] "BELGRANO" "PALERMO" "COLEGIALES"
## [25] "AGRONOMIA" "CHACARITA" "VILLA CRESPO"
## [28] "PARQUE CHAS" "VILLA ORTUZAR" "PATERNAL"
## [31] "RECOLETA" "BALVANERA" "SAN CRISTOBAL"
## [34] "NUEVA POMPEYA" "BARRACAS" "BOCA"
## [37] "PARQUE PATRICIOS" "ALMAGRO" "BOEDO"
## [40] "CABALLITO" "FLORES" "PARQUE CHACABUCO"
## [43] "VILLA LUGANO" "VILLA RIACHUELO" "VILLA SOLDATI"
## [46] "LINIERS" "PARQUE AVELLANEDA" "MATADEROS"
Ahora restamos estos barrios a nuestro set original y detectamos los problemáticos
setdiff(Barrios_prop,Barrios_radios)
## [1] "MONTSERRAT" "NU?æEZ" "FLORES " "VILLA DEVOTO "
## [5] "BARRACAS "
Vemos entonces que hay problemas de triming, de encoding y de ortografía. Intentamos solucionar el tema y ver si ahora la diferencia nos da vacia
prop_2020_summarized_clean$Barrio <- prop_2020_summarized_clean$Barrio %>%
gsub("MONTSERRAT",'MONSERRAT',.) %>%
gsub("NU.*EZ",'NUÑEZ',.) %>%
str_trim(.)
Barrios_prop <- as.character(unique(unlist(prop_2020_summarized_clean$Barrio)))
setdiff(Barrios_prop,Barrios_radios)
## character(0)
Veamos como se ve el último mapa con estas modificaciones.
barrios <- radios %>% group_by(BARRIO) %>% summarise(POBLACION = sum(POBLACION, na.rm = TRUE))
prop_barrio <- st_drop_geometry(prop_2020_summarized_clean) %>% group_by(Barrio) %>% summarise(DolaresM2 = mean(DolaresM2)) %>% left_join(barrios, by = c("Barrio" = "BARRIO"))
ggplot() + geom_sf(data = prop_barrio,aes(geometry=geometry, fill = DolaresM2)) +theme_void()
Notamos que el outlier que nos apreta la escala de colores es “Puerto Madero”, por lo tanto separamos los layers
barrios <- radios %>% group_by(BARRIO) %>% summarise(POBLACION = sum(POBLACION, na.rm = TRUE))
prop_barrio <- st_drop_geometry(prop_2020_summarized_clean) %>% group_by(Barrio) %>% summarise(DolaresM2 = mean(DolaresM2)) %>% left_join(barrios, by = c("Barrio" = "BARRIO"))
prop_barrio_sin_nunez <- prop_barrio %>% filter(!str_detect(Barrio,"PUERTO MADERO"))
prop_barrio_nunez <- prop_barrio %>% filter(str_detect(Barrio,"PUERTO MADERO"))
ggplot() + geom_sf(data = prop_barrio_sin_nunez,aes(geometry=geometry, fill = DolaresM2)) + geom_sf(data = prop_barrio_nunez,aes(geometry=geometry),fill = '#03c8ff') +theme_void()
Si hacemos un boxplot limpio nos encontramos con el problema de que hay algunos outliers puntuales que distorsionan mucho la escala. Son 3 donde el metro cuadrado esta por encima de 40.000 vamos a sacar estos para todos los sets de datos y vamos a ver como afecta todo.
ggplot(prop_2020_summarized_clean, aes(x=Barrio, y=DolaresM2)) +
geom_boxplot( ) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
prop_2020_summarized_clean <- prop_2020_summarized_clean %>% filter(DolaresM2 < 30000)
ggplot(prop_2020_summarized_clean, aes(x=Barrio, y=DolaresM2)) +
geom_boxplot( ) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
Lamentablemente el set de datos no tiene las fechas de las publicaciones pero si indica el trimestre. Vamos a analizar lo que se pueda con esta variable.
Con esto agrupamos por trimestre pero sumando las variables. Podríamos contar la cantidad de publicaciones para analizar la oferta comercial, sin embargo vamos a ensayar ver el crecimiento de la oferta de metros guardados calculando la división de la oferta por el valor del metro cuadrado. de esta forma lo que vamos a obtener es la cantidad de espacio ofrecido en cada barrio en cada trimestre (esta división ya fue hecha en el group by inicial).
prop_2020_summarized_clean_time <- prop_2020_summarized_clean %>% group_by(Barrio,Trimestre) %>% summarise(
Dolares=mean(Dolares),
Pesos=mean(Pesos),
DolaresM2=mean(DolaresM2),
PesosM2=mean(PesosM2),
Ambientes=max(Ambientes),
MetrosOfertados = sum(MetrosOfertados),
Ofertas = sum(Ofertas)
)
ggplot(prop_2020_summarized_clean_time, aes(fill=Barrio, y=MetrosOfertados, x=Trimestre)) +
geom_bar(position="dodge", stat="identity") +
theme(legend.position="none")
ggplot(prop_2020_summarized_clean_time, aes(fill=Barrio, y=Ofertas, x=Trimestre)) +
geom_bar(position="dodge", stat="identity") +
theme(legend.position="none")
Probando con el campo “MetrosOfertados”" se presentaba una suba abrupta en el segundo trimestre, pensando que podía ser un problema con algunos datos en la columna de “Dolares” por la cual se calculan los m2 hice el mismo gráfico con al campo que simplemente cuenta las ofertas y se presentó el mismo patrón, lo cual es bastante notable.
El siguiente trabajo analiza la oferta de propiedades publicadas en el 2020 en CABA para poder encontrar patrones de interés en la oferta y los valores de la tierra en la ciudad. Para ello tomamos el set de datos publicado por la ciudad de buenos aires y los datos geográficos relativos a los diferentes barrios y comunas de CABA.
La elección de este tema viene dada por el interés de indagar posteriormente en estudios similares en otros municipios sobre el que todavía estoy recabando fuentes de datos y organizando material. Las preguntas básicas que propongo son sencillas pero no dejan de ser interesantes. La presentación del material tiene por un lado el propósito de responder estas preguntas, pero también el de probar diferentes recursos visuales para trabajos posteriores.
Los objetivos propuestos para este trabajo son:
El siguiente mapa muestra el valor promedio del m2 en dólares obtenido a partir de toda la oferta inmobiliaria publicada para CABA en el 2020. En este análisis la unidad geográfica de interés son los barrios porteños. En el análisis surgió muy claramente una zona en particular que se despegaba del resto: Puerto Madero. Siendo el valor de este barrio tan alto, se decidió rellenarlo por separado para poder apreciar las diferencias en el resto de los barrios de forma más clara, ya que la diferencia entre los mismos es mucho menor.
ggplot() +
geom_sf(data = prop_barrio_sin_nunez,aes(geometry=geometry, fill = DolaresM2)) +
geom_sf(data = prop_barrio_nunez,aes(geometry=geometry),fill = '#fafa19')+
labs(title = "Mapa 1: Valor promedio de la propiedad por m2",
subtitle = "Barrios de la Ciudad Autónoma de Buenos Aires",
fill = "Promedio Dolares/m2") +
scale_fill_viridis_c() +
theme_void()
Para que el lector pueda consultar los valores de cada uno de los barrios, se ofrece una tabla interactiva. En la misma se pueden establecer filtros por rangos de valores para poder encontrar agrupamientos y categorizaciones.
prop_barrio_tbl <- prop_barrio[1:2]
datatable(prop_barrio_tbl,
caption = "Tabla 1: Valores promedio x m2 de propiedades ofrecidas en CABA 2020",
rownames = FALSE,
colnames=c("Barrio", "Valor m2"),
filter = 'top',
options = list(
searching = TRUE,
pageLength = 10,
autoWidth = TRUE,
language = list(url = '//cdn.datatables.net/plug-ins/1.10.11/i18n/Spanish.json')
)
) %>% formatRound('DolaresM2', 2) %>% formatCurrency('DolaresM2', 'USD ')
En el siguiente gráfico realizamos un análisis de la totalidad de las ofertas por barrió. Elegimos el boxplot para apreciar la dispersión existente entre los valores publicados en cada barrio y los outliers que indican hasta que tan alto y bajo se pueden llegar a realizar operaciones en cada barrio.
ggplot(prop_2020_summarized_clean,
aes(x=Barrio,
y=DolaresM2,
fill = "red")) +
geom_boxplot(outlier.colour= "#005eb0") +
labs(title="Gráfico 1: Valor de la propiedad x m2 (USD) en barrios de CABA 2020",
x = "Barrio",
y = "Valor x m2 (USD)") +
theme(axis.text.x = element_text(angle = 60, vjust = 1, hjust=1),
legend.position="none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black"))
Por último se presenta un gráfico de barras donde se muestra la cantidad de ofertas publicadas separadas por trimestre. Se utilizan barras para representar cada barrió (aunque no se etiquetan los mismos), no para entender que pasa en cada uno de ellos, sino más bien para ver si el patrón de conducta es consistente en todos ellos. Se puede ver claramente como el segundo trimestre es llamativamente mucho más activo que el resto en lo relativo a la oferta. Originalmente hice este análisis tomando un cálculo de los metros cuadrados ofrecidos, cuando vi este patrón extraños pensé que había cometido un error y por eso lo volví a analizar contando ahora la cantidad de ofertas (para descartar errores de cargas en valores). Para mi sorpresa, el mismo patrón se presentó con esta métrica y es lo que se puede ver a continuación.
prop_2020_summarized_clean_time$Trimestre <- factor(prop_2020_summarized_clean_time$Trimestre,levels = c("PRIMER", "SEGUNDO", "TERCER", "CUARTO"))
ggplot(prop_2020_summarized_clean_time, aes(fill=Barrio, y=Ofertas, x=Trimestre)) +
geom_bar(position="dodge", stat="identity") +
labs(title="Gráfico 2: Oferta m2 por trimestre y por barrio en CABA 2020",
subtitle = "(barrios representados en colores x orden alfabético)",
x = "Trimestre",
y = "Oferta m2") +
theme(legend.position="none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black"))
Los datos presentados en el mapa geográfico no arrojan sorpresas muy grandes. Se ve claramente como la zona norte de la ciudad tiene un valor por metro cuadrado superior a la zona sur. Lo sorprendente (o no tanto) fue ver la distancia que hay entre los valores de puerto madero y el segundo barrio más caro. Si vemos los barrios inmediatamente al sur de este barrio, podemos sospechar que podría ser de interés avanzar sobre estos espacios en el futuro para expandir el área de alto valor agregado de Puerto Madero. No es difícil pensar qu puede especularse con comprar propiedades baratas y sumarlas a la zona de Madero para generar negocios.
Por último es interesante y curiosos ver el patrón temporal que tiene la oferta de propiedades. El segundo trimestre se presenta realmente muy por encima del resto. Sería interesante indagar en series temporales anteriores, en particular pre pandémicas, para ver si esto es algo recurrente o excepcional. Sería interesante indagar las causas detrás de este fenómeno.