R Markdown

This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.

When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

ruta <- "C:/Users/diego/Documents/Diego/Estudios/Data science/Visualización de datos/Practica/alquiler_serpavi.xlsx"
serpavi_raw <- read_excel(ruta, sheet = 3)

serpavi_sel_vc <- serpavi_raw %>%
  select(
    provincia = LITPRO,
    matches("^ALQM2_LV_M_VC_[0-9]{2}$"),
    matches("^SLVM2_M_VC_[0-9]{2}$")
  )

serpavi_wide_vc <- serpavi_sel_vc %>%
  pivot_longer(
    cols = -provincia,
    names_to = c("var", "anio2"),
    names_pattern = "^(ALQM2_LV_M_VC|SLVM2_M_VC)_([0-9]{2})$",
    values_to = "valor"
  ) %>%
  mutate(anio = 2000 + as.integer(anio2)) %>%
  select(-anio2) %>%
  pivot_wider(names_from = var, values_from = valor) %>%
  rename(
    alquilerm2 = ALQM2_LV_M_VC,
    superficie = SLVM2_M_VC
  ) %>%
  mutate(alquiler_mensual_estimado = alquilerm2 * superficie) %>%
  select(provincia, anio, alquilerm2, superficie, alquiler_mensual_estimado) %>%
  pivot_wider(
    names_from = anio,
    values_from = c(alquilerm2, superficie, alquiler_mensual_estimado),
    names_glue = "{.value}_{anio}"
  ) %>%
  arrange(provincia)


serpavi_final <- serpavi_wide_vc %>%
  select(
    provincia,
    matches("_(2015|2016|2017|2018|2019|2020|2021|2022|2023)$")
  )

Including Plots

You can also embed plots, for example:

ruta_ine <- "C:/Users/diego/Documents/Diego/Estudios/Data science/Visualización de datos/Practica/ine_renta.csv"

ine_raw <- read_delim(
  file = ruta_ine,
  delim = ";",
  locale = locale(encoding = "UTF-8"),
  show_col_types = FALSE,
  trim_ws = TRUE
)
 
