---
title: "Monitor de Precios del Suelo - Mar del Plata"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
theme: cosmo
source_code: embed
---
```{r setup, include=FALSE}
library(flexdashboard)
library(tidyverse)
library(leaflet)
library(plotly)
library(DT)
library(scales)
# Cargar los datos
# Si ya tienes tabla_zonaprop_filter en el ambiente, úsalo directamente
tabla_zonaprop_filter <- readRDS("F:/RSTUDIO/terrenos/data/tabla_zonaprop_filter.rds")
# Limpiar datos (eliminar coordenadas no válidas)
datos <- tabla_zonaprop_filter %>%
filter(!coordenadas %in% c("No disponible", "Sin coordenadas", "Offline", "Error", NA)) %>%
filter(!is.na(lat) & !is.na(long)) %>%
filter(precio > 0 & m2ok > 0 & precio_m2 > 0)
# Crear categorías de precio
datos <- datos %>%
mutate(
rango_precio = cut(precio,
breaks = c(0, 50000, 100000, 200000, 500000, Inf),
labels = c("< $50k", "$50k-$100k", "$100k-$200k", "$200k-$500k", "> $500k")),
rango_m2 = cut(m2ok,
breaks = c(0, 500, 1000, 2000, 5000, Inf),
labels = c("< 500m²", "500-1000m²", "1000-2000m²", "2000-5000m²", "> 5000m²"))
)
```
# Overview
## Row {data-height=150}
### Total Propiedades
```{r}
valueBox(
nrow(datos),
icon = "fa-home",
color = "primary"
)
```
### Precio Promedio por m²
```{r}
valueBox(
paste0("USD ", round(mean(datos$precio_m2, na.rm = TRUE), 2)),
icon = "fa-dollar-sign",
color = "success"
)
```
### Precio Mediano Total
```{r}
valueBox(
paste0("USD ", format(median(datos$precio, na.rm = TRUE), big.mark = ",")),
icon = "fa-chart-line",
color = "info"
)
```
### Superficie Promedio
```{r}
valueBox(
paste0(round(mean(datos$m2ok, na.rm = TRUE), 0), " m²"),
icon = "fa-ruler-combined",
color = "warning"
)
```
## Row {data-height=850}
### Mapa Interactivo de Propiedades
```{r}
# Paleta de colores según precio por m²
pal <- colorNumeric(
palette = "YlOrRd",
domain = datos$precio_m2
)
# Crear el mapa
leaflet(datos) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
lng = ~long,
lat = ~lat,
radius = ~sqrt(m2ok) / 10,
color = ~pal(precio_m2),
fillOpacity = 0.7,
stroke = TRUE,
weight = 1,
popup = ~paste0(
"<b>Ubicación:</b> ", loc2, "<br>",
"<b>Precio:</b> USD ", format(precio, big.mark = ","), "<br>",
"<b>Superficie:</b> ", m2ok, " m²<br>",
"<b>Precio/m²:</b> USD ", round(precio_m2, 2), "<br>",
"<b>Dirección:</b> ", loc1, "<br>",
"<a href='", link, "' target='_blank'>Ver publicación</a>"
),
label = ~paste0(loc2, " - USD ", format(precio, big.mark = ","))
) %>%
addLegend(
position = "bottomright",
pal = pal,
values = ~precio_m2,
title = "Precio por m²<br>(USD)",
opacity = 1
) %>%
setView(lng = -57.57, lat = -38.00, zoom = 11)
```
# Análisis de Precios
## Row
### Distribución de Precios Totales
```{r}
p1 <- ggplot(datos, aes(x = precio)) +
geom_histogram(bins = 50, fill = "#3498db", alpha = 0.7) +
scale_x_continuous(labels = dollar_format(prefix = "USD ")) +
labs(
title = "Distribución de Precios",
x = "Precio Total (USD)",
y = "Frecuencia"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
ggplotly(p1)
```
### Distribución de Precio por m²
```{r}
p2 <- ggplot(datos, aes(x = precio_m2)) +
geom_histogram(bins = 50, fill = "#e74c3c", alpha = 0.7) +
scale_x_continuous(labels = dollar_format(prefix = "USD ")) +
labs(
title = "Distribución de Precio por m²",
x = "Precio por m² (USD)",
y = "Frecuencia"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
ggplotly(p2)
```
## Row
### Relación Superficie vs Precio
```{r}
p3 <- ggplot(datos, aes(x = m2ok, y = precio, color = precio_m2)) +
geom_point(alpha = 0.6, size = 2) +
scale_color_gradient(low = "#2ecc71", high = "#e74c3c", name = "Precio/m²") +
scale_x_continuous(labels = comma_format()) +
scale_y_continuous(labels = dollar_format(prefix = "USD ")) +
labs(
title = "Relación entre Superficie y Precio",
x = "Superficie (m²)",
y = "Precio Total (USD)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
ggplotly(p3)
```
### Precio por m² según Rango de Superficie
```{r}
p4 <- datos %>%
group_by(rango_m2) %>%
summarise(
precio_m2_medio = mean(precio_m2, na.rm = TRUE),
n = n()
) %>%
ggplot(aes(x = rango_m2, y = precio_m2_medio, fill = rango_m2)) +
geom_col(alpha = 0.8) +
geom_text(aes(label = paste0("n=", n)), vjust = -0.5, size = 3) +
scale_y_continuous(labels = dollar_format(prefix = "USD ")) +
scale_fill_brewer(palette = "Set2") +
labs(
title = "Precio Promedio por m² según Superficie",
x = "Rango de Superficie",
y = "Precio Promedio por m² (USD)"
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold"),
legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1)
)
ggplotly(p4)
```
# Por Zona
## Row
### Top 10 Zonas por Precio Promedio
```{r}
top_zonas <- datos %>%
group_by(loc2) %>%
summarise(
precio_promedio = mean(precio, na.rm = TRUE),
precio_m2_promedio = mean(precio_m2, na.rm = TRUE),
n_propiedades = n()
) %>%
filter(n_propiedades >= 5) %>%
arrange(desc(precio_m2_promedio)) %>%
head(10)
p5 <- ggplot(top_zonas, aes(x = reorder(loc2, precio_m2_promedio), y = precio_m2_promedio)) +
geom_col(aes(fill = precio_m2_promedio), alpha = 0.8) +
geom_text(aes(label = paste0("n=", n_propiedades)), hjust = -0.2, size = 3) +
scale_y_continuous(labels = dollar_format(prefix = "USD ")) +
scale_fill_gradient(low = "#3498db", high = "#e74c3c", name = "Precio/m²") +
coord_flip() +
labs(
title = "Top 10 Zonas por Precio/m² (mín. 5 propiedades)",
x = "",
y = "Precio Promedio por m² (USD)"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
ggplotly(p5)
```
### Número de Propiedades por Zona
```{r}
zonas_count <- datos %>%
count(loc2, sort = TRUE) %>%
head(15)
p6 <- ggplot(zonas_count, aes(x = reorder(loc2, n), y = n)) +
geom_col(fill = "#9b59b6", alpha = 0.8) +
coord_flip() +
labs(
title = "Top 15 Zonas con Más Propiedades",
x = "",
y = "Número de Propiedades"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
ggplotly(p6)
```
## Row
### Boxplot de Precios por Zona (Top 10)
```{r}
top_10_zonas <- datos %>%
count(loc2, sort = TRUE) %>%
head(10) %>%
pull(loc2)
datos_top <- datos %>%
filter(loc2 %in% top_10_zonas)
p7 <- ggplot(datos_top, aes(x = reorder(loc2, precio_m2, FUN = median), y = precio_m2, fill = loc2)) +
geom_boxplot(alpha = 0.7, outlier.alpha = 0.5) +
scale_y_continuous(labels = dollar_format(prefix = "USD ")) +
coord_flip() +
labs(
title = "Distribución de Precio/m² por Zona",
x = "",
y = "Precio por m² (USD)"
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold"),
legend.position = "none"
)
ggplotly(p7)
```
# Datos
## Row
### Tabla Completa de Propiedades
```{r}
datos %>%
select(
Zona = loc2,
Dirección = loc1,
`Precio (USD)` = precio,
`Superficie (m²)` = m2ok,
`Precio/m² (USD)` = precio_m2,
Link = link
) %>%
mutate(
`Precio (USD)` = format(`Precio (USD)`, big.mark = ","),
`Precio/m² (USD)` = round(`Precio/m² (USD)`, 2),
Link = paste0('<a href="', Link, '" target="_blank">Ver</a>')
) %>%
datatable(
escape = FALSE,
filter = "top",
options = list(
pageLength = 25,
scrollX = TRUE,
autoWidth = TRUE,
columnDefs = list(list(width = '200px', targets = c(0, 1)))
),
rownames = FALSE
)
```
# Estadísticas
## Row {.tabset}
### Resumen General
```{r}
resumen <- datos %>%
summarise(
`Total Propiedades` = n(),
`Precio Promedio` = mean(precio, na.rm = TRUE),
`Precio Mediano` = median(precio, na.rm = TRUE),
`Precio Mínimo` = min(precio, na.rm = TRUE),
`Precio Máximo` = max(precio, na.rm = TRUE),
`Precio/m² Promedio` = mean(precio_m2, na.rm = TRUE),
`Precio/m² Mediano` = median(precio_m2, na.rm = TRUE),
`Superficie Promedio (m²)` = mean(m2ok, na.rm = TRUE),
`Superficie Mediana (m²)` = median(m2ok, na.rm = TRUE)
) %>%
pivot_longer(everything(), names_to = "Estadística", values_to = "Valor") %>%
mutate(Valor = ifelse(grepl("Precio", Estadística),
paste0("USD ", format(round(Valor, 2), big.mark = ",")),
format(round(Valor, 2), big.mark = ",")))
knitr::kable(resumen, align = c("l", "r"))
```
### Por Rango de Precio
```{r}
resumen_rango <- datos %>%
group_by(rango_precio) %>%
summarise(
`Cantidad` = n(),
`% del Total` = round(n() / nrow(datos) * 100, 1),
`Superficie Promedio (m²)` = round(mean(m2ok, na.rm = TRUE), 0),
`Precio/m² Promedio (USD)` = round(mean(precio_m2, na.rm = TRUE), 2)
) %>%
arrange(rango_precio)
knitr::kable(resumen_rango, align = c("l", "r", "r", "r", "r"))
```
### Por Rango de Superficie
```{r}
resumen_m2 <- datos %>%
group_by(rango_m2) %>%
summarise(
`Cantidad` = n(),
`% del Total` = round(n() / nrow(datos) * 100, 1),
`Precio Promedio (USD)` = format(round(mean(precio, na.rm = TRUE), 0), big.mark = ","),
`Precio/m² Promedio (USD)` = round(mean(precio_m2, na.rm = TRUE), 2)
) %>%
arrange(rango_m2)
knitr::kable(resumen_m2, align = c("l", "r", "r", "r", "r"))
```