library(dplyr)
#>
#> Adjuntando el paquete: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(readxl)
#> Warning: package 'readxl' was built under R version 4.4.3
library(leaflet)
#> Warning: package 'leaflet' was built under R version 4.4.3
library(shiny)
#> Warning: package 'shiny' was built under R version 4.4.3
library(dplyr)
library(readxl)
library(jsonlite)
#>
#> Adjuntando el paquete: 'jsonlite'
#> The following object is masked from 'package:shiny':
#>
#> validate
library(stringr)
library(knitr)
library(FactoMineR)
#> Warning: package 'FactoMineR' was built under R version 4.4.3
library(factoextra)
#> Warning: package 'factoextra' was built under R version 4.4.3
#> Cargando paquete requerido: ggplot2
#> Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(mice)
#>
#> Adjuntando el paquete: 'mice'
#> The following object is masked from 'package:stats':
#>
#> filter
#> The following objects are masked from 'package:base':
#>
#> cbind, rbind
Cargamos la base de datos ya limpia de 2025, preparados para abordar nuestro objetivo:
datos <- readRDS("datos_limpios.Rds")
# Desactivar notación científica
options(scipen = 999)
# Histograma
hist(datos$price,
breaks = 30,
col = "skyblue",
border = "white",
main = "Histograma de precios de viviendas en Valencia 2025",
xlab = "Precio (€)",
ylab = "Frecuencia")
# Resumen sin notación científica
summary(datos$price)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 26800 217000 330000 445873 550000 4700000
datos$RANGO_PRECIO <- cut(
datos$price,
breaks = c(-Inf, 250000, 500000, Inf),
labels = c("Barato", "Medio", "Caro"),
right = TRUE
)
table(datos$RANGO_PRECIO)
#>
#> Barato Medio Caro
#> 1061 1199 879
Barato ≤ 250.000 €
Este rango abarca todos los precios hasta ligeramente por encima del 1er cuartil (217.000 €).
Como el 25% de las viviendas valen ≤ 217k, y un poco más del 25% están entre 217k y 250k, este grupo representa el segmento más asequible y frecuente.
Además, muchos valores están muy concentrados ahí, como se ve en el histograma.
Medio 250.001 – 550.000 €
Este rango contiene la mediana (330.000 €), por lo que representa el valor central del mercado.
El 2º y 3º cuartil (Q2 y Q3) están dentro de este rango, abarcando el 50% central de las viviendas.
Justo 550.000 € es el tercer cuartil, y marca una frontera natural con el segmento más alto del mercado.
Caro > 550.000 €
Todo lo que supera el Q3 (550k) se considera caro, y está respaldado por la asimetría del histograma:
Aunque hay viviendas muy caras (hasta 4,7 millones), son poco frecuentes.
Esta “cola larga” de precios altos distorsiona la media (445k), que es más alta que la mediana.
Justificación con el histograma La distribución es claramente sesgada a la derecha (asimétrica positiva).
La mayoría de las viviendas se concentran entre 100k y 400k, siendo 250k un punto de corte muy representativo para lo “barato”.
A partir de 550k, la frecuencia cae drásticamente, lo que respalda usar ese valor como umbral del segmento “caro”.
Barato: incluye el mercado más accesible (≈30–35% de los casos).
Medio: representa el rango donde está la mayoría (≈50%).
Caro: agrupa las viviendas de gama alta, menos frecuentes pero relevantes.
library(dplyr)
conteo_distrito2025 <- datos %>%
group_by(district) %>%
summarise(
Barato = sum(RANGO_PRECIO == "Barato", na.rm = TRUE),
Medio = sum(RANGO_PRECIO == "Medio", na.rm = TRUE),
Caro = sum(RANGO_PRECIO == "Caro", na.rm = TRUE)
) %>%
arrange(district)
print(conteo_distrito2025)
#> # A tibble: 18 × 4
#> district Barato Medio Caro
#> <chr> <int> <int> <int>
#> 1 Alboraya Centro 0 2 0
#> 2 Algirós 21 48 10
#> 3 Benicalap 58 41 11
#> 4 Benimaclet 8 19 8
#> 5 Camins al Grau 88 90 68
#> 6 Campanar 34 54 62
#> 7 Ciutat Vella 40 114 201
#> 8 El Pla del Real 7 21 55
#> 9 Extramurs 32 108 86
#> 10 Jesús 87 69 4
#> 11 L'Eixample 5 123 216
#> 12 L'Olivereta 119 31 5
#> 13 La Patacona 0 40 25
#> 14 La Saïdia 67 40 10
#> 15 Patraix 79 69 8
#> 16 Poblats Marítims 211 123 63
#> 17 Quatre Carreres 66 174 46
#> 18 Rascanya 139 33 1
Vamos a observar si los distritos que en 2018 tenían más viviendas baratas, medias o caras han mantenido esa tendencia en 2025 o si se ha producido un cambio en la composición relativa.
-> Distritos que siguen siendo baratos en 2025 Distritos con mayoría de pisos baratos tanto en 2018 como en 2025:
Poblats Marítims: sigue destacando con mayoría de pisos baratos (2018: 1068 | 2025: 211). Aunque ha crecido el porcentaje de caros, sigue siendo un distrito de perfil económico.
Benicalap y Rascanya: se mantienen como zonas con mayoría de pisos baratos, aunque con una leve subida proporcional de pisos medios.
Estos barrios mantienen su carácter más popular y asequible, aunque hay indicios de ligera transición.
-> Distritos que han ganado peso en rango medio Quatre Carreres: pasa de una mezcla bastante equilibrada a una clara predominancia del rango medio (174 frente a 66 baratos y 46 caros).
Camins al Grau: mantiene una distribución muy equilibrada en 2025, lo que sugiere estabilidad de perfil mixto.
Algunos barrios se consolidan como zonas de clase media, incluso más que en 2018.
-> Distritos que refuerzan su perfil caro L’Eixample: en 2018 ya tenía muchísimos pisos caros (1011), y en 2025 sigue siendo el distrito con más proporción de pisos caros (216 frente a solo 5 baratos).
Ciutat Vella: aumenta su polarización — en 2025 hay más pisos caros (201) que en cualquier otra categoría.
El Pla del Real: continúa con más caros que medios o baratos.
Estas zonas refuerzan su identidad como zonas de alto nivel económico, desplazando aún más las opciones asequibles.
-> Distritos que cambian de perfil Jesús: en 2018 tenía mayoría de pisos baratos (466) y pocos caros (5). En 2025 esa proporción se invierte: aunque siguen predominando los baratos, el porcentaje de medios ha crecido mucho.
Extramurs: pasa de ser muy medio (1695) a una distribución más equilibrada en 2025 entre medio y caro.
Hay indicios de gentrificación o transición socioeconómica en algunos distritos tradicionalmente asequibles.
Conclusión general de la comparación:
Tendencia Distritos afectados Mantienen perfil barato Poblats Marítims, Benicalap, Rascanya Mantienen perfil caro L’Eixample, Ciutat Vella, Pla del Real Transición hacia el rango medio Quatre Carreres, Camins al Grau Gentrificación visible Jesús, Extramurs, Benimaclet (ligero aumento de caros)
library(dplyr)
library(tidyr)
library(sf) # por si aún no estaba cargado
#> Warning: package 'sf' was built under R version 4.4.3
#> Linking to GEOS 3.13.0, GDAL 3.10.1, PROJ 9.5.1; sf_use_s2() is TRUE
datos <- datos %>%
filter(!district %in% c("Alboraya Centro", "La Patacona"))
# eliminamos estos distritos porque no nos interesan, además de no contar con ellos en la base de datos de 2018
datos$RHabitacion_Banyo <- ifelse(datos$rooms == 0, 0, datos$bathrooms / datos$rooms)
modelo_vars <- c(
"size","rooms","bathrooms",
"distance","distancia_min_estacion_m",
"priceByArea","RHabitacion_Banyo"
)
datos <- datos %>%
dplyr::mutate(
good = if_else(status == "good", 1L, 0L),
renew = if_else(status == "renew", 1L, 0L)
)
vars_bin <- c( "hasLift","newDevelopment", 'good', 'renew')
resumen <- datos %>%
st_drop_geometry() %>%
group_by(district, RANGO_PRECIO) %>%
summarise(
# Continuas: media, min, max, sd
across(
all_of(modelo_vars),
.fns = list(
media = \(x) mean(x, na.rm = TRUE),
min = \(x) min(x, na.rm = TRUE),
max = \(x) max(x, na.rm = TRUE),
sd = \(x) sd(x, na.rm = TRUE)
),
.names = "{.col}_{.fn}"
),
# Binarias: solo media (sin sd)
across(
all_of(vars_bin),
.fns = ~mean(.x, na.rm = TRUE),
.names = "{.col}_media"
),
.groups = "drop"
)
resumen_distritos <- resumen %>%
pivot_wider(
id_cols = district,
names_from = RANGO_PRECIO,
values_from = -c(district, RANGO_PRECIO),
names_glue = "{.value}_{RANGO_PRECIO}",
values_fill = NA
)
# 5) Lo convertimos a data.frame para verlo completo
resumen_distritos <- as.data.frame(resumen_distritos)
print(resumen_distritos)
#> district size_media_Barato size_media_Medio size_media_Caro
#> 1 Algirós 78.47619 109.31250 193.4000
#> 2 Benicalap 82.63793 129.21951 195.2727
#> 3 Benimaclet 70.62500 128.78947 296.1250
#> 4 Camins al Grau 77.70455 108.54444 187.6912
#> 5 Campanar 70.70588 121.18519 209.3710
#> 6 Ciutat Vella 64.50000 93.34211 213.5572
#> 7 El Pla del Real 65.42857 118.85714 221.9636
#> 8 Extramurs 70.03125 116.27778 199.0465
#> 9 Jesús 83.10345 103.28986 160.7500
#> 10 L'Eixample 64.00000 100.13008 196.0278
#> 11 L'Olivereta 70.38655 121.22581 225.8000
#> 12 La Saïdia 69.53731 112.45000 179.6000
#> 13 Patraix 86.62025 116.76812 199.3750
#> 14 Poblats Marítims 74.88626 116.11382 227.7619
#> 15 Quatre Carreres 81.40909 103.70690 183.4783
#> 16 Rascanya 74.99281 121.57576 122.0000
#> size_min_Barato size_min_Medio size_min_Caro size_max_Barato size_max_Medio
#> 1 39 70 129 110 161
#> 2 35 79 50 245 218
#> 3 35 86 202 114 150
#> 4 31 48 76 205 212
#> 5 27 60 63 129 275
#> 6 40 39 70 92 264
#> 7 38 66 121 99 170
#> 8 30 40 110 133 382
#> 9 31 72 134 156 220
#> 10 35 40 85 94 177
#> 11 32 70 181 142 240
#> 12 31 67 124 120 252
#> 13 35 45 152 170 300
#> 14 28 55 90 235 326
#> 15 44 45 76 149 436
#> 16 26 78 122 138 420
#> size_max_Caro size_sd_Barato size_sd_Medio size_sd_Caro rooms_media_Barato
#> 1 255 20.31162 21.51512 43.84873 2.714286
#> 2 360 31.89165 30.69977 106.05007 2.362069
#> 3 411 24.62831 17.90896 99.13546 2.375000
#> 4 368 27.71827 33.90143 85.35497 2.488636
#> 5 1100 23.88803 33.07286 133.94044 2.029412
#> 6 1000 18.06967 32.87811 90.02510 1.250000
#> 7 372 30.46778 27.61392 70.71774 1.571429
#> 8 734 22.64378 45.75939 79.00676 2.000000
#> 9 241 19.10134 28.46003 53.50000 2.712644
#> 10 457 29.09467 29.66755 76.30434 0.800000
#> 11 260 20.40775 34.50576 31.21218 2.445378
#> 12 239 18.61681 34.98274 42.63853 2.194030
#> 13 260 26.10791 40.31317 40.88289 1.430380
#> 14 974 24.52319 43.59279 139.09010 2.454976
#> 15 480 21.95168 37.34514 82.06576 2.409091
#> 16 122 24.56978 65.04759 NA 2.201439
#> rooms_media_Medio rooms_media_Caro rooms_min_Barato rooms_min_Medio
#> 1 3.166667 3.900000 1 0
#> 2 3.024390 4.363636 1 1
#> 3 3.684211 4.375000 1 2
#> 4 2.688889 3.838235 0 0
#> 5 3.166667 3.725806 0 0
#> 6 2.289474 3.985075 0 0
#> 7 3.190476 4.363636 0 1
#> 8 2.962963 3.883721 0 1
#> 9 3.202899 5.750000 1 2
#> 10 2.788618 3.796296 0 0
#> 11 3.161290 4.400000 1 2
#> 12 3.075000 3.500000 0 1
#> 13 2.826087 3.500000 0 0
#> 14 2.951220 3.841270 0 0
#> 15 2.718391 3.500000 0 0
#> 16 3.151515 3.000000 0 1
#> rooms_min_Caro rooms_max_Barato rooms_max_Medio rooms_max_Caro
#> 1 3 4 4 7
#> 2 2 4 5 8
#> 3 4 4 5 6
#> 4 1 5 4 6
#> 5 2 4 5 6
#> 6 1 3 9 14
#> 7 2 3 6 10
#> 8 2 4 6 10
#> 9 5 5 8 6
#> 10 0 2 5 7
#> 11 4 4 5 5
#> 12 3 4 5 4
#> 13 1 5 6 5
#> 14 0 4 9 8
#> 15 1 4 5 6
#> 16 3 5 7 3
#> rooms_sd_Barato rooms_sd_Medio rooms_sd_Caro bathrooms_media_Barato
#> 1 1.1464230 0.8832564 1.2866839 1.523810
#> 2 0.9309168 0.8799945 2.0135902 1.155172
#> 3 1.0606602 0.6710383 0.7440238 1.250000
#> 4 1.0282707 0.9438678 1.1409644 1.295455
#> 5 1.2181778 0.9059551 0.9084188 1.176471
#> 6 0.8697185 1.2173065 1.5345280 1.225000
#> 7 1.3972763 1.1233453 1.2226813 1.000000
#> 8 0.9503819 1.0671468 1.1211192 1.468750
#> 9 0.8748162 1.0924838 0.5000000 1.206897
#> 10 0.8366600 1.1682412 1.0848578 1.000000
#> 11 0.9716095 0.7787500 0.5477226 1.084034
#> 12 1.0185969 1.2065526 0.5270463 1.074627
#> 13 1.2266647 1.2942920 1.6035675 1.253165
#> 14 0.9163132 1.3601691 1.3936069 1.090047
#> 15 1.1499468 0.8708548 1.2064641 1.121212
#> 16 1.2109396 1.3019508 NA 1.179856
#> bathrooms_media_Medio bathrooms_media_Caro bathrooms_min_Barato
#> 1 1.875000 2.500000 1
#> 2 1.951220 3.454545 1
#> 3 1.947368 2.375000 1
#> 4 1.711111 2.705882 1
#> 5 2.018519 2.451613 1
#> 6 1.464912 2.736318 1
#> 7 1.761905 2.636364 1
#> 8 1.648148 2.697674 1
#> 9 1.739130 2.250000 1
#> 10 1.634146 2.583333 1
#> 11 1.838710 2.800000 1
#> 12 1.750000 2.900000 1
#> 13 1.927536 2.375000 1
#> 14 1.682927 2.349206 0
#> 15 1.678161 2.347826 0
#> 16 1.636364 2.000000 1
#> bathrooms_min_Medio bathrooms_min_Caro bathrooms_max_Barato
#> 1 1 2 4
#> 2 1 1 4
#> 3 1 1 2
#> 4 1 1 4
#> 5 1 1 2
#> 6 0 0 2
#> 7 1 2 1
#> 8 1 0 2
#> 9 1 2 3
#> 10 1 1 1
#> 11 1 2 2
#> 12 1 2 2
#> 13 1 1 2
#> 14 0 0 3
#> 15 0 1 2
#> 16 1 2 2
#> bathrooms_max_Medio bathrooms_max_Caro bathrooms_sd_Barato
#> 1 3 5 0.9283883
#> 2 3 8 0.4885253
#> 3 3 3 0.4629100
#> 4 4 6 0.5286959
#> 5 4 6 0.3869530
#> 6 4 8 0.4229021
#> 7 3 5 0.0000000
#> 8 5 4 0.5070073
#> 9 3 3 0.4350336
#> 10 4 6 0.0000000
#> 11 3 4 0.2786113
#> 12 4 4 0.2647716
#> 13 4 4 0.4376029
#> 14 4 6 0.3470220
#> 15 4 5 0.3727301
#> 16 2 2 0.3854566
#> bathrooms_sd_Medio bathrooms_sd_Caro distance_media_Barato
#> 1 0.3927535 0.9718253 2544.1605
#> 2 0.3123475 2.3817488 2981.2016
#> 3 0.5242650 0.7440238 2391.3441
#> 4 0.5245532 1.0797847 2718.8152
#> 5 0.5316667 0.7825435 2071.6781
#> 6 0.6541877 1.0747671 700.0520
#> 7 0.7003401 0.7784989 1755.3862
#> 8 0.6458323 0.7832026 1006.0417
#> 9 0.5044558 0.5000000 2405.5866
#> 10 0.5472478 0.9413004 995.9155
#> 11 0.4543695 0.8366600 2473.0117
#> 12 0.6698641 0.8755950 2016.1764
#> 13 0.5235299 1.0606602 2526.7618
#> 14 0.6182614 0.9186022 4114.1029
#> 15 0.5985387 0.8998121 2359.7101
#> 16 0.4885042 NA 2760.8129
#> distance_media_Medio distance_media_Caro distance_min_Barato
#> 1 2660.9679 2850.2678 2036.6353
#> 2 2767.1176 3037.5139 2126.9948
#> 3 2157.6786 2513.9284 2243.5589
#> 4 2547.8910 2575.8121 1712.2412
#> 5 2506.1964 2842.5951 1465.9997
#> 6 644.4532 496.5738 118.6952
#> 7 1816.6980 1464.4852 1188.6372
#> 8 972.0578 843.0665 532.3821
#> 9 1806.9096 1858.8128 1413.6127
#> 10 991.8064 850.0067 833.8770
#> 11 2008.5047 1779.8184 1322.7739
#> 12 1813.8966 1693.4330 1421.8781
#> 13 2206.6111 1788.0034 1156.9599
#> 14 3950.2356 4147.4600 3321.5476
#> 15 2314.7719 2123.1038 1275.0494
#> 16 2738.8802 2305.9364 2195.9914
#> distance_min_Medio distance_min_Caro distance_max_Barato distance_max_Medio
#> 1 1911.20803 2373.477883 3603.705 3594.210
#> 2 2351.85470 2574.101318 3733.178 3619.831
#> 3 1928.94019 1825.806963 2692.208 2474.486
#> 4 1578.33174 1691.392713 3581.400 3543.797
#> 5 1727.49651 1368.217009 2639.465 3869.129
#> 6 56.18609 1.318672 1179.676 1238.927
#> 7 1553.20801 953.993348 2102.473 2120.503
#> 8 222.46987 343.849472 1391.398 1868.096
#> 9 1129.12691 1622.759404 3773.648 3469.806
#> 10 220.66374 194.599651 1128.140 1538.294
#> 11 1192.66049 1443.877075 3395.172 3511.889
#> 12 1214.74416 1180.906208 2517.313 2548.350
#> 13 1148.48446 1316.074655 3543.063 3517.295
#> 14 3188.83198 3611.926288 4639.669 4693.588
#> 15 1204.76026 1261.844701 4585.945 4321.537
#> 16 2171.98834 2305.936401 3917.546 3241.399
#> distance_max_Caro distance_sd_Barato distance_sd_Medio distance_sd_Caro
#> 1 3093.347 479.4966 423.2437 235.5749
#> 2 3513.384 511.3138 367.1585 301.9392
#> 3 3118.937 135.8035 160.5720 433.8164
#> 4 3363.566 437.0007 553.9354 447.9655
#> 5 3768.272 266.8769 626.8768 649.6846
#> 6 1463.540 277.6577 304.8761 288.1755
#> 7 1955.244 318.0947 156.6653 275.8767
#> 8 1593.481 183.5152 337.7047 260.1143
#> 9 1998.006 594.8939 495.6640 164.6601
#> 10 1562.294 111.4170 297.3672 318.5956
#> 11 2230.385 500.7128 499.2390 325.1689
#> 12 2358.105 255.0015 361.8421 390.8044
#> 13 2359.167 747.3338 628.5580 423.8222
#> 14 4712.169 312.7185 328.9818 299.5912
#> 15 3942.088 862.5783 803.8500 704.2852
#> 16 2305.936 255.0294 313.3577 NA
#> distancia_min_estacion_m_media_Barato distancia_min_estacion_m_media_Medio
#> 1 338.3841 403.4459
#> 2 281.0649 315.6254
#> 3 228.2175 183.1931
#> 4 461.6160 473.7479
#> 5 411.5504 439.5215
#> 6 524.5174 503.0614
#> 7 389.9052 328.7241
#> 8 398.6183 352.1674
#> 9 677.4085 428.5356
#> 10 321.7878 332.6015
#> 11 476.2079 420.0378
#> 12 244.1294 277.2784
#> 13 704.8418 553.5154
#> 14 416.9629 304.3411
#> 15 636.9383 510.6316
#> 16 340.3013 274.7310
#> distancia_min_estacion_m_media_Caro distancia_min_estacion_m_min_Barato
#> 1 482.3957 77.14152
#> 2 260.2889 37.53246
#> 3 334.8016 124.51295
#> 4 725.6841 61.51311
#> 5 486.9350 59.29380
#> 6 372.8737 135.18466
#> 7 326.9718 158.20845
#> 8 300.9225 42.28041
#> 9 405.7809 27.15554
#> 10 317.8260 149.87090
#> 11 412.9131 101.44369
#> 12 269.4977 18.59988
#> 13 504.0990 72.00976
#> 14 377.7580 10.72766
#> 15 461.3959 85.97060
#> 16 112.7875 16.96371
#> distancia_min_estacion_m_min_Medio distancia_min_estacion_m_min_Caro
#> 1 72.798298 364.58590
#> 2 63.838484 48.46686
#> 3 35.066037 77.67252
#> 4 17.003706 222.10834
#> 5 83.965296 72.73660
#> 6 89.052071 27.71139
#> 7 82.315758 86.34362
#> 8 27.675186 50.51145
#> 9 37.679265 247.27100
#> 10 38.134789 6.49231
#> 11 98.295263 255.47277
#> 12 9.435182 34.86535
#> 13 108.539914 308.92736
#> 14 20.947033 40.02029
#> 15 13.567666 144.47407
#> 16 49.221072 112.78753
#> distancia_min_estacion_m_max_Barato distancia_min_estacion_m_max_Medio
#> 1 547.6123 595.2734
#> 2 788.6292 882.6608
#> 3 363.6414 388.7074
#> 4 935.3287 921.8318
#> 5 770.9103 880.8282
#> 6 787.8493 810.2360
#> 7 502.9758 752.8033
#> 8 592.7249 706.6833
#> 9 1618.4799 1566.8222
#> 10 552.1002 695.0028
#> 11 913.2086 885.8946
#> 12 541.4699 532.7734
#> 13 1264.8566 1321.5851
#> 14 1111.8753 1124.3708
#> 15 1401.3865 1125.2577
#> 16 789.5994 805.5647
#> distancia_min_estacion_m_max_Caro distancia_min_estacion_m_sd_Barato
#> 1 613.4337 154.21442
#> 2 591.1753 187.59992
#> 3 828.9519 75.09697
#> 4 954.7471 209.44339
#> 5 1069.8089 163.89725
#> 6 784.0632 190.73453
#> 7 794.2917 126.68390
#> 8 621.6309 130.85713
#> 9 575.3782 473.76039
#> 10 755.3088 148.65955
#> 11 706.1296 181.79401
#> 12 493.9249 113.62287
#> 13 718.2658 346.84787
#> 14 1064.5718 233.12878
#> 15 835.4435 256.35886
#> 16 112.7875 185.85910
#> distancia_min_estacion_m_sd_Medio distancia_min_estacion_m_sd_Caro
#> 1 131.01811 82.03086
#> 2 217.58294 203.70357
#> 3 85.95883 243.93101
#> 4 248.18622 166.16010
#> 5 217.27351 217.40513
#> 6 179.81362 197.56879
#> 7 177.99864 130.50181
#> 8 177.46316 146.20674
#> 9 298.70486 159.99427
#> 10 143.76291 158.09340
#> 11 200.39651 181.57790
#> 12 144.75493 161.07187
#> 13 313.55087 158.17169
#> 14 208.16786 268.46225
#> 15 242.99582 143.40040
#> 16 188.07613 NA
#> priceByArea_media_Barato priceByArea_media_Medio priceByArea_media_Caro
#> 1 2650.176 3277.900 3765.164
#> 2 2090.655 2842.780 5207.273
#> 3 2797.750 2928.789 2503.125
#> 4 2691.953 3481.602 5038.991
#> 5 3075.735 3007.148 4193.581
#> 6 3680.475 4306.000 4907.015
#> 7 4151.599 3541.259 4455.739
#> 8 3387.656 3605.481 3632.140
#> 9 2167.391 3411.710 3648.500
#> 10 3968.800 4070.756 5099.769
#> 11 2105.840 2838.387 2662.600
#> 12 2796.388 3140.225 3984.100
#> 13 2373.772 3110.797 3670.875
#> 14 2578.545 3209.001 3760.026
#> 15 2465.556 3532.122 4645.239
#> 16 2141.662 3053.303 4836.000
#> priceByArea_min_Barato priceByArea_min_Medio priceByArea_min_Caro
#> 1 1252.0000 2154.0000 3024.000
#> 2 606.0000 1693.0000 2639.000
#> 3 1837.0000 1986.0000 2190.000
#> 4 764.0000 2000.0000 2545.000
#> 5 743.0000 1818.0000 1636.000
#> 6 2011.0000 1875.0000 1840.000
#> 7 2419.0000 2662.0000 2285.000
#> 8 1451.0000 1212.0000 1635.000
#> 9 650.0000 1978.0000 2282.000
#> 10 2415.0000 2373.0000 2969.000
#> 11 452.0000 1776.0000 1942.000
#> 12 822.0000 1964.0000 3138.000
#> 13 1064.0000 1300.0000 2983.000
#> 14 454.5455 966.2577 1148.649
#> 15 511.0000 1147.0000 1833.000
#> 16 668.0000 1012.0000 4836.000
#> priceByArea_max_Barato priceByArea_max_Medio priceByArea_max_Caro
#> 1 5769 5000.000 5256.000
#> 2 4528 5125.000 11400.000
#> 3 3676 3477.000 2833.000
#> 4 6129 7292.000 10744.000
#> 5 8148 8333.000 10317.000
#> 6 6073 8333.000 14143.000
#> 7 6053 5076.000 9016.000
#> 8 5385 8000.000 6893.000
#> 9 6290 5000.000 4104.000
#> 10 5686 8140.000 13571.000
#> 11 4333 4859.000 4144.000
#> 12 6935 4478.000 5242.000
#> 13 4659 9111.000 4745.000
#> 14 8214 6666.667 8235.294
#> 15 4980 6481.000 8000.000
#> 16 6250 5449.000 4836.000
#> priceByArea_sd_Barato priceByArea_sd_Medio priceByArea_sd_Caro
#> 1 1136.5125 518.3348 793.9445
#> 2 821.1729 766.8771 2649.0044
#> 3 604.2946 384.8721 277.1701
#> 4 1014.0836 954.3202 1607.2319
#> 5 1846.0338 949.2750 1605.9555
#> 6 1131.1161 1198.1231 1898.3365
#> 7 1661.4509 715.8423 1548.9623
#> 8 888.0966 1111.6855 930.2172
#> 9 870.5196 833.2664 911.0000
#> 10 1562.3350 1130.0953 1666.7677
#> 11 812.3166 719.1609 872.5009
#> 12 1088.6322 697.0984 650.6140
#> 13 772.6189 1062.3385 648.6851
#> 14 1073.7446 953.5587 1438.7568
#> 15 1087.3112 1005.8152 1296.8595
#> 16 928.9510 913.4221 NA
#> RHabitacion_Banyo_media_Barato RHabitacion_Banyo_media_Medio
#> 1 0.6507937 0.6093750
#> 2 0.5804598 0.6995935
#> 3 0.6041667 0.5473684
#> 4 0.5886364 0.6750000
#> 5 0.4803922 0.6447531
#> 6 0.7583333 0.6637914
#> 7 0.4285714 0.5841270
#> 8 0.7421875 0.6108025
#> 9 0.5176245 0.5630780
#> 10 0.5000000 0.6547425
#> 11 0.5364146 0.6102151
#> 12 0.4838308 0.6591667
#> 13 0.6759494 0.7519324
#> 14 0.4589258 0.6508324
#> 15 0.5429293 0.6528736
#> 16 0.5334532 0.6005772
#> RHabitacion_Banyo_media_Caro RHabitacion_Banyo_min_Barato
#> 1 0.6595238 0.2500000
#> 2 0.7469697 0.2500000
#> 3 0.5541667 0.3333333
#> 4 0.7240196 0.0000000
#> 5 0.6803763 0.0000000
#> 6 0.7281331 0.0000000
#> 7 0.6341558 0.0000000
#> 8 0.7544574 0.0000000
#> 9 0.3916667 0.2000000
#> 10 0.7061067 0.0000000
#> 11 0.6400000 0.2500000
#> 12 0.8583333 0.0000000
#> 13 0.7500000 0.0000000
#> 14 0.6404762 0.0000000
#> 15 0.7333333 0.0000000
#> 16 0.6666667 0.0000000
#> RHabitacion_Banyo_min_Medio RHabitacion_Banyo_min_Caro
#> 1 0.0000000 0.4285714
#> 2 0.2000000 0.5000000
#> 3 0.2500000 0.2500000
#> 4 0.0000000 0.4000000
#> 5 0.0000000 0.3333333
#> 6 0.0000000 0.0000000
#> 7 0.2500000 0.2000000
#> 8 0.2000000 0.0000000
#> 9 0.2500000 0.3333333
#> 10 0.0000000 0.0000000
#> 11 0.2500000 0.5000000
#> 12 0.3333333 0.5000000
#> 13 0.0000000 0.4000000
#> 14 0.0000000 0.0000000
#> 15 0.0000000 0.1666667
#> 16 0.2000000 0.6666667
#> RHabitacion_Banyo_max_Barato RHabitacion_Banyo_max_Medio
#> 1 2 1.500000
#> 2 2 1.000000
#> 3 1 1.000000
#> 4 1 1.000000
#> 5 1 1.333333
#> 6 2 1.000000
#> 7 1 1.000000
#> 8 2 1.250000
#> 9 2 1.000000
#> 10 1 1.333333
#> 11 1 1.000000
#> 12 1 2.000000
#> 13 2 2.000000
#> 14 1 2.000000
#> 15 1 2.000000
#> 16 1 1.000000
#> RHabitacion_Banyo_max_Caro RHabitacion_Banyo_sd_Barato
#> 1 1.0000000 0.4204983
#> 2 1.0000000 0.3289054
#> 3 0.7500000 0.2663316
#> 4 2.0000000 0.3003037
#> 5 1.2000000 0.3057108
#> 6 1.6666667 0.4368203
#> 7 1.6666667 0.4178554
#> 8 1.3333333 0.4526700
#> 9 0.5000000 0.3266120
#> 10 1.5000000 0.5000000
#> 11 1.0000000 0.2715822
#> 12 1.3333333 0.2679730
#> 13 1.0000000 0.5494801
#> 14 1.5000000 0.2271822
#> 15 3.0000000 0.3183298
#> 16 0.6666667 0.3161354
#> RHabitacion_Banyo_sd_Medio RHabitacion_Banyo_sd_Caro hasLift_media_Barato
#> 1 0.2573714 0.20048128 0.4761905
#> 2 0.2125404 0.19604679 0.3750000
#> 3 0.1835237 0.19431402 0.3750000
#> 4 0.2905745 0.24022140 0.3764706
#> 5 0.2077842 0.20571357 0.5757576
#> 6 0.3157249 0.29014294 0.3000000
#> 7 0.2220783 0.21841419 0.8571429
#> 8 0.2505582 0.30369645 0.3548387
#> 9 0.1547579 0.07876359 0.6309524
#> 10 0.2726240 0.25836300 0.0000000
#> 11 0.2004937 0.20736441 0.3716814
#> 12 0.3863399 0.34032029 0.4242424
#> 13 0.4132362 0.23754699 0.8701299
#> 14 0.3599403 0.28218798 0.3210526
#> 15 0.2945851 0.40521295 0.5079365
#> 16 0.2776944 NA 0.4666667
#> hasLift_media_Medio hasLift_media_Caro newDevelopment_media_Barato
#> 1 0.8125000 1.0000000 0.00000000
#> 2 0.8536585 1.0000000 0.03448276
#> 3 0.8125000 1.0000000 0.00000000
#> 4 0.8160920 1.0000000 0.00000000
#> 5 0.7924528 1.0000000 0.00000000
#> 6 0.6842105 0.9540816 0.05000000
#> 7 0.9523810 1.0000000 0.14285714
#> 8 0.8148148 0.8048780 0.00000000
#> 9 0.9696970 NaN 0.00000000
#> 10 0.8536585 0.9722222 0.00000000
#> 11 0.9285714 1.0000000 0.00000000
#> 12 0.8974359 0.7777778 0.00000000
#> 13 0.9701493 0.8333333 0.02531646
#> 14 0.7126437 0.6923077 0.02369668
#> 15 0.9107143 0.9444444 0.01515152
#> 16 0.9230769 1.0000000 0.02877698
#> newDevelopment_media_Medio newDevelopment_media_Caro good_media_Barato
#> 1 0.00000000 0.20000000 0.8571429
#> 2 0.17073171 0.18181818 0.7543860
#> 3 0.00000000 0.00000000 0.7500000
#> 4 0.02222222 0.07352941 0.7500000
#> 5 0.01851852 0.03225806 0.8823529
#> 6 0.02631579 0.00000000 0.9000000
#> 7 0.00000000 0.00000000 0.8333333
#> 8 0.05555556 0.06976744 0.9062500
#> 9 0.01449275 0.00000000 0.8965517
#> 10 0.01626016 0.02314815 1.0000000
#> 11 0.03225806 0.00000000 0.7142857
#> 12 0.02500000 0.00000000 0.9253731
#> 13 0.20289855 0.25000000 0.8987342
#> 14 0.03252033 0.11111111 0.7914692
#> 15 0.16091954 0.00000000 0.9090909
#> 16 0.18181818 0.00000000 0.9197080
#> good_media_Medio good_media_Caro renew_media_Barato renew_media_Medio
#> 1 0.8750000 0.8000000 0.14285714 0.12500000
#> 2 0.8250000 0.8181818 0.22807018 0.02500000
#> 3 0.9473684 0.6250000 0.25000000 0.05263158
#> 4 0.8863636 0.9117647 0.25000000 0.11363636
#> 5 0.9259259 0.9193548 0.11764706 0.05555556
#> 6 0.8333333 0.8457711 0.05000000 0.14035088
#> 7 0.8571429 0.9818182 0.16666667 0.14285714
#> 8 0.8333333 0.8928571 0.09375000 0.11111111
#> 9 0.9565217 0.7500000 0.10344828 0.02898551
#> 10 0.7804878 0.7813953 0.00000000 0.20325203
#> 11 0.9000000 1.0000000 0.28571429 0.10000000
#> 12 0.9743590 1.0000000 0.07462687 0.02564103
#> 13 0.7424242 0.8571429 0.07594937 0.09090909
#> 14 0.8455285 0.7833333 0.18483412 0.12195122
#> 15 0.7664671 0.9782609 0.07575758 0.10778443
#> 16 0.7000000 1.0000000 0.06569343 0.20000000
#> renew_media_Caro
#> 1 0.00000000
#> 2 0.00000000
#> 3 0.37500000
#> 4 0.01470588
#> 5 0.04838710
#> 6 0.15422886
#> 7 0.01818182
#> 8 0.05952381
#> 9 0.25000000
#> 10 0.20000000
#> 11 0.00000000
#> 12 0.00000000
#> 13 0.00000000
#> 14 0.15000000
#> 15 0.02173913
#> 16 0.00000000
Encontramos valores faltantes en Jesús y Rascanya
resumen_distritos %>%
filter(if_any(everything(), is.na)) %>%
dplyr::select(district, everything())
#> district size_media_Barato size_media_Medio size_media_Caro size_min_Barato
#> 1 Jesús 83.10345 103.2899 160.75 31
#> 2 Rascanya 74.99281 121.5758 122.00 26
#> size_min_Medio size_min_Caro size_max_Barato size_max_Medio size_max_Caro
#> 1 72 134 156 220 241
#> 2 78 122 138 420 122
#> size_sd_Barato size_sd_Medio size_sd_Caro rooms_media_Barato
#> 1 19.10134 28.46003 53.5 2.712644
#> 2 24.56978 65.04759 NA 2.201439
#> rooms_media_Medio rooms_media_Caro rooms_min_Barato rooms_min_Medio
#> 1 3.202899 5.75 1 2
#> 2 3.151515 3.00 0 1
#> rooms_min_Caro rooms_max_Barato rooms_max_Medio rooms_max_Caro
#> 1 5 5 8 6
#> 2 3 5 7 3
#> rooms_sd_Barato rooms_sd_Medio rooms_sd_Caro bathrooms_media_Barato
#> 1 0.8748162 1.092484 0.5 1.206897
#> 2 1.2109396 1.301951 NA 1.179856
#> bathrooms_media_Medio bathrooms_media_Caro bathrooms_min_Barato
#> 1 1.739130 2.25 1
#> 2 1.636364 2.00 1
#> bathrooms_min_Medio bathrooms_min_Caro bathrooms_max_Barato
#> 1 1 2 3
#> 2 1 2 2
#> bathrooms_max_Medio bathrooms_max_Caro bathrooms_sd_Barato bathrooms_sd_Medio
#> 1 3 3 0.4350336 0.5044558
#> 2 2 2 0.3854566 0.4885042
#> bathrooms_sd_Caro distance_media_Barato distance_media_Medio
#> 1 0.5 2405.587 1806.91
#> 2 NA 2760.813 2738.88
#> distance_media_Caro distance_min_Barato distance_min_Medio distance_min_Caro
#> 1 1858.813 1413.613 1129.127 1622.759
#> 2 2305.936 2195.991 2171.988 2305.936
#> distance_max_Barato distance_max_Medio distance_max_Caro distance_sd_Barato
#> 1 3773.648 3469.806 1998.006 594.8939
#> 2 3917.546 3241.399 2305.936 255.0294
#> distance_sd_Medio distance_sd_Caro distancia_min_estacion_m_media_Barato
#> 1 495.6640 164.6601 677.4085
#> 2 313.3577 NA 340.3013
#> distancia_min_estacion_m_media_Medio distancia_min_estacion_m_media_Caro
#> 1 428.5356 405.7809
#> 2 274.7310 112.7875
#> distancia_min_estacion_m_min_Barato distancia_min_estacion_m_min_Medio
#> 1 27.15554 37.67927
#> 2 16.96371 49.22107
#> distancia_min_estacion_m_min_Caro distancia_min_estacion_m_max_Barato
#> 1 247.2710 1618.4799
#> 2 112.7875 789.5994
#> distancia_min_estacion_m_max_Medio distancia_min_estacion_m_max_Caro
#> 1 1566.8222 575.3782
#> 2 805.5647 112.7875
#> distancia_min_estacion_m_sd_Barato distancia_min_estacion_m_sd_Medio
#> 1 473.7604 298.7049
#> 2 185.8591 188.0761
#> distancia_min_estacion_m_sd_Caro priceByArea_media_Barato
#> 1 159.9943 2167.391
#> 2 NA 2141.662
#> priceByArea_media_Medio priceByArea_media_Caro priceByArea_min_Barato
#> 1 3411.710 3648.5 650
#> 2 3053.303 4836.0 668
#> priceByArea_min_Medio priceByArea_min_Caro priceByArea_max_Barato
#> 1 1978 2282 6290
#> 2 1012 4836 6250
#> priceByArea_max_Medio priceByArea_max_Caro priceByArea_sd_Barato
#> 1 5000 4104 870.5196
#> 2 5449 4836 928.9510
#> priceByArea_sd_Medio priceByArea_sd_Caro RHabitacion_Banyo_media_Barato
#> 1 833.2664 911 0.5176245
#> 2 913.4221 NA 0.5334532
#> RHabitacion_Banyo_media_Medio RHabitacion_Banyo_media_Caro
#> 1 0.5630780 0.3916667
#> 2 0.6005772 0.6666667
#> RHabitacion_Banyo_min_Barato RHabitacion_Banyo_min_Medio
#> 1 0.2 0.25
#> 2 0.0 0.20
#> RHabitacion_Banyo_min_Caro RHabitacion_Banyo_max_Barato
#> 1 0.3333333 2
#> 2 0.6666667 1
#> RHabitacion_Banyo_max_Medio RHabitacion_Banyo_max_Caro
#> 1 1 0.5000000
#> 2 1 0.6666667
#> RHabitacion_Banyo_sd_Barato RHabitacion_Banyo_sd_Medio
#> 1 0.3266120 0.1547579
#> 2 0.3161354 0.2776944
#> RHabitacion_Banyo_sd_Caro hasLift_media_Barato hasLift_media_Medio
#> 1 0.07876359 0.6309524 0.9696970
#> 2 NA 0.4666667 0.9230769
#> hasLift_media_Caro newDevelopment_media_Barato newDevelopment_media_Medio
#> 1 NaN 0.00000000 0.01449275
#> 2 1 0.02877698 0.18181818
#> newDevelopment_media_Caro good_media_Barato good_media_Medio good_media_Caro
#> 1 0 0.8965517 0.9565217 0.75
#> 2 0 0.9197080 0.7000000 1.00
#> renew_media_Barato renew_media_Medio renew_media_Caro
#> 1 0.10344828 0.02898551 0.25
#> 2 0.06569343 0.20000000 0.00
library(tidyr)
resumen_distritos %>%
mutate(row = row_number()) %>%
pivot_longer(-c(district, row), names_to = "variable", values_to = "valor") %>%
filter(is.na(valor)) %>%
dplyr::select(district, variable) %>%
distinct() %>%
arrange(district)
#> # A tibble: 8 × 2
#> district variable
#> <chr> <chr>
#> 1 Jesús hasLift_media_Caro
#> 2 Rascanya size_sd_Caro
#> 3 Rascanya rooms_sd_Caro
#> 4 Rascanya bathrooms_sd_Caro
#> 5 Rascanya distance_sd_Caro
#> 6 Rascanya distancia_min_estacion_m_sd_Caro
#> 7 Rascanya priceByArea_sd_Caro
#> 8 Rascanya RHabitacion_Banyo_sd_Caro
resumen_distritos[is.na(resumen_distritos)] <- 0
library(dplyr)
library(tidyr)
library(cluster)
library(factoextra)
library(tibble)
# 1) Ponemos DISTRITO como rownames y quitamos la columna
datos_raw <- resumen_distritos %>%
column_to_rownames("district")
# 2) Seleccionamos sólo las columnas numéricas
datos_num <- datos_raw %>% dplyr::select(where(is.numeric))
# 3) Estandarizamos
datos_Z <- scale(datos_num, center = TRUE, scale = TRUE)
datos_Z <- as.data.frame(datos_Z)
# 4) Distancias y heatmap
dist_mat <- dist(datos_Z, method = "euclidean")
factoextra::fviz_dist(dist_mat,
show_labels = TRUE,
lab_size = 3)
dist_mat_manh <- dist(datos_Z, method = "manhattan")
factoextra::fviz_dist(dist_mat_manh,
show_labels = TRUE,
lab_size = 3)
set.seed(1)
n_obs <- nrow(datos_Z) # 16
m_vec <- c(5, 8, 10, 12) # tamaños de muestra viables
seeds <- sample(1:1000, 10) # 10 semillas
hop_val <- numeric()
for (m in m_vec) {
for (s in seeds) {
h <- get_clust_tendency(data = datos_Z,
n = m,
seed = s,
graph = FALSE)$hopkins_stat
hop_val <- c(hop_val, as.numeric(h))
}
}
summary(hop_val)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 0.5408 0.5533 0.5609 0.5677 0.5780 0.6221
library(grid)
library(gridExtra)
#> Warning: package 'gridExtra' was built under R version 4.4.3
#>
#> Adjuntando el paquete: 'gridExtra'
#> The following object is masked from 'package:dplyr':
#>
#> combine
p1 = fviz_nbclust(x = datos_Z, FUNcluster = hcut, method = "silhouette",
hc_method = "ward.D2", k.max = 10, verbose = FALSE,
hc_metric = "euclidean") + labs(title = "Num. optimo clusters")
p2 = fviz_nbclust(x = datos_Z, FUNcluster = hcut, method = "wss",
hc_method = "ward.D2", k.max = 10, verbose = FALSE,
hc_metric = "euclidean") + labs(title = "Num. optimo clusters")
grid.arrange(p1, p2, nrow = 1)
dist_mat <- dist(datos_Z, method = "euclidean")
clust1 <- hclust(dist_mat, method="ward.D2")
grupos1 <- cutree(clust1, k=5)
table(grupos1)
#> grupos1
#> 1 2 3 4 5
#> 5 4 4 2 1
fviz_dend(clust1, k = 5,
cex = 0.5, color_labels_by_k = TRUE,
rect = TRUE) # dibujar rectángulos
#> Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
#> of ggplot2 3.3.4.
#> ℹ The deprecated feature was likely used in the factoextra package.
#> Please report the issue at <https://github.com/kassambara/factoextra/issues>.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
p1 = fviz_nbclust(x = datos_Z, FUNcluster = kmeans, method = "silhouette",
k.max = 10, verbose = FALSE) +
labs(title = "K-means")
p2 = fviz_nbclust(x = datos_Z, FUNcluster = kmeans, method = "wss",
k.max = 10, verbose = FALSE) +
labs(title = "K-means")
grid.arrange(p1, p2, nrow = 1)
library(NbClust)
res.km <- NbClust(data = datos_Z, distance = "euclidean",
min.nc = 2, max.nc = 6, method = "kmeans",
index = "silhouette")
res.km$All.index # todos los valores de índice para k = 2,…,8
#> 2 3 4 5 6
#> 0.0951 0.1263 0.1266 0.1740 0.2236
res.km$Best.nc
#> Number_clusters Value_Index
#> 6.0000 0.2236
p1 = fviz_nbclust(x = datos_Z, FUNcluster = pam, method = "silhouette",
k.max = 10, verbose = FALSE) +
labs(title = "Numero optimo de clusters")
p2 = fviz_nbclust(x = datos_Z, FUNcluster = pam, method = "wss",
k.max = 10, verbose = FALSE) +
labs(title = "Numero optimo de clusters")
grid.arrange(p1, p2, nrow = 1)
clust4 <- pam(datos_Z, k =4)
table(clust4$clustering)
#>
#> 1 2 3 4
#> 7 4 4 1
library(ggsci)
#> Warning: package 'ggsci' was built under R version 4.4.3
# ward
dist_mat <- dist(datos_Z, method = "euclidean")
clust1 <- hclust(dist_mat, method="ward.D2")
grupos1 <- cutree(clust1, k=3)
# k-medias
set.seed(114)
clust3 <- kmeans(datos_Z, centers = 4, nstart = 20)
# k-medoides
clust4 <- pam(datos_Z, k =6)
colores = pal_npg("nrc")(6)
colores2 = pal_npg("nrc")(7)
par(mfrow = c(1,3))
plot(silhouette(grupos1, dist_mat), col=colores, border=NA, main = "WARD")
plot(silhouette(clust3$cluster, dist_mat), col=colores, border=NA, main = "K-MEDIAS")
plot(silhouette(clust4$clustering, dist_mat), col=colores2, border=NA, main = "K-MEDOIDES")
Después de analizar detenidamente las gráficas, los resultados en cada método, decidimos quedarnos en este caso con 4 clústers mediante el método de k-means.
Tiene el coeficiente de silhouette más alto que podemos exigir. Además se separan correctamente los clusters, sin clasificar incorrectamente ningún cluster, a diferencia de otro número de clusters en este método y en los demás.
library(dplyr) # lo ponemos arriba para que use las instrucciones de esta librería
distr_cluster <- data.frame(
DISTRITO = rownames(datos_Z),
CLUSTER = clust3$cluster
)
# 2. Mostrar lista de distritos agrupados por cluster
clusters_lista <- split(distr_cluster$DISTRITO, distr_cluster$CLUSTER)
# 3. Imprimir de forma legible
for (i in sort(unique(distr_cluster$CLUSTER))) {
cat(paste0("\n📦 Cluster ", i, ":\n"))
print(clusters_lista[[as.character(i)]])
}
#>
#> 📦 Cluster 1:
#> [1] "Ciutat Vella" "El Pla del Real" "Extramurs" "L'Eixample"
#>
#> 📦 Cluster 2:
#> [1] "Benicalap" "Camins al Grau" "Campanar" "Patraix"
#> [5] "Poblats Marítims" "Quatre Carreres"
#>
#> 📦 Cluster 3:
#> [1] "Algirós" "Benimaclet" "Jesús" "L'Olivereta"
#>
#> 📦 Cluster 4:
#> [1] "La Saïdia" "Rascanya"
Similitudes notables: Cluster de lujo/alta centralidad:
En ambos años, existe un cluster que agrupa Ciutat Vella, L’Eixample, El Pla del Real y Extramurs, lo que sugiere que estos distritos mantienen características similares a lo largo del tiempo (probablemente elevados precios por m², buena ubicación, servicios, etc.).
Agrupamiento de distritos más periféricos o con precios medios:
Campanar, Patraix y Camins al Grau están juntos tanto en 2025 (Cluster 2) como en 2018 (Cluster 2). Esto sugiere estabilidad en su perfil medio (ni caros ni baratos).
Benimaclet y Jesús vuelven a estar juntos en un cluster en ambos años (Cluster 1 en 2025 y Cluster 3 en 2018), lo cual indica cierta homogeneidad entre ellos mantenida en el tiempo.
Diferencias y cambios destacables: Número de clusters:
En 2025 hay 5 clusters, mientras que en 2018 solo hay 4, lo cual indica una mayor diferenciación interna en el mercado en 2025.
Reubicación de distritos como Benicalap, Rascanya y La Saïdia:
En 2018, La Saïdia y Rascanya forman un cluster separado (Cluster 4), mientras que en 2025 están integrados con Poblat Marítims y otros en un cluster más amplio (Cluster 5). Esto puede sugerir que en 2025 se diluyen diferencias o convergen en perfil (por ejemplo, con precios más bajos o mayor heterogeneidad).
Poblat Marítims cambia completamente de grupo:
En 2018 está en Cluster 2 (con Benicalap, Campanar…), pero en 2025 está en Cluster 5 (junto con barrios más periféricos). Esto podría indicar una variación en su perfil socioeconómico, o una transformación del barrio (gentrificación, turismo, etc.).
Quatre Carreres:
En 2018 está junto a distritos medios en Cluster 2.
En 2025 también, pero se mantiene en el mismo grupo que Poblat Marítims y Rascanya (Cluster 5). Esto sugiere que Quatre Carreres ha podido volverse más similar a estos distritos.
Se mantiene cierta estabilidad en los grupos centrales de distritos de mayor valor y localización privilegiada.
Hay más fragmentación y cambio en los distritos medios y bajos, especialmente Benicalap, Poblat Marítims, La Saïdia y Rascanya, que cambian de grupo y podrían estar experimentando transformaciones importantes en precio, perfil de vivienda o población.
La aparición de 5 clusters en 2025 frente a 4 en 2018 sugiere una mayor complejidad del mercado o una mayor capacidad de diferenciación.
perfiles = aggregate(datos_Z, by = list("cluster" = clust3$cluster), mean)
kable(perfiles)
| cluster | size_media_Barato | size_media_Medio | size_media_Caro | size_min_Barato | size_min_Medio | size_min_Caro | size_max_Barato | size_max_Medio | size_max_Caro | size_sd_Barato | size_sd_Medio | size_sd_Caro | rooms_media_Barato | rooms_media_Medio | rooms_media_Caro | rooms_min_Barato | rooms_min_Medio | rooms_min_Caro | rooms_max_Barato | rooms_max_Medio | rooms_max_Caro | rooms_sd_Barato | rooms_sd_Medio | rooms_sd_Caro | bathrooms_media_Barato | bathrooms_media_Medio | bathrooms_media_Caro | bathrooms_min_Barato | bathrooms_min_Medio | bathrooms_min_Caro | bathrooms_max_Barato | bathrooms_max_Medio | bathrooms_max_Caro | bathrooms_sd_Barato | bathrooms_sd_Medio | bathrooms_sd_Caro | distance_media_Barato | distance_media_Medio | distance_media_Caro | distance_min_Barato | distance_min_Medio | distance_min_Caro | distance_max_Barato | distance_max_Medio | distance_max_Caro | distance_sd_Barato | distance_sd_Medio | distance_sd_Caro | distancia_min_estacion_m_media_Barato | distancia_min_estacion_m_media_Medio | distancia_min_estacion_m_media_Caro | distancia_min_estacion_m_min_Barato | distancia_min_estacion_m_min_Medio | distancia_min_estacion_m_min_Caro | distancia_min_estacion_m_max_Barato | distancia_min_estacion_m_max_Medio | distancia_min_estacion_m_max_Caro | distancia_min_estacion_m_sd_Barato | distancia_min_estacion_m_sd_Medio | distancia_min_estacion_m_sd_Caro | priceByArea_media_Barato | priceByArea_media_Medio | priceByArea_media_Caro | priceByArea_min_Barato | priceByArea_min_Medio | priceByArea_min_Caro | priceByArea_max_Barato | priceByArea_max_Medio | priceByArea_max_Caro | priceByArea_sd_Barato | priceByArea_sd_Medio | priceByArea_sd_Caro | RHabitacion_Banyo_media_Barato | RHabitacion_Banyo_media_Medio | RHabitacion_Banyo_media_Caro | RHabitacion_Banyo_min_Barato | RHabitacion_Banyo_min_Medio | RHabitacion_Banyo_min_Caro | RHabitacion_Banyo_max_Barato | RHabitacion_Banyo_max_Medio | RHabitacion_Banyo_max_Caro | RHabitacion_Banyo_sd_Barato | RHabitacion_Banyo_sd_Medio | RHabitacion_Banyo_sd_Caro | hasLift_media_Barato | hasLift_media_Medio | hasLift_media_Caro | newDevelopment_media_Barato | newDevelopment_media_Medio | newDevelopment_media_Caro | good_media_Barato | good_media_Medio | good_media_Caro | renew_media_Barato | renew_media_Medio | renew_media_Caro |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | -1.1483239 | -0.6548334 | 0.1904053 | 0.4381211 | -0.8814586 | -0.3525933 | -0.8748204 | -0.1610385 | 0.5361948 | 0.2520610 | -0.0785474 | 0.1522000 | -1.2413700 | -0.6352485 | 0.0402493 | -0.6527912 | -0.2363842 | -0.6014255 | -1.2247449 | 0.3939736 | 1.2406135 | -0.2310442 | 0.4688694 | 0.3768325 | -0.1405434 | -0.9368812 | 0.2751685 | 0.3659625 | -0.1550434 | -0.5833333 | -0.9139077 | 0.6014255 | 0.4802315 | -0.7430587 | 0.9023886 | -0.0421798 | -1.3369286 | -1.2474604 | -1.2382922 | -1.1238347 | -1.1042083 | -1.2156767 | -1.4006646 | -1.3864255 | -1.1076052 | -0.7826936 | -0.8105921 | -0.3493258 | -0.1339331 | -0.0214825 | -0.4034626 | 0.9843011 | 0.1937828 | -0.7880005 | -0.6635008 | -0.4500156 | 0.0809863 | -0.5100312 | -0.4833556 | -0.0886864 | 1.4751376 | 1.3079897 | 0.4830047 | 1.3678388 | 0.5802576 | -0.2894545 | -0.0158930 | 0.6344724 | 0.9035857 | 0.6757747 | 0.7527377 | 0.5055085 | 0.4055817 | -0.1498326 | 0.2565300 | -0.6398917 | -0.0648749 | -1.1363944 | 0.2500000 | -0.4541376 | 0.3684820 | 1.0155687 | -0.0475620 | 0.4095407 | -0.3655721 | -0.3520181 | 0.2335597 | 0.7732409 | -0.4866304 | -0.4271283 | 0.6703717 | -0.3420589 | 0.0358804 | -0.6888844 | 0.8417276 | 0.2408273 |
| 2 | 0.7008620 | 0.2092034 | -0.0057440 | -0.0458984 | -0.2991617 | -0.6334975 | 0.9164153 | 0.3548425 | 0.3710438 | 0.4746655 | 0.1533906 | 0.6606548 | 0.1919226 | -0.3486257 | -0.3084069 | -0.3046359 | -0.6566227 | -0.6587041 | 0.4082483 | -0.1313245 | -0.1638546 | 0.1745789 | -0.0258509 | 0.6505215 | -0.0826511 | 0.4148083 | 0.1253483 | -0.6099375 | -0.3617680 | -0.4722222 | 0.4787136 | 0.3723110 | 0.5294860 | 0.1717653 | -0.2408780 | 0.5792097 | 0.6617547 | 0.7346260 | 0.7253058 | 0.4211177 | 0.5346715 | 0.5011442 | 0.6759649 | 0.8572299 | 0.9372222 | 0.6170412 | 0.7719530 | 0.7433035 | 0.3915635 | 0.5073385 | 0.6231031 | -0.3947196 | -0.0508884 | 0.0728024 | 0.5757333 | 0.5658996 | 0.6552364 | 0.3531120 | 0.7204619 | 0.4672800 | -0.4140701 | -0.3295745 | 0.3563962 | -0.6422000 | -0.5226975 | -0.3489100 | 0.2307799 | 0.5064403 | 0.3370921 | 0.0626899 | 0.3515334 | 0.5506171 | -0.1343327 | 0.8293023 | 0.3226626 | -0.3074804 | -0.6811866 | 0.0355777 | -0.0833333 | 0.5114162 | 0.4930392 | -0.1955741 | 0.3772484 | 0.3412569 | 0.2329924 | -0.1618448 | 0.1501235 | -0.0980749 | 0.5675936 | 0.5907263 | -0.3026266 | -0.2676127 | 0.0592708 | 0.2394945 | -0.3067986 | -0.3676436 |
| 3 | 0.2250301 | 0.1827454 | 0.5020129 | 0.1376952 | 0.9295381 | 1.1689711 | -0.3225817 | -0.7800958 | -0.6070323 | -0.6804981 | -0.8538014 | -0.4425642 | 0.8557353 | 0.9788229 | 1.0238212 | 1.4361407 | 1.0243315 | 1.2887689 | 0.3061862 | -0.2363842 | -0.3511170 | -0.2318692 | -0.9332244 | -0.5646678 | 0.4924283 | 0.5614775 | -0.2772089 | 0.3659625 | 0.4651303 | 0.7500000 | 0.3916747 | -0.7732613 | -0.7018768 | 0.6390206 | -0.7144322 | -0.3209085 | 0.2553043 | 0.0486191 | 0.1895712 | 0.3040866 | 0.1397614 | 0.3354847 | 0.3018774 | 0.1900438 | -0.1023920 | 0.1730980 | -0.1230157 | -0.3250935 | 0.0121557 | -0.2215218 | 0.1793924 | 0.1837820 | 0.2448145 | 0.9345317 | 0.0512912 | -0.0525319 | -0.1678349 | 0.2327619 | -0.3275213 | 0.0517919 | -0.5888525 | -0.5284992 | -1.1901661 | -0.1230823 | 0.4645041 | -0.0847251 | -0.6375088 | -1.0033297 | -1.0296769 | -0.6632286 | -1.1369426 | -0.6930538 | 0.0981339 | -1.0337194 | -1.1442797 | 1.4210581 | 0.5189993 | 0.4011214 | 0.2500000 | -0.5032336 | -0.8424916 | -0.3776184 | -0.9491384 | -0.6140365 | 0.0391385 | 0.2823989 | -0.4887742 | -0.5489447 | -0.6630258 | -0.1061375 | -0.6295950 | 0.8441735 | -0.7148169 | 0.7185150 | -0.4721025 | 0.6674192 |
| 4 | -0.2559984 | 0.3165658 | -1.3676042 | -1.0139374 | 0.8013260 | 0.2677368 | -0.3544417 | 0.8177411 | -0.9714565 | -0.5671223 | 1.4045258 | -1.4012359 | 0.1955016 | 0.3587280 | -1.2029202 | -0.6527912 | 0.3939736 | 0.6014255 | 0.6123724 | 0.0787947 | -1.2874291 | 0.4020901 | 1.0062627 | -1.5758940 | -0.4558164 | -0.4936175 | -0.3719642 | 0.3659625 | 0.4651303 | 1.0833333 | -0.3916747 | -0.7732613 | -1.1451674 | -0.3072198 | 0.3467213 | -1.0114524 | 0.1779847 | 0.1938046 | -0.0784757 | 0.3761431 | 0.3248791 | 0.2569516 | 0.1696798 | -0.1789263 | -0.3916724 | -0.6319324 | -0.4486433 | -0.8810717 | -0.9311355 | -1.0360070 | -1.4211689 | -1.1520074 | -0.7245293 | -0.5114694 | -0.5027807 | -0.6926038 | -1.7920120 | -0.5047973 | -0.5396319 | -1.3280511 | -0.5303601 | -0.5702575 | 0.3451343 | -0.5629131 | -0.5214309 | 1.7950893 | 0.6144639 | -0.7816064 | -0.7590938 | -0.2131618 | -0.2861905 | -1.2767609 | -0.6044332 | -0.1208030 | 0.8075116 | -0.6398917 | 1.1353110 | 1.3638128 | -0.7500000 | 0.3804937 | -0.5310984 | -0.6891783 | 0.8616553 | -0.6147790 | -0.0461101 | 0.6247728 | 0.0600586 | -0.1543676 | 0.5965317 | -0.7056474 | 0.8263265 | -0.2013910 | 1.1800607 | -0.7777448 | 0.1811456 | -0.7135623 |
-> Valores positivos indican que ese grupo (cluster) tiene mayores valores que la media general de esa variable.
-> Valores negativos indican que ese grupo tiene valores inferiores a la media general.
# Quitamos la columna "cluster" para aplicar la lógica solo a las variables numéricas
perfiles_sin_cluster <- perfiles %>% dplyr::select(-cluster)
# Creamos un resumen con el número de veces que está por encima o debajo de la media
conteo_cluster <- perfiles_sin_cluster %>%
mutate(cluster = perfiles$cluster) %>%
rowwise() %>%
mutate(
por_encima = sum(c_across(everything()) > 0, na.rm = TRUE),
por_debajo = sum(c_across(everything()) < 0, na.rm = TRUE)
) %>%
dplyr::select(cluster, por_encima, por_debajo)
conteo_cluster
#> # A tibble: 4 × 3
#> # Rowwise:
#> cluster por_encima por_debajo
#> <int> <int> <int>
#> 1 1 41 56
#> 2 2 62 35
#> 3 3 49 48
#> 4 4 38 59
conteo_cluster_por <- perfiles_sin_cluster %>%
mutate(cluster = perfiles$cluster) %>%
rowwise() %>%
mutate(
total_vars = ncol(perfiles_sin_cluster),
por_encima = sum(c_across(everything()) > 0, na.rm = TRUE),
por_debajo = sum(c_across(everything()) < 0, na.rm = TRUE),
pct_encima = por_encima / total_vars,
pct_debajo = por_debajo / total_vars
) %>%
dplyr::select(cluster, por_encima, por_debajo, pct_encima, pct_debajo)
conteo_cluster_por
#> # A tibble: 4 × 5
#> # Rowwise:
#> cluster por_encima por_debajo pct_encima pct_debajo
#> <int> <int> <int> <dbl> <dbl>
#> 1 1 42 56 0.438 0.583
#> 2 2 63 35 0.656 0.365
#> 3 3 50 48 0.521 0.5
#> 4 4 39 59 0.406 0.615
matplot(t(perfiles[,-1]), type = "l", ylab = "medias", lty = 1, lwd = 2,
col = rainbow(4), xlab = "", xaxt = "n")
axis(side = 1, at = 1:ncol(datos_Z), labels = colnames(datos_Z), las = 2, cex.axis = 0.6)
legend("topright", as.character(1:4), col = rainbow(4), lwd = 2, bty = "n")
Extraer las variables que más distinguen los clusters Como estás trabajando con valores escalados, una buena forma de detectar las variables más relevantes es calcular la varianza entre clusters para cada variable:
La varianza mide la dispersión de los valores medios por cada cluster.
-> Una varianza más elevada se traduce en mayores diferencias entre clusters, por lo que nos será más fácil explicar por qué se formaron.
-> Una varianza baja, por otra parte, indicaría que todos los clusters son más o menos iguales en esa variable.
library(dplyr)
library(tidyr)
df_clusters <- data.frame(cluster = clust3$cluster, datos_raw)
# datos_centrados <- as.data.frame(scale(df_clusters, center = TRUE, scale = FALSE))
# Calculamos varianza entre clusters
variabilidad <- df_clusters %>%
pivot_longer(-cluster, names_to = "variable", values_to = "valor") %>%
group_by(variable) %>%
summarise(varianza = var(valor)) %>%
arrange(desc(varianza))
top_vars <- head(variabilidad, 96)
print(top_vars)
#> # A tibble: 96 × 2
#> variable varianza
#> <chr> <dbl>
#> 1 priceByArea_max_Caro 12450250.
#> 2 priceByArea_max_Medio 2929549.
#> 3 priceByArea_max_Barato 1583606.
#> 4 distance_max_Barato 1266156.
#> 5 distance_max_Medio 993254.
#> 6 distance_max_Caro 924513.
#> 7 distance_media_Caro 877006.
#> 8 distance_min_Caro 865452.
#> 9 priceByArea_min_Caro 749570.
#> 10 distance_media_Barato 707396.
#> # ℹ 86 more rows
Precio por m² es el factor maestro
Sobre todo el máximo dentro de cada segmento de precio (priceByArea_max_Caro, priceByArea_max_Medio, priceByArea_max_Barato).
El clúster “caro” está definido ante todo por viviendas cuyo €/m² punta es muy superior al resto.
Distancia al centro urbano importa (en negativo)
Tanto la distancia máxima como la media y mínima son muy discriminantes.
Sugiere que algunos clústeres agrupan barrios periféricos (distancias altas) frente a otros más céntricos.
Extremos pesan más que promedios
Las variables _max o _min aparecen antes que _media o _sd.
Esto indica que los clústeres se han formado atendiendo a outliers internos (viviendas muy caras o muy alejadas) más que al nivel medio del distrito.
Tamaño empieza a contar, pero solo el máximo
size_max_Caro/Medio entra a mitad de tabla: hay clústeres con viviendas sensiblemente más grandes, pero la separación que ofrece es mucho menor que la de precio o distancia.
Habitabilidad (nº habitaciones/baños) y flags casi no discriminan
rooms_, bathrooms_, hasLift, newDevelopment, good, renew tienen varianzas entre clúster próximas a cero → los clústeres son muy similares en estos aspectos.
En resumen:
-> Los máximos de precio €/m² y de distancia al centro separan los clústeres con muchísima fuerza.
-> Las medias y mínimos de distancia (y en menor grado el precio €/m² mínimo) siguen influyendo bastante.
-> El tamaño máximo de la vivienda empieza a diferenciar, pero ya con mucha menos intensidad.
-> Variables de habitabilidad y flags aportan poquísimo poder discriminante.
Para averiguar que variables distinguen más a cada cluster individualmente, se puede calcular cuánto se desvía cada cluster de la media general por variable (z-score absoluto).
Este ranking_por_cluster nos mostrará un top-5 de variables más extremas por cluster, con su valor absoluto.
df_clusters <- data.frame(cluster = clust3$cluster, datos_Z) # aquí sí que usamos los datos centrados y escalados
ranking_por_cluster <- df_clusters %>%
pivot_longer(-cluster, names_to = "variable", values_to = "valor") %>%
mutate(valor_abs = abs(valor)) %>%
group_by(cluster) %>%
arrange(cluster, desc(valor_abs)) %>%
slice_head(n = 5) # top 5 por cluster
print(ranking_por_cluster)
#> # A tibble: 20 × 4
#> # Groups: cluster [4]
#> cluster variable valor valor_abs
#> <int> <chr> <dbl> <dbl>
#> 1 1 newDevelopment_media_Barato 3.37 3.37
#> 2 1 rooms_max_Caro 2.65 2.65
#> 3 1 rooms_max_Barato -2.45 2.45
#> 4 1 rooms_media_Barato -2.34 2.34
#> 5 1 priceByArea_media_Medio 2.33 2.33
#> 6 2 bathrooms_sd_Caro 3.11 3.11
#> 7 2 RHabitacion_Banyo_max_Caro 2.79 2.79
#> 8 2 bathrooms_media_Caro 2.67 2.67
#> 9 2 bathrooms_min_Barato -2.56 2.56
#> 10 2 bathrooms_min_Barato -2.56 2.56
#> 11 3 hasLift_media_Caro -3.45 3.45
#> 12 3 rooms_media_Caro 2.90 2.90
#> 13 3 distancia_min_estacion_m_sd_Barato 2.84 2.84
#> 14 3 RHabitacion_Banyo_media_Caro -2.79 2.79
#> 15 3 size_media_Caro 2.62 2.62
#> 16 4 size_sd_Medio 2.79 2.79
#> 17 4 priceByArea_min_Caro 2.78 2.78
#> 18 4 distancia_min_estacion_m_sd_Caro -2.62 2.62
#> 19 4 distancia_min_estacion_m_max_Caro -2.61 2.61
#> 20 4 RHabitacion_Banyo_sd_Caro -2.40 2.40
newDevelopment_media_Barato = +3.36 → viviendas “baratas” del cluster con un porcentaje inusualmente alto de obra nueva.
rooms_max_Caro = +2.65 → viviendas caras con muchos dormitorios.
rooms_max_Barato = −2.44 → pero las viviendas baratas tienen muy pocos dormitorios.
rooms_media_Barato = −2.34 → media también muy baja en nº de habitaciones.
priceByArea_media_Medio = +2.33 → precio por m² elevado en el segmento medio.
Alta presencia de obra nueva, especialmente en viviendas baratas.
Contrastes marcados: viviendas caras del cluster son espaciosas, las baratas sorprendentemente pequeñas.
Puede representar zonas de reciente desarrollo o expansión urbanística con polarización de producto inmobiliario.
bathrooms_sd_Caro = +3.11 → gran variabilidad en número de baños en viviendas caras.
RHabitacion_Banyo_max_Caro = +2.79 → relación habitaciones/baños muy alta.
bathrooms_media_Caro = +2.67 → viviendas caras con muchos baños.
bathrooms_min_Barato = −2.56 (repite dos veces) → viviendas baratas sin baños o con el mínimo posible.
Cluster definido por baños: en viviendas caras, hay muchos y son variables; en las baratas, apenas existen.
Podría reflejar una clara distinción en calidad y distribución de espacios entre extremos del mercado.
Probable que incluya zonas con bloques premium y otros con oferta más básica o antigua.
hasLift_media_Caro = −3.45 → viviendas caras sin ascensor: puede indicar edificios antiguos.
rooms_media_Caro = +2.90 → muchas habitaciones en viviendas caras.
distancia_min_estacion_m_sd_Barato = +2.84 → gran dispersión en distancia a estación para las baratas (algunas muy lejos).
RHabitacion_Banyo_media_Caro = −2.79 → baja relación habitaciones/baños → pisos grandes pero pocos baños.
Viviendas caras en este cluster son antiguas, grandes, sin modernidades (sin ascensor, pocos baños).
Baratas con ubicaciones muy dispares en accesibilidad.
Cluster que agrupa viviendas con superficies amplias pero no necesariamente alta calidad.
size_media_Caro = +2.61
size_sd_Medio = +2.79 → tamaño en el segmento medio es muy variable.
priceByArea_min_Caro = +2.77 → los precios por m² más bajos incluso dentro del grupo caro.
distancia_min_estacion_m_sd_Caro = −2.61
distancia_min_estacion_m_max_Caro = −2.61 → poco alejamiento a estaciones en las viviendas caras.
RHabitacion_Banyo_sd_Caro = −2.40 → poca dispersión en habitaciones por baño: hay uniformidad.
Viviendas caras amplias, pero con precio por m² bajo, lo que sugiere ubicaciones más periféricas.
Muy cerca de transporte público en el segmento caro.
Segmento posiblemente más familiar o suburbano: espacio, precio moderado, bien conectado.
Cluster 1: nuevas promociones; Cluster 2: baños como diferenciador de calidad; Cluster 3: grandes pero viejas; Cluster 4: bien conectadas y espaciosas pero baratas en €/m².
library(sf)
library(dplyr)
library(stringr)
library(stringi) # para quitar tildes y diéresis
library(ggplot2)
# 1) Carga del GeoJSON bruto
url_geo <- "https://valencia.opendatasoft.com/explore/dataset/districtes-distritos/download/?format=geojson&timezone=Europe/Madrid&lang=es"
distritos_raw <- st_read(url_geo, quiet = TRUE)
# 2) Normalizar nombres: – quitar tildes/diéresis – pasar a MAYÚSCULAS – eliminar "LA " al principio – quitar espacios sobrantes
distritos_sf <- distritos_raw %>%
mutate(
nombre_clean = nombre %>%
stri_trans_general("Latin-ASCII") %>% # convierte “Jesús”→“Jesus”, “Saïdia”→“Saidia”
str_to_upper() %>%
str_remove("^LA\\s+") %>% # quita “LA ” si existe
str_squish()
) %>%
group_by(nombre_clean) %>%
summarise(geometry = st_union(geometry), .groups = "drop") %>%
st_as_sf()
# 3) Prepara tu tabla de clusters; aplica el mismo limpieza de nombre:
distr_cluster <- tibble(
DISTRITO = rownames(datos_Z),
CLUSTER = factor(clust3$cluster)
) %>%
mutate(
DISTRITO_clean = DISTRITO %>%
stri_trans_general("Latin-ASCII") %>%
str_to_upper() %>%
str_remove("^LA\\s+") %>%
str_squish()
)
# 4) Une por el nombre limpio
distritos_map <- distritos_sf %>%
left_join(distr_cluster, by = c("nombre_clean" = "DISTRITO_clean"))
# 5) Pinta todo Valencia
ggplot(distritos_map) +
geom_sf(aes(fill = CLUSTER), color = "white", size = 0.2) +
scale_fill_brewer(palette = "Set2", na.value = "grey80") +
labs(
title = "Valencia: Distritos coloreados por Cluster",
subtitle = "Clusters k-means",
fill = "Cluster"
) +
theme_minimal(base_size = 14) +
theme(
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank()
)