ine_hogar <- ine_raw %>%
  filter(
    !is.na(Provincias),
    `Indicadores de renta media` == "Renta neta media por hogar"
  ) %>%
  mutate(
    anio = as.integer(Periodo),
    renta_hogar = parse_number(
      as.character(Total),
      locale = locale(decimal_mark = ",", grouping_mark = ".")
    )
  ) %>%
  select(
    provincia = Provincias,
    anio,
    renta_hogar
  )
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `renta_hogar = parse_number(...)`.
## Caused by warning:
## ! 1 parsing failure.
## row col expected actual
## 531  -- a number     ..
ine_final <- ine_hogar %>%
  group_by(provincia, anio) %>%
  summarise(renta_hogar = mean(renta_hogar, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(
    names_from = anio,
    values_from = renta_hogar,
    names_prefix = "renta_hogar_"
  ) %>%
  arrange(provincia)
library(dplyr)

years <- 2015:2023

# Unimos manteniendo TODO lo que ya tienes en servavi_final
#    y añadiendo las rentas del ine_final
dataset_final <- serpavi_final %>%
  left_join(ine_final, by = "provincia")

# Asegurar que exista alquiler_mensual_estimado_YYYY
#    (por si en algún punto no se generó o falta algún año)
for (y in years) {
  col_m2 <- paste0("alquilerm2_", y)
  col_su <- paste0("superficie_", y)
  col_am <- paste0("alquiler_mensual_estimado_", y)

  if (!(col_am %in% names(dataset_final)) &&
      all(c(col_m2, col_su) %in% names(dataset_final))) {
    dataset_final[[col_am]] <- dataset_final[[col_m2]] * dataset_final[[col_su]]
  }
}

# Crear renta mensual por año (renta_hogar_YYYY / 12)
for (y in years) {
  col_ra <- paste0("renta_hogar_", y)
  col_rm <- paste0("renta_mensual_", y)

  if (col_ra %in% names(dataset_final)) {
    dataset_final[[col_rm]] <- dataset_final[[col_ra]] / 12
  }
}

# Calcular esfuerzo (%) por año
#    esfuerzo_YYYY = alquiler_mensual_estimado_YYYY / renta_mensual_YYYY * 100
for (y in years) {
  col_am <- paste0("alquiler_mensual_estimado_", y)
  col_rm <- paste0("renta_mensual_", y)
  col_es <- paste0("esfuerzo_", y)

  if (all(c(col_am, col_rm) %in% names(dataset_final))) {
    dataset_final[[col_es]] <- (dataset_final[[col_am]] / dataset_final[[col_rm]]) * 100
  }
}

# Resultado
names(dataset_final)
##  [1] "provincia"                      "alquilerm2_2015"               
##  [3] "alquilerm2_2016"                "alquilerm2_2017"               
##  [5] "alquilerm2_2018"                "alquilerm2_2019"               
##  [7] "alquilerm2_2020"                "alquilerm2_2021"               
##  [9] "alquilerm2_2022"                "alquilerm2_2023"               
## [11] "superficie_2015"                "superficie_2016"               
## [13] "superficie_2017"                "superficie_2018"               
## [15] "superficie_2019"                "superficie_2020"               
## [17] "superficie_2021"                "superficie_2022"               
## [19] "superficie_2023"                "alquiler_mensual_estimado_2015"
## [21] "alquiler_mensual_estimado_2016" "alquiler_mensual_estimado_2017"
## [23] "alquiler_mensual_estimado_2018" "alquiler_mensual_estimado_2019"
## [25] "alquiler_mensual_estimado_2020" "alquiler_mensual_estimado_2021"
## [27] "alquiler_mensual_estimado_2022" "alquiler_mensual_estimado_2023"
## [29] "renta_hogar_2015"               "renta_hogar_2016"              
## [31] "renta_hogar_2017"               "renta_hogar_2018"              
## [33] "renta_hogar_2019"               "renta_hogar_2020"              
## [35] "renta_hogar_2021"               "renta_hogar_2022"              
## [37] "renta_hogar_2023"               "renta_mensual_2015"            
## [39] "renta_mensual_2016"             "renta_mensual_2017"            
## [41] "renta_mensual_2018"             "renta_mensual_2019"            
## [43] "renta_mensual_2020"             "renta_mensual_2021"            
## [45] "renta_mensual_2022"             "renta_mensual_2023"            
## [47] "esfuerzo_2015"                  "esfuerzo_2016"                 
## [49] "esfuerzo_2017"                  "esfuerzo_2018"                 
## [51] "esfuerzo_2019"                  "esfuerzo_2020"                 
## [53] "esfuerzo_2021"                  "esfuerzo_2022"                 
## [55] "esfuerzo_2023"
# ¿Dónde supone mayor esfuerzo el alquiler?
# Top provincias por esfuerzo en 2023 + evolución

# Tabla resumen
dataset_final %>%
  select(provincia, esfuerzo_2015, esfuerzo_2023) %>%
  mutate(
    variacion = esfuerzo_2023 - esfuerzo_2015,
    tendencia = if_else(variacion > 0, "↑ Empeora", "↓ Mejora")
  ) %>%
  arrange(desc(esfuerzo_2023)) %>%
  print(n = Inf)
## # A tibble: 52 × 5
##    provincia              esfuerzo_2015 esfuerzo_2023 variacion tendencia
##    <chr>                          <dbl>         <dbl>     <dbl> <chr>    
##  1 Málaga                          24.5          24.8    0.216  ↑ Empeora
##  2 Sevilla                         22.1          21.5   -0.668  ↓ Mejora 
##  3 Barcelona                       19.8          20.6    0.839  ↑ Empeora
##  4 Balears, Illes                  19.9          20.2    0.297  ↑ Empeora
##  5 Madrid                          19.2          19.9    0.663  ↑ Empeora
##  6 Cuenca                          21.2          19.6   -1.56   ↓ Mejora 
##  7 Cádiz                           20.5          19.6   -0.928  ↓ Mejora 
##  8 Córdoba                         21.0          19.6   -1.46   ↓ Mejora 
##  9 Granada                         19.2          19.1   -0.105  ↓ Mejora 
## 10 Santa Cruz de Tenerife          19.5          19.0   -0.517  ↓ Mejora 
## 11 Gipuzkoa                        NA            18.9   NA      <NA>     
## 12 Huelva                          19.7          18.6   -1.08   ↓ Mejora 
## 13 Badajoz                         19.3          18.3   -0.994  ↓ Mejora 
## 14 Toledo                          18.0          18.0    0.0666 ↑ Empeora
## 15 Guadalajara                     17.3          18.0    0.627  ↑ Empeora
## 16 Ceuta                           19.5          17.9   -1.53   ↓ Mejora 
## 17 Albacete                        19.0          17.9   -1.02   ↓ Mejora 
## 18 Salamanca                       18.9          17.8   -1.06   ↓ Mejora 
## 19 Almería                         18.8          17.8   -0.984  ↓ Mejora 
## 20 Palmas, Las                     18.6          17.7   -0.891  ↓ Mejora 
## 21 Pontevedra                      18.3          17.6   -0.696  ↓ Mejora 
## 22 Cáceres                         18.7          17.5   -1.25   ↓ Mejora 
## 23 Cantabria                       17.6          17.2   -0.459  ↓ Mejora 
## 24 Jaén                            18.7          17.1   -1.60   ↓ Mejora 
## 25 Ávila                           17.1          16.9   -0.206  ↓ Mejora 
## 26 Valladolid                      16.9          16.9   -0.0525 ↓ Mejora 
## 27 Murcia                          16.8          16.6   -0.182  ↓ Mejora 
## 28 Girona                          17.1          16.6   -0.533  ↓ Mejora 
## 29 Asturias                        16.8          16.5   -0.362  ↓ Mejora 
## 30 Segovia                         17.8          16.4   -1.38   ↓ Mejora 
## 31 Burgos                          16.7          16.4   -0.335  ↓ Mejora 
## 32 Tarragona                       16.5          16.4   -0.145  ↓ Mejora 
## 33 Soria                           16.4          16.4   -0.0598 ↓ Mejora 
## 34 Rioja, La                       16.6          16.4   -0.230  ↓ Mejora 
## 35 Zaragoza                        16.1          16.3    0.199  ↑ Empeora
## 36 Navarra                         NA            16.3   NA      <NA>     
## 37 Zamora                          17.5          16.1   -1.41   ↓ Mejora 
## 38 León                            16.3          16.0   -0.344  ↓ Mejora 
## 39 Melilla                         17.5          16.0   -1.54   ↓ Mejora 
## 40 Palencia                        16.3          15.7   -0.617  ↓ Mejora 
## 41 Ciudad Real                     16.8          15.6   -1.22   ↓ Mejora 
## 42 Ourense                         16.9          15.5   -1.39   ↓ Mejora 
## 43 Coruña, A                       16.3          15.4   -0.898  ↓ Mejora 
## 44 Castellón/Castelló              14.4          15.1    0.664  ↑ Empeora
## 45 Lleida                          15.2          14.7   -0.539  ↓ Mejora 
## 46 Huesca                          14.5          14.6    0.110  ↑ Empeora
## 47 Teruel                          14.4          14.6    0.209  ↑ Empeora
## 48 Lugo                            14.9          14.2   -0.640  ↓ Mejora 
## 49 Alicante                        NA            NA     NA      <NA>     
## 50 Araba/Álava                     NA            NA     NA      <NA>     
## 51 Bizkaia                         NA            NA     NA      <NA>     
## 52 Valencia/Valéncia               NA            NA     NA      <NA>
# Gráfico: ranking 2023 coloreado por variación
dataset_final %>%
  select(provincia, esfuerzo_2015, esfuerzo_2023) %>%
  drop_na(esfuerzo_2023) %>%
  mutate(
    variacion = esfuerzo_2023 - esfuerzo_2015,
    provincia = fct_reorder(provincia, esfuerzo_2023)
  ) %>%
  ggplot(aes(x = esfuerzo_2023, y = provincia, fill = variacion)) +
  geom_col() +
  geom_vline(xintercept = 30, linetype = "dashed", color = "red", linewidth = 0.8) +
  annotate("text", x = 31, y = 1, label = "Umbral 30%", color = "red", hjust = 0, size = 3.5) +
  scale_fill_gradient2(low = "#2ECC71", mid = "#F7DC6F", high = "#E74C3C", midpoint = 0) +
  scale_x_continuous(labels = scales::percent_format(scale = 1)) +
  labs(title = "Esfuerzo económico para el alquiler por provincia (2023)",
       subtitle = "Color = variación respecto a 2015 | Línea roja = umbral crítico 30%",
       x = "% renta destinado al alquiler", y = NULL,
       fill = "Variación\n2015–2023 (pp)") +
  theme_minimal(base_size = 13)

# ¿Relación renta vs precio de alquiler? ¿Hay desajustes?
# Correlación por año
años <- 2015:2023
correlaciones <- map_dfr(años, function(a) {
  tibble(
    año = a,
    correlacion = cor(
      dataset_final[[paste0("renta_mensual_", a)]],
      dataset_final[[paste0("alquilerm2_", a)]],
      use = "complete.obs"
    )
  )
})

print(correlaciones)
## # A tibble: 9 × 2
##     año correlacion
##   <int>       <dbl>
## 1  2015       0.791
## 2  2016       0.783
## 3  2017       0.787
## 4  2018       0.795
## 5  2019       0.793
## 6  2020       0.767
## 7  2021       0.765
## 8  2022       0.811
## 9  2023       0.833
# Scatter renta vs alquiler con línea de tendencia y outliers etiquetados
dataset_final %>%
  select(provincia, renta_mensual_2023, alquilerm2_2023, esfuerzo_2023) %>%
  drop_na() %>%
  mutate(
    desajuste  = as.vector(scale(alquilerm2_2023)) - as.vector(scale(renta_mensual_2023)),
    es_outlier = abs(desajuste) > 1
  ) %>%
  ggplot(aes(x = renta_mensual_2023, y = alquilerm2_2023)) +
  geom_point(aes(color = esfuerzo_2023, size = esfuerzo_2023), alpha = 0.8) +
  geom_smooth(method = "lm", se = TRUE, color = "gray40", linetype = "dashed") +
  ggrepel::geom_text_repel(
    aes(label = if_else(es_outlier, provincia, NA_character_)),
    size = 3.2, max.overlaps = 20
  ) +
  scale_color_gradient(low = "#2ECC71", high = "#E74C3C") +
  scale_x_continuous(labels = scales::dollar_format(suffix = "€", prefix = "")) +
  scale_y_continuous(labels = scales::dollar_format(suffix = "€/m²", prefix = "")) +
  labs(title = "Relación entre renta mensual y precio de alquiler (2023)",
       subtitle = "Provincias etiquetadas = desajuste significativo entre renta y alquiler",
       x = "Renta mensual del hogar (€)", y = "Precio alquiler €/m²",
       color = "Esfuerzo (%)", size = "Esfuerzo (%)") +
  theme_minimal(base_size = 13)
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 45 rows containing missing values or values outside the scale range
## (`geom_text_repel()`).

#  ¿Qué diferencias territoriales existen?

# Heatmap completo con clustering por similitud
library(tidyverse)

mat_esfuerzo <- dataset_final %>%
  select(provincia, starts_with("esfuerzo_")) %>%
  column_to_rownames("provincia")

# Ordenar provincias por esfuerzo medio (agrupa similares visualmente)
orden <- mat_esfuerzo %>%
  rowMeans(na.rm = TRUE) %>%
  sort(decreasing = TRUE) %>%
  names()

dataset_final %>%
  pivot_longer(starts_with("esfuerzo_"), names_to = "año", 
               values_to = "esfuerzo", names_prefix = "esfuerzo_") %>%
  mutate(
    año = as.integer(año),
    provincia = factor(provincia, levels = orden)
  ) %>%
  ggplot(aes(x = año, y = provincia, fill = esfuerzo)) +
  geom_tile(color = "white", linewidth = 0.4) +
  geom_text(aes(label = round(esfuerzo, 1)), size = 2.5, color = "black") +
  scale_fill_gradient2(low = "#2ECC71", mid = "#F7DC6F", high = "#E74C3C",
                       midpoint = 30) +
  scale_x_continuous(breaks = 2015:2023) +
  labs(title = "Esfuerzo de alquiler por provincia y año (%)",
       subtitle = "Ordenado por esfuerzo medio 2015–2023",
       x = NULL, y = NULL, fill = "Esfuerzo (%)") +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
## Warning: Removed 49 rows containing missing values or values outside the scale range
## (`geom_text()`).

# Boxplot de dispersión entre provincias por año
dataset_final %>%
  pivot_longer(starts_with("esfuerzo_"), names_to = "año",
               values_to = "esfuerzo", names_prefix = "esfuerzo_") %>%
  mutate(año = as.integer(año)) %>%
  ggplot(aes(x = factor(año), y = esfuerzo)) +
  geom_boxplot(fill = "#3498DB", alpha = 0.5, outlier.shape = NA) +
  geom_jitter(aes(color = provincia), width = 0.15, size = 2, alpha = 0.7) +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  labs(title = "Dispersión territorial del esfuerzo por año",
       subtitle = "Cada punto es una provincia — la caja muestra la distribución",
       x = NULL, y = "Esfuerzo (%)", color = "Provincia") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "right")
## Warning: Removed 49 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
## Warning: Removed 49 rows containing missing values or values outside the scale range
## (`geom_point()`).

# ¿Cómo ha evolucionado la accesibilidad?

# Evolución del esfuerzo medio nacional + rango entre provincias
dataset_final %>%
  pivot_longer(starts_with("esfuerzo_"), names_to = "año",
               values_to = "esfuerzo", names_prefix = "esfuerzo_") %>%
  mutate(año = as.integer(año)) %>%
  group_by(año) %>%
  summarise(
    media   = mean(esfuerzo, na.rm = TRUE),
    minimo  = min(esfuerzo,  na.rm = TRUE),
    maximo  = max(esfuerzo,  na.rm = TRUE),
    p25     = quantile(esfuerzo, 0.25, na.rm = TRUE),
    p75     = quantile(esfuerzo, 0.75, na.rm = TRUE)
  ) %>%
  ggplot(aes(x = año)) +
  geom_ribbon(aes(ymin = minimo, ymax = maximo), fill = "#E74C3C", alpha = 0.15) +
  geom_ribbon(aes(ymin = p25,    ymax = p75),    fill = "#E74C3C", alpha = 0.30) +
  geom_line(aes(y = media), color = "#E74C3C", linewidth = 1.4) +
  geom_point(aes(y = media), color = "#E74C3C", size = 3) +
  geom_hline(yintercept = 30, linetype = "dashed", color = "gray40") +
  annotate("text", x = 2015, y = 31, label = "Umbral crítico 30%", 
           hjust = 0, size = 3.5, color = "gray40") +
  scale_x_continuous(breaks = 2015:2023) +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  labs(title = "Evolución de la accesibilidad al alquiler (2015–2023)",
       subtitle = "Línea = media nacional | Banda oscura = P25–P75 | Banda clara = rango total",
       x = NULL, y = "Esfuerzo (%)") +
  theme_minimal(base_size = 13)

# Índice encadenado base 2015=100 para comparar alquiler vs renta
dataset_final %>%
  summarise(across(starts_with("alquilerm2_") | starts_with("renta_mensual_"), 
                   ~ mean(.x, na.rm = TRUE))) %>%
  pivot_longer(everything(), names_to = "variable", values_to = "valor") %>%
  mutate(
    año  = as.integer(str_extract(variable, "\\d{4}")),
    tipo = case_when(
      str_detect(variable, "alquilerm2")    ~ "Precio alquiler €/m²",
      str_detect(variable, "renta_mensual") ~ "Renta mensual"
    )
  ) %>%
  group_by(tipo) %>%
  mutate(indice = valor / first(valor) * 100) %>%
  ggplot(aes(x = año, y = indice, color = tipo, group = tipo)) +
  geom_line(linewidth = 1.4) +
  geom_point(size = 3) +
  geom_hline(yintercept = 100, linetype = "dashed", color = "gray60") +
  scale_x_continuous(breaks = 2015:2023) +
  scale_color_manual(values = c("#E74C3C", "#3498DB")) +
  labs(title = "Alquiler vs Renta — Índice base 2015 = 100",
       subtitle = "Si el alquiler crece más rápido que la renta, la accesibilidad empeora",
       x = NULL, y = "Índice (2015 = 100)", color = NULL) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "top")