La construcción de un indice compuesto ayuda a explicar las dinamicas territoriales, integrando distintas variables en una suma ponderada. Los pasos metodologicos implican la seleccion de indicadores, un análisis factorial para la determinacion de pesos y la integracion de los mismos en la sumatoria final. Los resultados se presentarán en un mapa interactivo para mejor visualización.
Se trabaja con datos continuos para volumen poblacional, Concentración de servicios, Acceso a servicios y Ubicación estratégica.
df_yungay <- read_excel("/Users/dalineportocarrero/Documents/Daline/4.PROYECTOS/PCM_PNUD/R_CUF/YUNGAY/BD_Jerarquia_310326ev.xlsx")
# Nuevo dataframe
df_red <- df_yungay[, c("COD","POB", "PIB_gs", "Acc_capital1", "A_IE", "A_IS")]
glimpse(df_red)## Rows: 95
## Columns: 6
## $ COD <chr> "NP - 1", "NP - 2", "NP - 3", "NP - 5", "NP - 8", "NP - 9…
## $ POB <dbl> 11932, 2898, 2461, 1388, 941, 899, 619, 676, 1390, 871, 8…
## $ PIB_gs <dbl> 79273.901, 82647.749, 30588.009, 26379.958, 46779.670, 97…
## $ Acc_capital1 <dbl> 1.937721, 5.203335, 23.180526, 11.881624, 22.076591, 1.05…
## $ A_IE <dbl> 1.4559873, 4.7662559, 1.1158021, 7.1962497, 22.8238485, 0…
## $ A_IS <dbl> 2.731443, 15.195762, 36.931645, 26.852967, 63.642009, 183…
## COD POB PIB_gs Acc_capital1
## Length:95 Min. : 51.0 Min. : 211.1 Min. : 0.00
## Class :character 1st Qu.: 131.0 1st Qu.: 644.5 1st Qu.: 24.30
## Mode :character Median : 193.0 Median : 1516.2 Median : 44.76
## Mean : 463.2 Mean : 6210.1 Mean : 98.62
## 3rd Qu.: 364.5 3rd Qu.: 4566.5 3rd Qu.:138.51
## Max. :11932.0 Max. :82647.7 Max. :631.30
## A_IE A_IS
## Min. : 0.000 Min. : 1.23
## 1st Qu.: 1.844 1st Qu.: 36.86
## Median : 10.451 Median : 92.16
## Mean : 15.267 Mean :130.15
## 3rd Qu.: 21.680 3rd Qu.:186.36
## Max. :107.482 Max. :630.79
Para aplicar el analisis factorial es necesario que todos los indicadores esten en una misma escala. Se aplica una normalizacion bajo metodo min max de 0 a 1. Asimismo, se han invertido los valores finales de los indicadores que guardan una relación contraría con la jerarquía, en este caso los indicadores de tiempos de desplazamiento, debido a que a menor tiempo de dezplazamiento, presentan mayor oportunidad y por ende mayor jerarquía.
# Normalizacion de variables seleccionadas
vars_modelo <- c("POB", "PIB_gs", "Acc_capital1", "A_IE", "A_IS")
# Función Min-Max → [0, 1]
minmax <- function(x) (x - min(x, na.rm = TRUE)) / (max(x, na.rm = TRUE) - min(x, na.rm = TRUE))
# Aplicar y guardar en nuevas columnas
df_red <- df_red |>
mutate(across(all_of(vars_modelo), minmax, .names = "{.col}_mm"))
df_red <- df_red |>
mutate(across(ends_with("_mm"), ~ round(., 3)))
#invertir los indicadores de tiempo
vars_tiempo <- c("Acc_capital1_mm", "A_IE_mm", "A_IS_mm")
df_red <- df_red |>
mutate(
across(
all_of(vars_tiempo),
~ 1 - .,
.names = "{.col}_inv"
)
)Se emplea el Análisis factorial (AF) con el objetivo de identificar estructuras latentes entre los indicadores y derivar ponderaciones que reflejen su contribución relativa para la construcción de un indice compuesto.
Teniendo un conjunto de indicadores X= (x1,X2….Xn) se construye una matriz de correlaciones.
El modelo factorial se expresa como
X = ΛF + ε donde:
Λ es la matriz de cargas factoriales (p x m) F es el vector de factores comunes (m x l) ε es el vector de errores específicos.
Asimismo, la matriz de correlaciones puede descomponerse como:
R= ΛΛ’ + Ψ
donde Ψ es la matriz diagonal de varianzas específicas.
# Matriz de datos estandarizados
vars_fa <- c("POB_mm", "PIB_gs_mm", "Acc_capital1_mm_inv", "A_IE_mm_inv", "A_IS_mm_inv")
mat_af <- df_red |>
select(all_of(vars_fa)) |>
as.matrix()
mat_cor <- df_red |>
select(all_of(vars_fa)) |>
cor(use = "complete.obs")
round(mat_cor, 2)## POB_mm PIB_gs_mm Acc_capital1_mm_inv A_IE_mm_inv
## POB_mm 1.00 0.75 0.14 0.15
## PIB_gs_mm 0.75 1.00 0.17 0.16
## Acc_capital1_mm_inv 0.14 0.17 1.00 0.21
## A_IE_mm_inv 0.15 0.16 0.21 1.00
## A_IS_mm_inv 0.16 0.19 0.87 0.11
## A_IS_mm_inv
## POB_mm 0.16
## PIB_gs_mm 0.19
## Acc_capital1_mm_inv 0.87
## A_IE_mm_inv 0.11
## A_IS_mm_inv 1.00
## corrplot 0.95 loaded
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = mat_af)
## Overall MSA = 0.52
## MSA for each item =
## POB_mm PIB_gs_mm Acc_capital1_mm_inv A_IE_mm_inv
## 0.53 0.54 0.51 0.53
## A_IS_mm_inv
## 0.51
## R was not square, finding R from data
## $chisq
## [1] 220.9732
##
## $p.value
## [1] 6.686125e-42
##
## $df
## [1] 10
Para el AF se necesita determinar el número de factores “m”, esto puede realizarse mediante: - Análisis paralelo - Valores propios mayores a 1 - Porcentaje acumulado de varianza explicada >= 60%
Se observa que los factores M1 y M2 presentan cargas mayores a 1 (1.814 y 1.524 respectivamente) y cuentan con una varianza acumulada de 67%
Una vez estimados los factores, se aplica una rotación ortogonal tipo varimax, con el objetivo de obtener una estructura simple que facilite la interpretación, maximizando las cargas altas y minimizando las bajas dentro de cada factor.
Para cada factor j, se obtiene su contribución a la varianza total:
donde
λij es la carga de la variable i en el factor j vj representa la proporción de varianza explicada por el factor j
El peso de cada indicador se construye en dos etapas:
Contribución de la variable dentro de cada factor
Ponderación por la importancia del factor
Finalmente los pesos se reescalan para asegurar que sumen la unidad.
## Parallel analysis suggests that the number of factors = 2 and the number of components = NA
# Modelo con n factores
fa_rot <- fa(
r = cor(mat_af),
nfactors = 2,
rotate = "varimax",
fm = "ml"
)
# Cargas factoriales rotadas, comunalidades y varianza por factor
print(fa_rot$loadings, cutoff = 0.3)##
## Loadings:
## ML1 ML2
## POB_mm 0.874
## PIB_gs_mm 0.851
## Acc_capital1_mm_inv 0.996
## A_IE_mm_inv
## A_IS_mm_inv 0.872
##
## ML1 ML2
## SS loadings 1.814 1.524
## Proportion Var 0.363 0.305
## Cumulative Var 0.363 0.668
comunalidades <- fa_rot$communality # h² por variable
var_por_factor <- fa_rot$Vaccounted # matriz: SS loadings, prop var, etc.
prop_var <- var_por_factor["Proportion Var", ] # proporción de varianza explicada
# Cargas factoriales rotadas (variables × factores)
cargas <- fa_rot$loadings[] # matriz completa sin truncar
# Peso de cada variable = suma ponderada de cargas² × proporción de varianza del factor
pesos_fa <- rowSums((cargas^2) * prop_var)
pesos_fa <- pesos_fa / sum(pesos_fa) # normalizar a suma = 1
print(round(pesos_fa, 5))## POB_mm PIB_gs_mm Acc_capital1_mm_inv A_IE_mm_inv
## 0.20257 0.22966 0.31025 0.01778
## A_IS_mm_inv
## 0.23974
Con los pesos obtenidos se procede a construir el indice compuesto de jerarquia.
# Combinación lineal simple con los pesos derivados
indice_fa <- as.vector(mat_af %*% pesos_fa)
# Añadir al dataframe
df_red <- df_red |>
mutate(
indice_fa = round (indice_fa, 5)
)El resultado se puede visualizar con un cluster exploratorio bajo el algoritmo K-means
#K-means
data_k <- df_red |> select("indice_fa")
# Elbow method
fviz_nbclust(data_k, FUNcluster = kmeans, method = "wss", k.max = 10) +
labs(title = "Elbow method — k-means")# Silhouette
fviz_nbclust(data_k, FUNcluster = kmeans, method = "silhouette", k.max = 10) +
labs(title = "Silhouette — validación k óptimo")# K-means (ajusta k según los gráficos anteriores)
set.seed(123)
km_yungay <- kmeans(data_k, centers = 6, nstart = 25)
# Asignar cluster al dataframe ← nombre corregido
df_red$cluster_km <- as.factor(km_yungay$cluster)
df_red |>
group_by(cluster_km) |>
summarise(
n = n(),
pob_max = max(POB),
pob_med = median(POB),
pob_min = min(POB)
) |>
arrange(desc(pob_max))## # A tibble: 6 × 5
## cluster_km n pob_max pob_med pob_min
## <fct> <int> <dbl> <dbl> <dbl>
## 1 2 2 11932 7415 2898
## 2 4 5 2461 941 220
## 3 1 36 1390 181 51
## 4 3 26 1029 180 63
## 5 6 20 491 202 53
## 6 5 6 314 180. 72
# Cargar y reproyectar a WGS84 geográfico
cp_puntos <- st_read("/Users/dalineportocarrero/Documents/Daline/4.PROYECTOS/PCM_PNUD/R_CUF/YUNGAY/BD_CCFF/BD_CCFF.shp") |> st_transform(4326)## Reading layer `BD_CCFF' from data source
## `/Users/dalineportocarrero/Documents/Daline/4.PROYECTOS/PCM_PNUD/R_CUF/YUNGAY/BD_CCFF/BD_CCFF.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 95 features and 44 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 163835.1 ymin: 8957433 xmax: 239084.5 ymax: 9004316
## Projected CRS: WGS 84 / UTM zone 18S
## [1] "ID_CP" "COD" "UBIGEO" "CODCP" "PROVINCIA"
## [6] "DISTRITO" "NOMBRE" "Y" "X" "CAPITAL"
## [11] "RANGO_POB" "T_AP" "ID_NP" "NOM_AP" "POB_CP"
## [16] "T_CP" "T_IE" "T_IS" "POB" "NIV_IE"
## [21] "NIV_IS" "PIB_gs" "PIB_cub" "PIB_cubn" "A_IE"
## [26] "A_IS" "UE" "P_POB" "P_IE" "P_IS"
## [31] "P_SERV_gs" "P_SERV_cub" "P_SERV_cn" "P_AIE" "P_AIS"
## [36] "P_UE" "SUMsinPIB" "SUM_J_gs" "SUM_J_cub" "SUM_J_n"
## [41] "nombdist" "TIPOLOGIA" "POBMIN" "VER_CF" "geometry"
## [1] "CP - 1" "CP - 2" "CP - 3" "CP - 4" "CP - 5" "CP - 6"
## [1] "NP - 1" "NP - 2" "NP - 3" "NP - 5" "NP - 8" "NP - 9"
# Unir clusters al shapefile de puntos
cp_mapa <- cp_puntos |>
left_join(df_red |>
select(COD, cluster_km),
by = "COD")
# Paleta por cluster
pal <- colorFactor(palette = brewer.pal(5, "Set2"),
domain = cp_mapa$indice_fa)
# Mapa
leaflet() |>
addProviderTiles("CartoDB.Positron") |>
# Puntos centros poblados
addCircleMarkers(data = cp_mapa,
radius = ~case_when(
cluster_km == 2 ~ 16,
cluster_km == 4 ~ 10,
cluster_km == 1 ~ 6,
cluster_km == 3 ~ 4,
cluster_km == 6 ~ 3,
cluster_km == 5 ~ 2,
),
fillColor = "#039ccf",
fillOpacity = 0.5,
color = "white",
weight = 0.2,
label = ~paste0("Cluster: ", cluster_km,
" | Pob: ", round(POB, 0)),
popup = ~paste0("<b>COD: ", COD, "</b><br>",
"Cluster: <b>", cluster_km, "</b><br>",
"Población: ", round(POB, 0))) |>
addLegend(
position = "bottomright",
title = "Jerarquía Kmeans",
colors = rep("#039ccf", 6),
labels = c("2 - Mayor", "4", "1","3","6", "5 - Menor"),
opacity = 0.35
)```