Examen práctico módulo 4: análisis de datos multivariantes II
DESCRIPCIÓN DATASET PARA EJERCICIOS 1, 2 Y 3
El dataset para las preguntas 1, 2 y 3 incluye información a nivel de las secciones censales de la ciudad de Santander, relativa a los censos de 2001 y 2011. Dicho dataset dispone de las siguientes variables:
- COD SECC: código de sección censal.
- 75_2001, 75_2011: porcentaje de población de 75 años y más.
- HOG_64_2001, HOG_64_2011: porcentaje de hogares unipersonales de mayores de 64 años.
- HOG_MONO_2001, HOG_MONO_2011: porcentaje de hogares con un adulto y un menor o más.
- EXT_2001, EXT_2011: porcentaje de población extranjera.
- EXT_MEN_2001, EXT_MEN_2011: porcentaje de población extranjera infantil.
EXTRACCIÓN DE DATOS 2011, ANÁLISIS EXPLORATORIO Y MATRIZ DE CORRELACIONES
En primer lugar se cargan las librerías necesarias para el trabajo solicitado.
library(ggplot2)
library(esquisse)
library(formattable)
library(corrplot)
library(knitr)
library(kableExtra)
library(psych)
library(cluster)
library(kableExtra)
library(fpc)
library(plm)
library(MASS)
library(ggrepel)
library(RGCCA)
library(ca)
library(FactoMineR)A continuación, se procede a leer el archivo crudo que se propone para esta prueba y se hace una primera aproximación exploratoria observando el encabezado y la cola del dataset, así como un análisis inicial somero de las caracteristicas de las variables.
datos_raw <- read.csv2("C:/Users/Oscar/OneDrive/Escritorio/Proyectos_R/Modulo_4/EVALUACION/Indicador_SOCIODEMO.csv", stringsAsFactors = FALSE,
check.names = FALSE
)
formattable(head(datos_raw))| COD SECC | 75_2001 | 75_2011 | HOG_64_2001 | HOG_64_2011 | HOG_MONO_2001 | HOG_MONO_2011 | EXT_2001 | EXT_2011 | EXT_MEN_2001 | EXT_MEN_2011 |
|---|---|---|---|---|---|---|---|---|---|---|
| 3907501001 | 14.20 | 8.33 | 14.77 | 12.73 | 1.87 | 3.01 | 5.27 | 30.83 | 6.93 | 0.0 |
| 3907501002 | 14.17 | 18.45 | 11.46 | 14.12 | 1.04 | 0.00 | 1.55 | 14.00 | 1.98 | 0.0 |
| 3907501003 | 19.15 | 12.80 | 12.42 | 22.46 | 1.35 | 5.32 | 1.77 | 6.37 | 5.06 | 0.0 |
| 3907501004 | 21.11 | 27.78 | 15.56 | 27.99 | 1.11 | 0.00 | 2.04 | 9.36 | 0.00 | 0.0 |
| 3907501005 | 17.17 | 11.16 | 17.03 | 26.78 | 1.39 | 5.39 | 1.62 | 39.31 | 0.00 | 25.9 |
| 3907501006 | 12.15 | 8.86 | 11.87 | 13.85 | 2.04 | 12.01 | 3.25 | 23.00 | 3.97 | 23.0 |
| COD SECC | 75_2001 | 75_2011 | HOG_64_2001 | HOG_64_2011 | HOG_MONO_2001 | HOG_MONO_2011 | EXT_2001 | EXT_2011 | EXT_MEN_2001 | EXT_MEN_2011 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 139 | 3907508020 | 2.58 | 1.60 | 2.17 | 5.57 | 2.99 | 10.94 | 1.87 | 9.95 | 0.90 | 27.77 |
| 140 | 3907508021 | 6.49 | 3.04 | 5.79 | 4.15 | 1.38 | 4.56 | 1.30 | 1.24 | 1.15 | 0.00 |
| 141 | 3907508022 | 1.49 | 2.83 | 0.89 | 5.48 | 2.84 | 3.91 | 2.27 | 1.50 | 1.94 | 0.00 |
| 142 | 3907508023 | 9.60 | 2.48 | 1.10 | 5.79 | 2.75 | 5.07 | 1.61 | 1.88 | 0.77 | 0.00 |
| 143 | 3907508024 | 5.21 | 5.56 | 4.49 | 0.00 | 1.22 | 5.83 | 1.16 | 0.73 | 1.40 | 0.00 |
| 144 | 3907508025 | 2.17 | 2.05 | 2.45 | 0.00 | 2.45 | 4.13 | 2.17 | 5.37 | 0.00 | 23.81 |
## [1] 144 11
## 'data.frame': 144 obs. of 11 variables:
## $ COD SECC : num 3.91e+09 3.91e+09 3.91e+09 3.91e+09 3.91e+09 ...
## $ 75_2001 : num 14.2 14.2 19.1 21.1 17.2 ...
## $ 75_2011 : num 8.33 18.45 12.8 27.78 11.16 ...
## $ HOG_64_2001 : num 14.8 11.5 12.4 15.6 17 ...
## $ HOG_64_2011 : num 12.7 14.1 22.5 28 26.8 ...
## $ HOG_MONO_2001: num 1.87 1.04 1.35 1.11 1.39 2.04 1.8 1.86 0.55 0.75 ...
## $ HOG_MONO_2011: num 3.01 0 5.32 0 5.39 ...
## $ EXT_2001 : num 5.27 1.55 1.77 2.04 1.62 3.25 3.86 3.85 2.59 5.27 ...
## $ EXT_2011 : num 30.83 14 6.37 9.36 39.31 ...
## $ EXT_MEN_2001 : num 6.93 1.98 5.06 0 0 3.97 3.03 3.03 3.85 4.35 ...
## $ EXT_MEN_2011 : num 0 0 0 0 25.9 ...
Como puede comprobarse, el dataset consta de 144 filas, que se corresponden con el total de secciones censales existentes en la ciudad de Santander, mientras que hay un total de 11 columnas correspondientes a las variables demográficas registradas. Todas las variables son de tipo numérico, informando sobre porcentajes poblacionales
A continuación, se procede a extraer y prepar los datos de 2011 para el análisis.
# Variables de 2011 seleccionadas
datos_2011 <- data.frame(
COD_SECC = datos_raw[["COD SECC"]],
P75 = datos_raw[["75_2011"]],
HOG_64 = datos_raw[["HOG_64_2011"]],
HOG_MONO = datos_raw[["HOG_MONO_2011"]],
EXT = datos_raw[["EXT_2011"]],
EXT_MEN = datos_raw[["EXT_MEN_2011"]]
)
# Matriz numérica para los análisis
X2011 <- datos_2011[, c("P75", "HOG_64", "HOG_MONO", "EXT", "EXT_MEN")]
# Comprobación de estructura
str(datos_2011)## 'data.frame': 144 obs. of 6 variables:
## $ COD_SECC: num 3.91e+09 3.91e+09 3.91e+09 3.91e+09 3.91e+09 ...
## $ P75 : num 8.33 18.45 12.8 27.78 11.16 ...
## $ HOG_64 : num 12.7 14.1 22.5 28 26.8 ...
## $ HOG_MONO: num 3.01 0 5.32 0 5.39 ...
## $ EXT : num 30.83 14 6.37 9.36 39.31 ...
## $ EXT_MEN : num 0 0 0 0 25.9 ...
## P75 HOG_64 HOG_MONO EXT
## Min. : 0.000 Min. : 0.000 Min. : 0.000 Min. : 0.000
## 1st Qu.: 6.763 1st Qu.: 6.655 1st Qu.: 2.663 1st Qu.: 1.827
## Median :12.330 Median :12.860 Median : 4.645 Median : 5.545
## Mean :12.387 Mean :13.315 Mean : 5.143 Mean : 8.550
## 3rd Qu.:16.233 3rd Qu.:18.840 3rd Qu.: 7.470 3rd Qu.:12.338
## Max. :31.930 Max. :35.020 Max. :19.010 Max. :46.010
## EXT_MEN
## Min. : 0.000
## 1st Qu.: 0.000
## Median : 0.000
## Mean : 9.128
## 3rd Qu.:18.247
## Max. :75.000
Se continúa comprobando si hay datos perdidos e identificadores de sección repetidos en el dataset.
## COD_SECC P75 HOG_64 HOG_MONO EXT EXT_MEN
## 0 0 0 0 0 0
## [1] 0
Como se desprende de los reultados, no aparecen valores perdidos y no hay códigos de sección censal duplicados. Por tanto, no es necesario imputar datos ni eliminar registros en esta fase.
El siguiente paso será realizar un análisis exploratorio de las variables del dataset.
# Estadísticos descriptivos básicos
resumen <- data.frame(
Media = sapply(X2011, mean),
Desv_tipica = sapply(X2011, sd),
Minimo = sapply(X2011, min),
Q1 = sapply(X2011, quantile, probs = 0.25),
Mediana = sapply(X2011, median),
Q3 = sapply(X2011, quantile, probs = 0.75),
Maximo = sapply(X2011, max)
)
formattable(round(resumen, 2))| Media | Desv_tipica | Minimo | Q1 | Mediana | Q3 | Maximo | |
|---|---|---|---|---|---|---|---|
| P75 | 12.39 | 7.26 | 0 | 6.76 | 12.33 | 16.23 | 31.93 |
| HOG_64 | 13.32 | 7.68 | 0 | 6.65 | 12.86 | 18.84 | 35.02 |
| HOG_MONO | 5.14 | 4.19 | 0 | 2.66 | 4.64 | 7.47 | 19.01 |
| EXT | 8.55 | 9.52 | 0 | 1.83 | 5.54 | 12.34 | 46.01 |
| EXT_MEN | 9.13 | 14.84 | 0 | 0.00 | 0.00 | 18.25 | 75.00 |
# Revisión de ceros
ceros <- colSums(X2011 == 0)
porcentaje_ceros <- round(ceros / nrow(X2011) * 100, 1)
tabla_ceros <- data.frame(
Variable = names(ceros),
Numero_ceros = as.numeric(ceros),
Porcentaje_ceros = porcentaje_ceros
)
formattable(tabla_ceros)| Variable | Numero_ceros | Porcentaje_ceros | |
|---|---|---|---|
| P75 | P75 | 1 | 0.7 |
| HOG_64 | HOG_64 | 5 | 3.5 |
| HOG_MONO | HOG_MONO | 30 | 20.8 |
| EXT | EXT | 21 | 14.6 |
| EXT_MEN | EXT_MEN | 93 | 64.6 |
# Histogramas
par(mfrow = c(2, 3))
for (v in names(X2011)) {
hist(X2011[[v]],
main = paste("Histograma de", v),
xlab = v,
col = "blue",
border = "white")
}
par(mfrow = c(1, 1))# Boxplots para detectar dispersión y posibles valores extremos
boxplot(X2011,
main = "Distribución de variables sociodemográficas 2011",
las = 2,
col = "blue")Tras la revisión específica de valores iguales a cero en las
variables seleccionadas para 2011, los resultados muestran una
presencia muy reducida de ceros en las variables de
envejecimiento, con solo un caso en P75 y cinco en
HOG_64. En cambio, las variables vinculadas a hogares
monoparentales y población extranjera presentan una mayor frecuencia de
valores nulos, especialmente EXT_MEN, con 93 secciones
censales con valor cero. Esto sugiere una distribución muy asimétrica de
la población extranjera infantil, concentrada solo en determinadas
secciones. Esta comprobación no debe confundirse con un análisis de
valores perdidos, ya que los ceros representan porcentajes nulos y
pueden ser valores perfectamente válidos desde el punto de vista
sociodemográfico. Por ello, se mantienen los ceros en el análisis, pero
se advierte que esta estructura puede influir en las correlaciones y en
la interpretación posterior del ACP y del análisis factorial.
En cuanto a la exploración gráfica, se evidencian diferencias
importantes en la forma de las distribuciones. Las variables vinculadas
al envejecimiento, P75 y HOG_64, presentan
distribuciones relativamente más equilibradas, aunque con cierta cola
hacia valores altos. Esto sugiere que la presencia de población mayor y
de hogares unipersonales de mayores de 64 años está relativamente
extendida entre las secciones censales, aunque con distinta intensidad.
Por el contrario, HOG_MONO, EXT y
especialmente EXT_MEN presentan distribuciones claramente
asimétricas hacia la derecha, con una elevada concentración de secciones
en valores bajos y algunos casos con valores elevados. Esta situación es
especialmente visible en la variable de población extranjera infantil,
donde se aprecia una fuerte acumulación de ceros y varios valores
extremos.
El gráfico de boxplot confirma esta interpretación,
mostrando mayor dispersión y presencia de valores atípicos en las
variables relacionadas con población extranjera, especialmente en
EXT_MEN. Estos valores no deben considerarse
automáticamente errores, ya que pueden reflejar la concentración
territorial de determinados perfiles sociodemográficos. No obstante,
conviene tenerlos en cuenta porque pueden influir en las correlaciones,
en el análisis de componentes principales, en el análisis factorial y en
la posterior clasificación mediante clúster.
Para finalizar con la preparación de los procesos previos a los análisis ACP y AF, se genera una matriz de correlación de las cinco variables de la que consta el dataset.
# Matriz de correlaciones
cor_2011 <- cor(X2011, use = "pairwise.complete.obs")
# Nombres más descriptivos para la tabla y el gráfico
nombres_variables <- c(
P75 = "Población ≥ 75 años",
HOG_64 = "Hogares unipersonales ≥ 64",
HOG_MONO = "Hogares monoparentales",
EXT = "Población extranjera",
EXT_MEN = "Población extranjera infantil"
)
# Aplicamos nombres descriptivos
cor_tabla <- round(cor_2011, 3)
rownames(cor_tabla) <- nombres_variables[rownames(cor_tabla)]
colnames(cor_tabla) <- nombres_variables[colnames(cor_tabla)]
# Tabla formateada
kable(
cor_tabla,
format = "html",
caption = "Matriz de correlaciones entre variables sociodemográficas. Año 2011",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed", "responsive")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
) %>%
column_spec(
1,
bold = TRUE,
width = "16em"
)| Población ≥ 75 años | Hogares unipersonales ≥ 64 | Hogares monoparentales | Población extranjera | Población extranjera infantil | |
|---|---|---|---|---|---|
| Población ≥ 75 años | 1.000 | 0.636 | -0.217 | 0.088 | -0.057 |
| Hogares unipersonales ≥ 64 | 0.636 | 1.000 | -0.154 | 0.175 | 0.051 |
| Hogares monoparentales | -0.217 | -0.154 | 1.000 | -0.063 | 0.104 |
| Población extranjera | 0.088 | 0.175 | -0.063 | 1.000 | 0.374 |
| Población extranjera infantil | -0.057 | 0.051 | 0.104 | 0.374 | 1.000 |
# Correlograma visual
corrplot(
cor_2011,
method = "color",
type = "lower",
order = "hclust",
addCoef.col = "black",
number.cex = 0.75,
tl.col = "#1F2937",
tl.srt = 35,
tl.cex = 0.85,
diag = FALSE,
col = colorRampPalette(c("#C0392B", "#F7F7F7", "#1F77B4"))(200),
mar = c(0, 0, 2, 0),
title = "Correlograma de variables sociodemográficas. Año 2011"
)La matriz de correlaciones muestra una relación positiva destacada entre la población de 75 años o más y los hogares unipersonales de mayores de 64 años. Esta asociación sugiere una dimensión común vinculada al envejecimiento demográfico y a la posible vulnerabilidad residencial. También se observa una correlación positiva moderada entre población extranjera total y población extranjera infantil. El resto de relaciones son débiles, lo que indica que las variables no son redundantes y pueden aportar información diferenciada en los análisis multivariantes posteriores.
| Bloque | Relación | Intensidad |
|---|---|---|
| Envejecimiento / hogares mayores | Población ≥ 75 años – Hogares unipersonales ≥ 64 | 0.636 |
| Extranjería / infancia extranjera | Población extranjera – Población extranjera infantil | 0.374 |
| Resto de relaciones | Valores bajos o débiles | Entre -0.217 y 0.175 |
Ejercicio 1 - Análisis de componentes principales
Dada la variabilidad detectada en la dispersión de las variables durante el análisis exploratorio, se procede a realizar una transformación para la estandarización de las variables, como paso previo al análisis de Componentes Principales (ACP). Esto evitará que las variables con mayor dispersión, como la población extranjera infantil, dominen artificialmente los análisis posteriores. En el ACP se trabajará con la matriz de correlaciones, lo que es equivalente a utilizar las variables estandarizadas.
# Datos estandarizados
X2011_z <- scale(X2011)
# Comprobación: medias cercanas a 0 y desviaciones típicas cercanas a 1
round(colMeans(X2011_z), 3)## P75 HOG_64 HOG_MONO EXT EXT_MEN
## 0 0 0 0 0
## P75 HOG_64 HOG_MONO EXT EXT_MEN
## 1 1 1 1 1
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = cor_2011)
## Overall MSA = 0.53
## MSA for each item =
## P75 HOG_64 HOG_MONO EXT EXT_MEN
## 0.52 0.53 0.67 0.53 0.48
## $chisq
## [1] 109.4898
##
## $p.value
## [1] 6.761642e-19
##
## $df
## [1] 10
Antes de aplicar el análisis de componentes principales, se comprueba la adecuación de la matriz de correlaciones mediante el índice KMO y el test de esfericidad de Bartlett. El índice KMO global obtiene un valor de 0.53, lo que indica una adecuación muestral baja, aunque ligeramente superior al umbral mínimo habitualmente aceptado de 0.50. Por tanto, los datos pueden ser utilizados para un análisis exploratorio de reducción de dimensionalidad, si bien la estructura subyacente no parece especialmente robusta.
El análisis de los MSA individuales muestra que la mayoría de las
variables se sitúan en valores próximos al mínimo aceptable,
especialmente P75, HOG_64 y EXT.
La variable HOG_MONO presenta la mejor adecuación relativa,
con un MSA de 0.67. En cambio, EXT_MEN obtiene un MSA de
0.48, por debajo del umbral de 0.50, por lo que debe considerarse una
variable menos integrada en la estructura común del conjunto.
Por su parte, el test de esfericidad de Bartlett resulta estadísticamente significativo, \(\chi^2 = 109.49\), \(p < 0.001\), por lo que se rechaza la hipótesis de que la matriz de correlaciones sea una matriz identidad. Esto confirma que existen asociaciones suficientes entre las variables para justificar la aplicación de un PCA. En conjunto, los resultados apoyan la realización del análisis de componentes principales con finalidad exploratoria, aunque aconsejan interpretar con cautela los resultados, especialmente si se pretende derivar de ellos una estructura factorial latente.
El siguiente paso será aplicar el Análisis de Componentes
Principales (ACP) sobre la matriz previamente estandarizada
X2011_z, por lo que no se vuelve a centrar ni escalar
dentro de prcomp().
## Standard deviations (1, .., p=5):
## [1] 1.3357658 1.1693186 0.9528702 0.7672378 0.5931343
##
## Rotation (n x k) = (5 x 5):
## PC1 PC2 PC3 PC4 PC5
## P75 0.63338859 -0.20120285 0.23938618 0.04749359 0.70623999
## HOG_64 0.64355196 -0.05491227 0.30395817 0.04497481 -0.69886495
## HOG_MONO -0.31249283 0.23951156 0.88641325 -0.23490293 0.06383256
## EXT 0.28127333 0.61752500 -0.25378972 -0.68703320 0.05589586
## EXT_MEN 0.08884431 0.71958262 0.01288453 0.68449077 0.07492609
## Importance of components:
## PC1 PC2 PC3 PC4 PC5
## Standard deviation 1.3358 1.1693 0.9529 0.7672 0.59313
## Proportion of Variance 0.3568 0.2735 0.1816 0.1177 0.07036
## Cumulative Proportion 0.3568 0.6303 0.8119 0.9296 1.00000
# Cálculo de autovalores a partir de las desviaciones típicas del ACP
desv_tipica <- acp_2011$sdev
autovalores <- desv_tipica^2
prop_varianza <- autovalores / sum(autovalores)
varianza_acumulada <- cumsum(prop_varianza)
# Tabla resumen
tabla_acp <- data.frame(
Componente = paste0("PC", seq_along(desv_tipica)),
`Desviación típica` = round(desv_tipica, 4),
Autovalor = round(autovalores, 4),
`Proporción de varianza` = round(prop_varianza, 4),
`Varianza acumulada` = round(varianza_acumulada, 4)
)
# Mostrar tabla formateada
kable(
tabla_acp,
caption = "Autovalores y varianza explicada por los componentes principales",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Componente | Desviación.típica | Autovalor | Proporción.de.varianza | Varianza.acumulada |
|---|---|---|---|---|
| PC1 | 1.3358 | 1.7843 | 0.3569 | 0.3569 |
| PC2 | 1.1693 | 1.3673 | 0.2735 | 0.6303 |
| PC3 | 0.9529 | 0.9080 | 0.1816 | 0.8119 |
| PC4 | 0.7672 | 0.5887 | 0.1177 | 0.9296 |
| PC5 | 0.5931 | 0.3518 | 0.0704 | 1.0000 |
# Datos para el gráfico de sedimentación
datos_scree <- data.frame(
Componente = paste0("PC", seq_along(autovalores)),
Numero = seq_along(autovalores),
Autovalor = autovalores
)
# Gráfico de sedimentación
ggplot(datos_scree, aes(x = Numero, y = Autovalor)) +
geom_col(fill = "#4E79A7", width = 0.6) +
geom_line(linewidth = 0.8, color = "#333333") +
geom_point(size = 2.8, color = "#333333") +
geom_hline(
yintercept = 1,
linetype = "dashed",
color = "#B2182B"
) +
scale_x_continuous(
breaks = datos_scree$Numero,
labels = datos_scree$Componente
) +
labs(
title = "Gráfico de sedimentación del ACP",
subtitle = "Autovalores de los componentes principales",
x = "Componentes principales",
y = "Autovalor"
) +
theme_minimal(base_size = 12)Los resultados del ACP muestran que los dos primeros componentes explican conjuntamente el 63.03 % de la varianza total.
El primer componente obtiene un autovalor de 1.7843 y explica el 35.68 % de la varianza, estando definido principalmente por las variables de población de 75 años o más y hogares unipersonales de mayores de 64 años, por lo que puede interpretarse como una dimensión de envejecimiento y vulnerabilidad residencial.
El segundo componente obtiene un autovalor de 1.3673 y explica el 27.35 % adicional, presentando cargas elevadas en población extranjera total y población extranjera infantil, por lo que puede interpretarse como una dimensión de presencia de población extranjera.
El tercer componente obtiene un autovalor de 0.9080 y explica un 18.16 % adicional de la varianza y está fuertemente asociado a los hogares monoparentales, lo que sugiere que esta variable constituye una dimensión parcialmente diferenciada. Este componente obtiene un autovalor inferior a uno, por lo que siguiendo
El resto de componentes tienen autovalores bajos y explican proporciones muy marginales de la varianza total.
Justificación de la selección de componentes
Para decidir cuántos componentes principales conservar, se han utilizado dos criterios complementarios: el criterio de los autovalores y el gráfico de sedimentación.
Dado que el ACP se ha realizado sobre variables estandarizadas, cada variable original aporta inicialmente una unidad de varianza. Por tanto, siguiendo el criterio de Kaiser, deben conservarse aquellos componentes cuyo autovalor sea superior a 1, ya que explican más varianza que una variable original individual.
De acuerdo con este criterio, se retienen los dos primeros componentes, ya que son los únicos con autovalores superiores a 1. El primer componente explica el 35.68 % de la varianza total y el segundo añade un 27.35 %, alcanzando conjuntamente una varianza acumulada del 63.03 %. Este nivel total de varianza explicada por estos dos componentes se consideraría suficiente para variables demográficas que se pueden considerar propias de ámbito de las ciencias sociales.
Por otro lado, el gráfico de sedimentación confirma esta decisión. Se observa que los dos primeros componentes concentran la mayor parte de la varianza explicada y que, a partir del tercer componente, la aportación marginal disminuye. Aunque el tercer componente todavía explica un 18.16 % de la varianza, su autovalor es inferior a 1, por lo que no se retiene en la solución principal según el criterio de Kaiser.
Por tanto, se opta por una solución de dos componentes principales, que permite reducir las cinco variables originales a dos dimensiones sintéticas, conservando una proporción razonable de la información total del dataset. Esta reducción facilita la interpretación posterior de la estructura sociodemográfica, aunque debe considerarse exploratoria debido a la adecuación muestral moderada-baja observada previamente mediante el índice KMO.
Interpretación de los coeficientes de los dos componentes seleccionados.
Atendiendo a los coeficientes de la matriz Rotation, los
dos primeros componentes son bastante interpretables y tienen una lógica
sociodemográfica clara.
Interpretación del PC1
Los coeficientes del primer componente son:
| Variable | Coeficiente PC1 |
|---|---|
| P75 | 0.633 |
| HOG_64 | 0.644 |
| HOG_MONO | -0.312 |
| EXT | 0.281 |
| EXT_MEN | 0.089 |
El PC1 está dominado por dos variables: P75 y
HOG_64. Ambas presentan coeficientes positivos elevados y
muy similares. Esto indica que el componente recoge principalmente una
dimensión asociada al envejecimiento demográfico y a la presencia de
hogares unipersonales de personas mayores.
La variable HOG_MONO aparece con coeficiente negativo
moderado. Esto sugiere que las secciones con mayor puntuación en este
componente tienden a estar más asociadas a población mayor y hogares
unipersonales de mayores, y relativamente menos a hogares
monoparentales.
Por tanto, el PC1 puede interpretarse como:
Componente de envejecimiento y vulnerabilidad residencial de personas mayores.
Una sección censal con puntuación alta en PC1 sería una sección con mayor peso relativo de población de 75 años o más y de hogares unipersonales formados por personas mayores.
Interpretación del PC2
Los coeficientes del segundo componente son:
| Variable | Coeficiente PC2 |
|---|---|
| P75 | -0.201 |
| HOG_64 | -0.055 |
| HOG_MONO | 0.240 |
| EXT | 0.618 |
| EXT_MEN | 0.720 |
El PC2 está definido principalmente por EXT y
EXT_MEN. Ambas variables tienen coeficientes positivos
elevados, especialmente EXT_MEN, que es la variable con
mayor peso en este componente.
Esto indica que el segundo componente recoge una dimensión vinculada a la presencia de población extranjera, especialmente de población extranjera infantil. La variable HOG_MONO tiene un peso positivo, pero bastante menor, por lo que puede considerarse secundaria en la interpretación.
Las variables de envejecimiento tienen coeficientes negativos o muy
bajos, especialmente HOG_64, lo que refuerza la idea de que
este segundo eje representa una dimensión distinta del
envejecimiento.
Por tanto, el PC2 puede interpretarse como:
Componente de presencia de población extranjera y población extranjera infantil.
Una sección censal con puntuación alta en PC2 sería una sección con mayor presencia relativa de población extranjera, especialmente menor de edad.
MATIZ METODOLÓGICO: el signo de los componentes en ACP es arbitrario. Lo importante no es que los coeficientes sean positivos o negativos en sí mismos, sino qué variables pesan más en cada componente y si lo hacen en el mismo sentido o en sentido opuesto.
Creación de un nuevo dataset con las nuevas variables retenidas y realizar un análisis estadístico descriptivo de las dos variables principales obtenidas.
# NUEVO DATASET CON LOS DOS COMPONENTES RETENIDOS
# Extraer puntuaciones de los dos primeros componentes
componentes_2011 <- as.data.frame(acp_2011$x[, 1:2])
# Renombrar variables para facilitar la interpretación
colnames(componentes_2011) <- c("PC1_Envejecimiento", "PC2_Extranjeria")
# Añadir identificador de sección censal
componentes_2011$COD_SECC <- datos_2011$COD_SECC
# Reordenar columnas
componentes_2011 <- componentes_2011[, c("COD_SECC",
"PC1_Envejecimiento",
"PC2_Extranjeria")]
# Mostrar primeras filas
formattable(head(componentes_2011))| COD_SECC | PC1_Envejecimiento | PC2_Extranjeria |
|---|---|---|
| 3907501001 | 0.3594631 | 0.9969772 |
| 3907501002 | 1.0858510 | -0.5565985 |
| 3907501003 | 0.6703931 | -0.6506197 |
| 3907501004 | 2.9252472 | -1.2151805 |
| 3907501005 | 2.0121944 | 2.7593977 |
| 3907501006 | -0.2647118 | 2.0955259 |
En la anterior tabla puede verse una muestra con las primeras seis secciones censales del nuevo dataset generado con las dos variables basasdas en los dos componentes principales extraídos del dataset original.
Las puntuaciones de las seis primeras secciones censales en los dos
componentes principales permiten caracterizar sus perfiles
sociodemográficos relativos. El primer componente representa
principalmente una dimensión de envejecimiento y hogares
unipersonales de personas mayores, mientras que el
segundo componente se asocia a la presencia de población
extranjera, especialmente extranjera infantil. Las secciones
3907501002, 3907501003 y especialmente
3907501004 presentan puntuaciones positivas en el primer
componente y negativas en el segundo, lo que sugiere un perfil más
envejecido y con menor presencia extranjera. Por el contrario, la
sección 3907501006 presenta baja puntuación en
envejecimiento y alta puntuación en extranjería. La sección
3907501005 destaca por obtener puntuaciones elevadas en
ambos componentes, lo que apunta a un perfil más complejo, con
coexistencia de envejecimiento y presencia extranjera. Por tanto, puede
decirse que. en conjunto, las puntuaciones de los componentes permiten
sintetizar y comparar los perfiles de las secciones censales de forma
más clara que las variables originales.
Seguidamente, se realiza un análisis descriptivo de las dos nuevas variables que componen el nuevo dataset.
Análisis descriptivo de las dos nuevas variables
# Seleccionar solo los componentes
componentes_numericos <- componentes_2011[, c("PC1_Envejecimiento",
"PC2_Extranjeria")]
# Tabla descriptiva
tabla_desc_componentes <- data.frame(
Variable = names(componentes_numericos),
Media = round(sapply(componentes_numericos, mean), 4),
Desv_tipica = round(sapply(componentes_numericos, sd), 4),
Minimo = round(sapply(componentes_numericos, min), 4),
Q1 = round(sapply(componentes_numericos, quantile, probs = 0.25), 4),
Mediana = round(sapply(componentes_numericos, median), 4),
Q3 = round(sapply(componentes_numericos, quantile, probs = 0.75), 4),
Maximo = round(sapply(componentes_numericos, max), 4)
)
# Eliminar nombres de fila para evitar que se repitan las variables
rownames(tabla_desc_componentes) <- NULL
kable(
tabla_desc_componentes,
caption = "Análisis descriptivo de los dos componentes principales retenidos",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Variable | Media | Desv_tipica | Minimo | Q1 | Mediana | Q3 | Maximo |
|---|---|---|---|---|---|---|---|
| PC1_Envejecimiento | 0 | 1.3358 | -3.0207 | -1.1532 | 0.1182 | 0.8852 | 3.4919 |
| PC2_Extranjeria | 0 | 1.1693 | -1.9530 | -0.8835 | -0.4501 | 0.8168 | 3.6795 |
# Histogramas
par(mfrow = c(2, 3))
for (v in names(componentes_numericos)) {
hist(componentes_numericos[[v]],
main = paste("Histograma de", v),
xlab = v,
col = "blue",
border = "white")
}
par(mfrow = c(1, 1))# Boxplots para detectar dispersión y posibles valores extremos
boxplot(componentes_numericos,
main = "Distribución de variables sociodemográficas 2011",
las = 2,
col = "blue")El análisis descriptivo de los dos componentes principales muestra que ambas variables tienen medias próximas a cero, como consecuencia de haberse obtenido a partir de variables estandarizadas. El primer componente, asociado al envejecimiento y a los hogares unipersonales de personas mayores, presenta una mayor dispersión que el segundo, lo que es coherente con su mayor capacidad explicativa dentro del ACP. Su distribución es relativamente equilibrada, aunque con secciones situadas en ambos extremos del eje de envejecimiento. En cambio, el segundo componente, vinculado a la presencia de población extranjera y extranjera infantil, muestra una distribución más asimétrica hacia la derecha, con muchas secciones en valores bajos o moderados y algunos casos con puntuaciones elevadas. Esto sugiere que la dimensión de extranjería está más concentrada territorialmente en un número menor de secciones censales.
En conjunto, los dos componentes permiten sintetizar dos patrones diferenciados: uno más extendido y gradual, vinculado al envejecimiento, y otro más concentrado espacialmente, vinculado a la presencia de población extranjera.
Correlación entre los dos componentes
# Matriz de correlación entre los componentes retenidos
cor_componentes <- cor(componentes_numericos)
# Redondeo de los componentes para simplificar
tabla_cor_componentes <- round(cor_componentes, 4)
# Generamos una tabla con los resultados de correlación
kable(
tabla_cor_componentes,
caption = "Matriz de correlación entre los componentes principales retenidos",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| PC1_Envejecimiento | PC2_Extranjeria | |
|---|---|---|
| PC1_Envejecimiento | 1 | 0 |
| PC2_Extranjeria | 0 | 1 |
La matriz de correlación entre los dos componentes principales retenidos muestra una correlación nula entre PC1_Envejecimiento y PC2_Extranjeria. Este resultado es coherente con la lógica del análisis de componentes principales, ya que los componentes obtenidos son ortogonales por construcción. Por tanto, las dos nuevas variables sintetizan dimensiones independientes de la variabilidad del dataset: una primera dimensión asociada al envejecimiento y los hogares unipersonales de personas mayores, y una segunda dimensión vinculada a la presencia de población extranjera y extranjera infantil.
Ejercicio 2 - Análisis factorial
El análisis paralelo se atiende también como criterio complementario al criterio de Kaiser y a los gráficos de sedimentación.
## Parallel analysis suggests that the number of factors = 2 and the number of components = 2
El gráfico de análisis paralelo confirma la conveniencia de retener dos componentes principales y dos factores. En el caso del ACP, los dos primeros componentes presentan autovalores reales superiores a los obtenidos a partir de datos simulados, mientras que desde el tercer componente los valores reales quedan por debajo de los simulados. Esto respalda la decisión de conservar dos componentes principales, en coherencia con el criterio de Kaiser y con el porcentaje de varianza explicada acumulada.
En el caso del análisis factorial, el gráfico muestra igualmente que los dos primeros factores reales superan a los factores simulados, mientras que los factores posteriores no aportan una estructura diferenciada respecto a la esperable por azar. Por tanto, el análisis paralelo apoya la solución bifactorial adoptada. Esta solución resulta además interpretable desde el punto de vista sustantivo, al distinguir un primer factor asociado al envejecimiento y los hogares unipersonales de personas mayores, y un segundo factor vinculado a la presencia de población extranjera.
En conjunto, el análisis paralelo refuerza la elección de dos dimensiones principales en ambos análisis y permite justificar que la reducción del conjunto original de variables a dos componentes o factores no responde solo a criterios descriptivos, sino también a una comparación con la estructura esperable en datos aleatorios.
Análisis Factorial Exploratorio
Se realiza un análisis factorial exploratorio como una técnica de reducción de la dimensionalidad orientada a identificar posibles factores latentes que expliquen las relaciones observadas entre las variables sociodemográficas seleccionadas para el año 2011. A diferencia del análisis de componentes principales, cuyo objetivo principal es sintetizar la varianza total de las variables originales, el análisis factorial busca explicar la varianza común compartida entre ellas.
En este ejercicio se utiliza la misma base de datos de 2011 y se
parte de la matriz de correlaciones entre las variables originales. Esta
matriz se obtiene mediante la función cor(), utilizando las
correlaciones de Pearson, que son las que R calcula por defecto.
Posteriormente, se aplica un análisis factorial exploratorio con la
librería psych, empleando el método de ejes principales
(fm = "pa"), adecuado cuando el interés se centra en
estimar factores comunes a partir de la estructura correlacional de los
datos.
Dado que el análisis previo de componentes principales sugería la existencia de dos dimensiones principales —una vinculada al envejecimiento y los hogares unipersonales de personas mayores, y otra asociada a la presencia de población extranjera—, se propone inicialmente una solución de dos factores. No obstante, teniendo en cuenta que el índice KMO obtenido previamente mostraba una adecuación moderada-baja, los resultados del análisis factorial se interpretan con finalidad exploratoria y con la debida cautela.
# Análisis factorial exploratorio con ejes principales
fa_2011 <- fa(
r = cor_2011,
nfactors = 2,
fm = "pa",
rotate = "varimax",
n.obs = nrow(X2011)
)
fa_2011## Factor Analysis using method = pa
## Call: fa(r = cor_2011, nfactors = 2, n.obs = nrow(X2011), rotate = "varimax",
## fm = "pa")
## Standardized loadings (pattern matrix) based upon correlation matrix
## PA1 PA2 h2 u2 com
## P75 0.86 0.04 0.738 0.26 1.0
## HOG_64 0.73 0.18 0.564 0.44 1.1
## HOG_MONO -0.25 0.05 0.065 0.93 1.1
## EXT 0.11 0.49 0.249 0.75 1.1
## EXT_MEN -0.12 0.79 0.634 0.37 1.0
##
## PA1 PA2
## SS loadings 1.36 0.90
## Proportion Var 0.27 0.18
## Cumulative Var 0.27 0.45
## Proportion Explained 0.60 0.40
## Cumulative Proportion 0.60 1.00
##
## Mean item complexity = 1.1
## Test of the hypothesis that 2 factors are sufficient.
##
## df null model = 10 with the objective function = 0.78 with Chi Square = 109.49
## df of the model are 1 and the objective function was 0.01
##
## The root mean square of the residuals (RMSR) is 0.02
## The df corrected root mean square of the residuals is 0.06
##
## The harmonic n.obs is 144 with the empirical chi square 0.87 with prob < 0.35
## The total n.obs was 144 with Likelihood Chi Square = 1.86 with prob < 0.17
##
## Tucker Lewis Index of factoring reliability = 0.913
## RMSEA index = 0.077 and the 90 % confidence intervals are 0 0.252
## BIC = -3.11
## Fit based upon off diagonal values = 1
## Measures of factor score adequacy
## PA1 PA2
## Correlation of (regression) scores with factors 0.90 0.82
## Multiple R square of scores with factors 0.81 0.68
## Minimum correlation of possible factor scores 0.61 0.35
##
## Loadings:
## PA1 PA2
## P75 0.858
## HOG_64 0.728
## HOG_MONO
## EXT 0.487
## EXT_MEN 0.788
##
## PA1 PA2
## SS loadings 1.355 0.895
## Proportion Var 0.271 0.179
## Cumulative Var 0.271 0.450
## PA1 PA2
## SS loadings 1.3552668 0.8953015
## Proportion Var 0.2710534 0.1790603
## Cumulative Var 0.2710534 0.4501137
## Proportion Explained 0.6021887 0.3978113
## Cumulative Proportion 0.6021887 1.0000000
El análisis factorial exploratorio mediante el método de ejes
principales y rotación varimax permite identificar una solución de dos
factores con una interpretación sustantiva clara. El primer factor,
PA1, presenta cargas elevadas en la población de 75 años o
más y en los hogares unipersonales de mayores de 64 años, por lo que
puede interpretarse como una dimensión de envejecimiento demográfico y
vulnerabilidad residencial de personas mayores. El segundo factor,
PA2, muestra cargas más elevadas en la población extranjera
infantil y, en menor medida, en la población extranjera total, por lo
que puede interpretarse como una dimensión asociada a la presencia de
población extranjera.
La solución factorial explica conjuntamente el 45,01 % de la varianza total, correspondiendo el 27,11 % al primer factor y el 17,91 % al segundo. Aunque este porcentaje es inferior al obtenido en el ACP, debe tenerse en cuenta que el análisis factorial trata de explicar la varianza común entre las variables, no la varianza total. Los indicadores de ajuste son razonables: el RMSR es bajo, el TLI se sitúa por encima de 0.90, el BIC es negativo y el contraste de suficiencia de dos factores no resulta significativo, por lo que no se rechaza la adecuación de la solución bifactorial.
No obstante, la interpretación debe realizarse con cautela. La
variable HOG_MONO presenta una comunalidad muy baja y no
carga de forma relevante en ninguno de los dos factores, lo que indica
que los hogares monoparentales no quedan bien representados por la
estructura factorial obtenida. En conjunto, el análisis factorial
permite resumir la información en dos dimensiones principales
—envejecimiento y extranjería—, aunque la solución debe entenderse como
exploratoria.
Se indica qué procedimiento se ha utilizado para obtener la matriz de correlación.
La matriz de correlación se obtuvo a partir de las variables
sociodemográficas seleccionadas para el año 2011 mediante la función
cor() de R. Concretamente, se utilizó el objeto
X2011, que contiene únicamente las variables numéricas del
análisis, excluyendo el identificador de sección censal. El
procedimiento aplicado fue:
cor_2011 <- cor(X2011, use = “pairwise.complete.obs”)
Al no especificar el argumento method, la función
cor() calcula por defecto la matriz de correlaciones de
Pearson. Por tanto, la matriz utilizada en el análisis factorial recoge
las asociaciones lineales entre las variables originales de 2011.
El argumento use = "pairwise.complete.obs" indica que,
en caso de existir valores perdidos, cada correlación se calcularía
utilizando todos los pares de observaciones disponibles para las dos
variables implicadas. No obstante, en este dataset no se detectaron
valores perdidos, por lo que la matriz se calculó sobre el conjunto
completo de secciones censales.
Esta matriz de correlaciones fue posteriormente utilizada como
entrada del análisis factorial exploratorio con la función
fa() de la librería psych, aplicando el método
de ejes principales.
Obtención de las puntuaciones factoriales
Para obtener puntuaciones factoriales necesitamos aplicar
fa() sobre la matriz de datos, no solo sobre la matriz de
correlaciones. Como ya tenemos las variables del dataset original
estandarizadas en X2011_z, usaremos este objeto como base
para esta tarea.
## [1] "P75" "HOG_64" "HOG_MONO" "EXT" "EXT_MEN"
# Análisis factorial sobre los datos estandarizados para obtener puntuaciones
fa_2011_scores <- fa(
r = X2011_z,
nfactors = 2,
fm = "pa",
rotate = "varimax",
scores = "regression"
)## maximum iteration exceeded
## [1] "PA1" "PA2"
# Crear directamente el nuevo dataset de puntuaciones
puntuaciones_factores <- data.frame(
COD_SECC = datos_2011$COD_SECC,
F1_Envejecimiento = fa_2011_scores$scores[, 1],
F2_Extranjeria = fa_2011_scores$scores[, 2]
)
head(puntuaciones_factores)## COD_SECC F1_Envejecimiento F2_Extranjeria
## 1 3907501001 -0.2127151 0.04248055
## 2 3907501002 0.7120695 -0.32247211
## 3 3907501003 0.4653397 -0.33637468
## 4 3907501004 2.0834454 -0.22251810
## 5 3907501005 0.4270473 1.67110942
## 6 3907501006 -0.4197269 0.99483423
Se realiza un análisis estadístico de las puntuaciones y se indica el método utilizado para obtener los nuevos factores.
El análisis estadístico descriptivo de estas puntuaciones permite conocer la posición relativa de las secciones censales en cada dimensión factorial. Las medias de las puntuaciones suelen situarse próximas a cero, ya que proceden de variables estandarizadas, mientras que la dispersión indica el grado de diferenciación existente entre secciones. Valores altos en el primer factor indican secciones con mayor peso relativo de población de 75 años o más y hogares unipersonales de mayores de 64 años. Valores altos en el segundo factor indican secciones con mayor presencia relativa de población extranjera, especialmente población extranjera infantil.
# Seleccionar únicamente las puntuaciones factoriales
factores_numericos <- puntuaciones_factores[
, c("F1_Envejecimiento", "F2_Extranjeria")
]
# Tabla descriptiva
tabla_desc_factores <- data.frame(
Factor = names(factores_numericos),
Media = round(sapply(factores_numericos, mean), 4),
Desv_tipica = round(sapply(factores_numericos, sd), 4),
Minimo = round(sapply(factores_numericos, min), 4),
Q1 = round(sapply(factores_numericos, quantile, probs = 0.25), 4),
Mediana = round(sapply(factores_numericos, median), 4),
Q3 = round(sapply(factores_numericos, quantile, probs = 0.75), 4),
Maximo = round(sapply(factores_numericos, max), 4)
)
rownames(tabla_desc_factores) <- NULL
kable(
tabla_desc_factores,
caption = "Análisis descriptivo de las puntuaciones factoriales por sección censal",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Factor | Media | Desv_tipica | Minimo | Q1 | Mediana | Q3 | Maximo |
|---|---|---|---|---|---|---|---|
| F1_Envejecimiento | 0 | 0.8974 | -1.6986 | -0.7147 | 0.0662 | 0.5430 | 2.5942 |
| F2_Extranjeria | 0 | 0.8220 | -0.8139 | -0.5767 | -0.4168 | 0.5414 | 2.9116 |
# Histogramas
par(mfrow = c(2, 3))
for (v in names(factores_numericos)) {
hist(factores_numericos[[v]],
main = paste("Histograma de", v),
xlab = v,
col = "blue",
border = "white")
}
par(mfrow = c(1, 1))# Boxplots para detectar dispersión y posibles valores extremos
boxplot(factores_numericos,
main = "Distribución de variables sociodemográficas 2011",
las = 2,
col = "blue")Para obtener las puntuaciones factoriales de cada sección censal se
aplicó el análisis factorial exploratorio sobre la matriz de datos
estandarizados correspondiente al año 2011. La extracción de los
factores se realizó mediante el método de ejes principales
(fm = "pa"), manteniendo una solución de dos factores con
rotación varimax. La rotación se utilizó para facilitar la
interpretación de la estructura factorial, permitiendo diferenciar con
mayor claridad una dimensión asociada al envejecimiento y otra vinculada
a la presencia de población extranjera.
Las puntuaciones factoriales se obtuvieron mediante el método de
regresión (scores = "regression"). Este procedimiento
estima, para cada sección censal, una puntuación en cada uno de los
factores retenidos. De este modo, cada sección queda caracterizada por
dos nuevas variables: una puntuación en el factor de envejecimiento y
vulnerabilidad residencial de personas mayores, y otra puntuación en el
factor de presencia de población extranjera.
En consecuencia, las puntuaciones factoriales permiten sintetizar la información original en dos dimensiones interpretables y comparar las secciones censales de Santander según su perfil sociodemográfico.
Identificación de las 10 secciones más vulnerables demográficamente de Santander.
Para identificar las secciones censales más vulnerables demográficamente se utiliza la puntuación factorial correspondiente al factor de envejecimiento. Este factor presenta cargas elevadas en la población de 75 años o más y en los hogares unipersonales de mayores de 64 años, por lo que valores altos en este factor indican secciones con mayor vulnerabilidad demográfica asociada al envejecimiento y a la posible soledad residencial de personas mayores. A partir de este criterio, se ordenaron las secciones censales de mayor a menor puntuación en F1_Envejecimiento y se seleccionaron las diez primeras.
Estas secciones no deben interpretarse como las más vulnerables en términos generales, sino específicamente como las más vulnerables desde el punto de vista demográfico asociado al envejecimiento y a los hogares unipersonales de personas mayores.
# Ordenar secciones según el factor de envejecimiento
top10_vulnerables <- puntuaciones_factores[
order(puntuaciones_factores$F1_Envejecimiento, decreasing = TRUE),
][1:10, ]
# Incorporar columna con el puesto en la clasificación
top10_vulnerables <- data.frame(
Puesto = 1:nrow(top10_vulnerables),
top10_vulnerables
)
# Eliminar nombres de fila
rownames(top10_vulnerables) <- NULL
# Mostrar tabla
kable(
top10_vulnerables,
caption = "Diez secciones censales con mayor vulnerabilidad demográfica considerada como mayor puntuación en F1_Envejecimiento",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Puesto | COD_SECC | F1_Envejecimiento | F2_Extranjeria |
|---|---|---|---|
| 1 | 3907506010 | 2.594247 | -0.3193468 |
| 2 | 3907506011 | 2.159575 | 0.9529416 |
| 3 | 3907502009 | 2.102381 | 1.0565200 |
| 4 | 3907501004 | 2.083445 | -0.2225181 |
| 5 | 3907505001 | 1.995947 | -0.3164033 |
| 6 | 3907502015 | 1.904030 | -0.1742827 |
| 7 | 3907502012 | 1.749885 | -0.4649740 |
| 8 | 3907502026 | 1.737979 | 0.5081624 |
| 9 | 3907507003 | 1.678074 | -0.3263296 |
| 10 | 3907502013 | 1.558041 | -0.6638004 |
Los resultados muestran que la sección censal 3907506010
ocupa el primer lugar, con una puntuación de 2.59 en
F1_Envejecimiento, seguida de las secciones
3907506011´, ´3907502009 y 3907501004, todas
ellas con puntuaciones superiores a 2. Estos valores indican que dichas
secciones se sitúan claramente por encima de la media del conjunto de
Santander en la dimensión de envejecimiento, por lo que pueden
considerarse las áreas con mayor vulnerabilidad demográfica desde este
criterio.
La segunda puntuación factorial, F2_Extranjeria,
permite matizar el perfil de estas secciones. Algunas de ellas, como
3907506010, 3907501004,
3907505001, 3907502015,
3907502012, 3907507003 y
3907502013, presentan valores negativos en el factor de
extranjería, lo que sugiere que su vulnerabilidad demográfica se asocia
principalmente al envejecimiento y no a una presencia elevada de
población extranjera. En contrapunto a las anteriores, las secciones
3907506011, 3907502009 y
3907502026 combinan puntuaciones altas en envejecimiento
con valores positivos en extranjería, por lo que presentan un perfil
sociodemográfico más mixto.
Se vuelve a recordar que, en conjunto, estas diez secciones no deben interpretarse como las más vulnerables en términos generales, sino específicamente como las más vulnerables desde la perspectiva del envejecimiento demográfico y de la posible soledad residencial de personas mayores. El ranking obtenido permitiría priorizar territorialmente aquellas secciones en las que el peso relativo de la población mayor y de los hogares unipersonales de mayores es más elevado.
Ejercicio 3 - Análisis cluster
Justificación del método de agrupamiento
Se utiliza un análisis de clúster jerárquico aglomerativo a partir de las puntuaciones factoriales obtenidas en el análisis factorial exploratorio. La distancia utilizada es la distancia Manhattan, tal como solicita el enunciado. Para seleccionar el método de agrupamiento más adecuado se comparan distintos métodos de enlace —simple, completo, promedio y McQuitty— mediante la correlación cofenética. Este criterio permite evaluar en qué medida el dendrograma conserva las distancias originales entre observaciones. Se seleccionará el método con mayor correlación cofenética, al ser el que reproduce con mayor fidelidad la estructura de distancias del conjunto de secciones censales.
No se utiliza el método de Ward porque está más directamente vinculado a la distancia euclídea y a la minimización de la varianza interna de los grupos, mientras que, en este ejercicio, el enunciado impone el uso de distancia Manhattan.
# 1. DATOS DE PARTIDA: PUNTUACIONES FACTORIALES
# Seleccionamos las puntuaciones factoriales obtenidas en el ejercicio 2
datos_cluster <- puntuaciones_factores[, c("F1_Envejecimiento", "F2_Extranjeria")]
# Estandarizamos las puntuaciones para que ambos factores tengan el mismo peso en la distancia
datos_cluster_z <- scale(datos_cluster)
# Añadimos los códigos de sección censal como nombres de fila
rownames(datos_cluster_z) <- puntuaciones_factores$COD_SECC
# 2. MATRIZ DE DISTANCIAS: MANHATTAN
dist_manhattan <- dist(datos_cluster_z, method = "manhattan")
# 3. COMPARACIÓN DE MÉTODOS DE AGRUPAMIENTO
metodos <- c("single", "complete", "average", "mcquitty")
modelos_hc <- lapply(
metodos,
function(m) hclust(dist_manhattan, method = m)
)
names(modelos_hc) <- metodos
# Correlación cofenética para comparar métodos
cor_cophenetica <- sapply(
modelos_hc,
function(modelo) cor(dist_manhattan, cophenetic(modelo))
)
tabla_metodos <- data.frame(
Metodo = names(cor_cophenetica),
Correlacion_cofenetica = round(cor_cophenetica, 4)
)
kable(
tabla_metodos,
caption = "Comparación de métodos de agrupamiento mediante correlación cofenética",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Metodo | Correlacion_cofenetica |
|---|---|
| single | 0.7278 |
| complete | 0.6936 |
| average | 0.7752 |
| mcquitty | 0.7034 |
# Seleccionamos automáticamente el método con mayor correlación cofenética
metodo_elegido <- names(which.max(cor_cophenetica))
metodo_elegido## [1] "average"
Cómo puede observarse, el resultado del análisis de las correlaciones cofenéticas de los cuatro métodos de enlace testeados da como mejor opción el método del promedio o average, puesto que da el indice más elevado de las cuatro opciones, lo que significa que dicho método garantiza que el dendograma reproduzca con la mayor fidelidad la estructura de distancias del conjunto de secciones censales.
Selección del número de clústeres
Se utiliza el método de la silueta media (o average silhouette method) para seleccionar el número de clústeres que represente mejor la calidad de las diferentes posibilidades de agrupamiento y ayude a identificar el número óptimo de clústers para un conjunto de datos.
# 4. Selección del número de clústeres mediante silueta media
ks <- 2:8
silueta_media <- sapply(
ks,
function(k) {
grupos <- cutree(cluster_hc, k = k)
mean(silhouette(grupos, dist_manhattan)[, 3])
}
)
tabla_silueta <- data.frame(
Numero_clusters = ks,
Silueta_media = round(silueta_media, 4)
)
kable(
tabla_silueta,
caption = "Silueta media según el número de clústeres",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Numero_clusters | Silueta_media |
|---|---|
| 2 | 0.5668 |
| 3 | 0.3355 |
| 4 | 0.4591 |
| 5 | 0.4185 |
| 6 | 0.4154 |
| 7 | 0.4037 |
| 8 | 0.5244 |
# Número óptimo de clústeres según la mayor silueta media
k_optimo <- ks[which.max(silueta_media)]
k_optimo## [1] 2
# Gráfico sencillo de silueta media
ggplot(tabla_silueta, aes(x = Numero_clusters, y = Silueta_media)) +
geom_line(color = "#333333", linewidth = 0.8) +
geom_point(color = "#1F4E79", size = 3) +
scale_x_continuous(breaks = ks) +
labs(
title = "Selección del número de clústeres",
subtitle = "Criterio: mayor silueta media",
x = "Número de clústeres",
y = "Silueta media"
) +
theme_minimal(base_size = 12)
La selección inicial de una solución de dos clústeres se justifica a
partir del criterio de silueta media, ya que el valor
máximo se alcanza para \(k = 2\). Esto
indica que, desde el punto de vista de la separación interna de los
grupos, la partición en dos conglomerados es la que ofrece mejor
equilibrio entre cohesión dentro de los clústeres y diferenciación entre
ellos.
Dendrograma con el número de clústeres seleccionado
plot(
cluster_hc,
labels = FALSE,
hang = -1,
main = paste("Dendrograma de secciones censales - método", metodo_elegido),
xlab = "Secciones censales",
sub = ""
)
rect.hclust(
cluster_hc,
k = k_optimo,
border = 2:(k_optimo + 1)
)El dendrograma obtenido mediante distancia Manhattan y método de enlace average refuerza la primera lectura recibida de la interpretación de la silueta media, ya que muestra una separación temprana entre un grupo muy reducido (sección única) y el conjunto principal de secciones censales. El corte en dos grupos permite, por tanto, identificar una estructura básica de diferenciación: por un lado, la mayoría de secciones con perfiles relativamente próximos entre sí y, por otro, una sección o grupo muy singular que se separa claramente del resto.
No obstante, esta solución debe interpretarse con cautela, ya que la partición en dos clústeres parece estar muy influida por la presencia de un caso extremo o perfil atípico. Por ello, aunque \(k = 2\) constituye la primera opción desde el punto de vista del índice de silueta, más adelante convendría valorar soluciones alternativas con mayor número de clústeres si el objetivo es obtener una segmentación territorial más rica e interpretable.
Inclusión en la base de datos de resultados del cluster al que pertenece cada sección censal.
# 5. ASIGNACIÓN DE CLUSTER A CADA SECCIÓN CENSAL
grupo_cluster <- cutree(cluster_hc, k = k_optimo)
resultados_cluster <- data.frame(
COD_SECC = puntuaciones_factores$COD_SECC,
F1_Envejecimiento = puntuaciones_factores$F1_Envejecimiento,
F2_Extranjeria = puntuaciones_factores$F2_Extranjeria,
Cluster = factor(grupo_cluster)
)
# Ordenar por clúster y sección censal
resultados_cluster <- resultados_cluster[
order(resultados_cluster$Cluster, resultados_cluster$COD_SECC),
]
rownames(resultados_cluster) <- NULL
# Generar tabla con los 20 primeros distritos mostrando el clúster al que se le ha asignado
kable(
head(resultados_cluster, 20),
caption = "Primeras secciones censales con clúster asignado",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| COD_SECC | F1_Envejecimiento | F2_Extranjeria | Cluster |
|---|---|---|---|
| 3907501001 | -0.2127151 | 0.0424805 | 1 |
| 3907501002 | 0.7120695 | -0.3224721 | 1 |
| 3907501003 | 0.4653397 | -0.3363747 | 1 |
| 3907501004 | 2.0834454 | -0.2225181 | 1 |
| 3907501005 | 0.4270473 | 1.6711094 | 1 |
| 3907501006 | -0.4197269 | 0.9948342 | 1 |
| 3907501007 | 0.3397056 | -0.5631617 | 1 |
| 3907501008 | 0.3504110 | 0.5325480 | 1 |
| 3907501009 | 0.5025992 | 1.3618291 | 1 |
| 3907501010 | 0.0956864 | 2.4225122 | 1 |
| 3907501011 | 0.6822947 | -0.4145090 | 1 |
| 3907502001 | 0.1324954 | -0.6135221 | 1 |
| 3907502002 | 0.7362813 | 0.2552141 | 1 |
| 3907502003 | 0.5684337 | -0.7065813 | 1 |
| 3907502004 | 0.0335640 | 1.4586857 | 1 |
| 3907502005 | 0.4110843 | -0.4412259 | 1 |
| 3907502006 | -0.2603096 | 1.7597144 | 1 |
| 3907502007 | 0.3000120 | 0.5832659 | 1 |
| 3907502008 | 0.6783090 | 1.7870969 | 1 |
| 3907502009 | 2.1023808 | 1.0565200 | 1 |
set.seed(123) # Opcional: permite reproducir siempre la misma muestra aleatoria
kable(
resultados_cluster[sample(nrow(resultados_cluster), 20), ],
caption = "Veinte secciones censales seleccionadas aleatoriamente con clúster asignado",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| COD_SECC | F1_Envejecimiento | F2_Extranjeria | Cluster |
|---|---|---|---|
| 3907502003 | 0.5684337 | -0.7065813 | 1 |
| 3907503012 | 0.0563326 | -0.5878335 | 1 |
| 3907507023 | -1.5689434 | 0.6974677 | 1 |
| 3907503005 | -1.0135151 | -0.3788434 | 1 |
| 3907507010 | -1.6986427 | 2.9115508 | 2 |
| 3907508024 | -1.1151951 | -0.7957002 | 1 |
| 3907506008 | -0.8350645 | 2.1697439 | 1 |
| 3907506009 | 0.1547858 | 0.4986670 | 1 |
| 3907508019 | -1.0469771 | -0.6548585 | 1 |
| 3907506010 | 2.5942471 | -0.3193468 | 1 |
| 3907507003 | 1.6780744 | -0.3263296 | 1 |
| 3907505005 | 0.2932821 | -0.4538180 | 1 |
| 3907502015 | 1.9040301 | -0.1742827 | 1 |
| 3907501007 | 0.3397056 | -0.5631617 | 1 |
| 3907505011 | 0.1391006 | 1.0036618 | 1 |
| 3907505014 | 0.6916878 | 0.3694950 | 1 |
| 3907508023 | -1.1362141 | -0.6754554 | 1 |
| 3907507007 | -0.6915658 | -0.6254734 | 1 |
| 3907507022 | -1.2437003 | -0.6945225 | 1 |
| 3907505009 | -0.3163578 | -0.7189534 | 1 |
Perfil medio de cada clúster
Para ayudar a una mejor interpretación los grupos generados al clusterizar, se analiza el perfil medio de las secciones que componen cada uno de los dos clústers en los que se ha particionado inicialmente el total de secciones censales.
# Número de secciones por clúster
tabla_tamano_cluster <- as.data.frame(table(resultados_cluster$Cluster))
colnames(tabla_tamano_cluster) <- c("Cluster", "Numero_secciones")
# Perfil medio de los clústeres
perfil_cluster <- aggregate(
resultados_cluster[, c("F1_Envejecimiento", "F2_Extranjeria")],
by = list(Cluster = resultados_cluster$Cluster),
FUN = mean
)
perfil_cluster <- merge(
perfil_cluster,
tabla_tamano_cluster,
by = "Cluster"
)
kable(
perfil_cluster,
caption = "Perfil medio de los clústeres según las puntuaciones factoriales",
align = "c",
row.names = FALSE,
digits = 3
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Cluster | F1_Envejecimiento | F2_Extranjeria | Numero_secciones |
|---|---|---|---|
| 1 | 0.012 | -0.020 | 143 |
| 2 | -1.699 | 2.912 | 1 |
La solución de dos clústeres muestra una partición muy
desequilibrada. El clúster 1 agrupa 143 secciones censales y presenta
valores medios prácticamente nulos en ambos factores
(F1_Envejecimiento = 0.012;
F2_Extranjeria = -0.020), por lo que representa el perfil
general o mayoritario del municipio. En cambio, el clúster 2 está
formado por una única sección censal, con bajo envejecimiento relativo
(F1 = -1.699) y una puntuación muy elevada en extranjería
(F2 = 2.912).
Por tanto, aunque la solución de dos grupos es la mejor según el criterio de silueta media, su interpretación debe realizarse con cautela, ya que no segmenta el territorio en dos perfiles amplios, sino que identifica una sección claramente atípica frente al resto. Esta solución es útil para detectar un caso singular de alta extranjería y bajo envejecimiento, pero puede resultar limitada si el objetivo del análisis es obtener una tipología territorial más rica y equilibrada.
En resumen, parece que situar el agrupamiento en \(k = 2\) maximiza la silueta porque detecta muy bien un outlier territorial, no porque existan dos grandes grupos homogéneos y comparables.
Gráfico de clústeres sobre los dos factores
ggplot(
resultados_cluster,
aes(
x = F1_Envejecimiento,
y = F2_Extranjeria,
color = Cluster
)
) +
geom_point(size = 2.5, alpha = 0.85) +
geom_vline(xintercept = 0, linetype = "dashed", color = "gray60") +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray60") +
labs(
title = "Clústeres de secciones censales de Santander",
subtitle = "Agrupación sobre puntuaciones factoriales. Distancia Manhattan",
x = "Factor 1: Envejecimiento",
y = "Factor 2: Extranjería",
color = "Clúster"
) +
theme_minimal(base_size = 12)
Tras los análisis realizados en base a una solución inicial de
dos clústeres, se observa que dicha clusterización genera una
partición muy desequilibrada, con un primer grupo formado por 143
secciones censales y un segundo grupo compuesto por una única sección.
Aunque esta solución puede ser estadísticamente posible, su
interpretación debe realizarse con cautela, ya que no refleja
propiamente una tipología territorial de dos grupos, sino la separación
de una sección censal con un perfil claramente atípico.
El segundo clúster presenta una puntuación negativa en el factor de envejecimiento y una puntuación muy elevada en el factor de extranjería, lo que sugiere que se trata de una sección singular, caracterizada por baja vulnerabilidad asociada al envejecimiento y alta presencia relativa de población extranjera. Por tanto, más que un grupo territorial amplio, este clúster puede interpretarse como un caso extremo.
En consecuencia, para seleccionar el número final de clústeres no debería atenderse únicamente a la silueta media, sino que también debe dársele importancia a la interpretabilidad de la solución y al tamaño de los grupos resultantes. Si una solución alternativa con tres o cuatro clústeres ofrece grupos más equilibrados y perfiles sociodemográficos diferenciados, resultaría preferible para construir una clasificación útil de las secciones censales de Santander.
Clusterización alternativa
Para realizar una exploración de la mejor opción respecto al número de grupos, se analizan soluciones alternativas entre 3 y 8 clústeres, considerando no solo la silueta media, sino también el tamaño de los grupos, la estructura observada en el dendrograma y la interpretabilidad de los perfiles resultantes.
# Comparación de soluciones entre 2 y 8 clústeres
ks <- 2:8
diagnostico_clusters <- data.frame(
Numero_clusters = integer(),
Silueta_media = numeric(),
Minimo_secciones_cluster = integer(),
Maximo_secciones_cluster = integer()
)
for (k in ks) {
grupos_k <- cutree(cluster_hc, k = k)
sil_k <- silhouette(grupos_k, dist_manhattan)
tam_k <- table(grupos_k)
diagnostico_clusters <- rbind(
diagnostico_clusters,
data.frame(
Numero_clusters = k,
Silueta_media = round(mean(sil_k[, 3]), 4),
Minimo_secciones_cluster = min(tam_k),
Maximo_secciones_cluster = max(tam_k)
)
)
}
kable(
diagnostico_clusters,
caption = "Diagnóstico de soluciones alternativas de clústeres",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Numero_clusters | Silueta_media | Minimo_secciones_cluster | Maximo_secciones_cluster |
|---|---|---|---|
| 2 | 0.5668 | 1 | 143 |
| 3 | 0.3355 | 1 | 131 |
| 4 | 0.4591 | 1 | 84 |
| 5 | 0.4185 | 1 | 84 |
| 6 | 0.4154 | 1 | 84 |
| 7 | 0.4037 | 1 | 84 |
| 8 | 0.5244 | 1 | 44 |
Solución alternativa propuesta: 4 clústeres
La solución de 4 clústeres se considera una alternativa más útil, al permitir diferenciar perfiles sociodemográficos más matizados sin reducir el análisis a la separación de un único caso extremo.
Esta solución mantiene la distancia Manhattan y el método jerárquico
aglomerativo con enlace promedio (average). El método
average resulta adecuado porque ofrece una agrupación menos
sensible a casos extremos que el enlace simple y más equilibrada que
otros métodos, conservando una estructura interpretable de distancias
entre secciones.
En consecuencia, se adopta una solución alternativa de cuatro clústeres, no porque maximice estrictamente la silueta media, sino porque ofrece una segmentación territorial más informativa y operativamente útil de las secciones censales de Santander.
# SELECCIÓN ALTERNATIVA DE 4 CLÚSTERES
k_alt <- 4
grupo_cluster_alt <- cutree(cluster_hc, k = k_alt)
# Base de resultados con clúster asignado
resultados_cluster_alt <- data.frame(
COD_SECC = puntuaciones_factores$COD_SECC,
F1_Envejecimiento = puntuaciones_factores$F1_Envejecimiento,
F2_Extranjeria = puntuaciones_factores$F2_Extranjeria,
Cluster = factor(grupo_cluster_alt)
)
# Ordenar por clúster y sección
resultados_cluster_alt <- resultados_cluster_alt[
order(resultados_cluster_alt$Cluster, resultados_cluster_alt$COD_SECC),
]
rownames(resultados_cluster_alt) <- NULL
kable(
resultados_cluster_alt[sample(nrow(resultados_cluster_alt), 20), ],
caption = "Primeras secciones censales con clúster asignado. Solución alternativa de 4 clústeres",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| COD_SECC | F1_Envejecimiento | F2_Extranjeria | Cluster |
|---|---|---|---|
| 3907507003 | 1.6780744 | -0.3263296 | 3 |
| 3907506006 | 0.0689589 | 0.4946518 | 1 |
| 3907507019 | 1.0430058 | -0.3055600 | 2 |
| 3907502002 | 0.7362813 | 0.2552141 | 1 |
| 3907502026 | 1.7379789 | 0.5081624 | 3 |
| 3907503014 | -0.4330144 | -0.4706365 | 2 |
| 3907505006 | -0.6661586 | 0.9425082 | 1 |
| 3907502003 | 0.5684337 | -0.7065813 | 2 |
| 3907502013 | 1.5580412 | -0.6638004 | 3 |
| 3907505013 | 1.5275409 | -0.3790136 | 3 |
| 3907506009 | 0.1547858 | 0.4986670 | 1 |
| 3907503006 | 0.1195073 | -0.6265003 | 2 |
| 3907503012 | 0.0563326 | -0.5878335 | 2 |
| 3907504001 | 0.2256610 | -0.5565671 | 2 |
| 3907502023 | -1.0568410 | -0.6041982 | 2 |
| 3907506013 | 0.0634585 | -0.3685095 | 2 |
| 3907508011 | -0.3501716 | -0.7557587 | 2 |
| 3907505008 | -1.5447086 | -0.7020211 | 2 |
| 3907505011 | 0.1391006 | 1.0036618 | 1 |
| 3907507013 | 0.3249312 | 0.9343292 | 1 |
# PERFIL MEDIO DE CADA UNO DE LOS 4 CLÚSTERES
# Tamaño de cada clúster
tamano_cluster_alt <- as.data.frame(table(resultados_cluster_alt$Cluster))
colnames(tamano_cluster_alt) <- c("Cluster", "Numero_secciones")
# Perfil medio de cada clúster
perfil_cluster_alt <- aggregate(
resultados_cluster_alt[, c("F1_Envejecimiento", "F2_Extranjeria")],
by = list(Cluster = resultados_cluster_alt$Cluster),
FUN = mean
)
perfil_cluster_alt <- merge(
perfil_cluster_alt,
tamano_cluster_alt,
by = "Cluster"
)
kable(
perfil_cluster_alt,
caption = "Perfil medio de los clústeres según las puntuaciones factoriales. Solución de 4 clústeres",
align = "c",
row.names = FALSE,
digits = 3
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Cluster | F1_Envejecimiento | F2_Extranjeria | Numero_secciones |
|---|---|---|---|
| 1 | -0.136 | 0.951 | 47 |
| 2 | -0.172 | -0.559 | 84 |
| 3 | 1.883 | -0.055 | 12 |
| 4 | -1.699 | 2.912 | 1 |
| Clúster | Nº secciones | Perfil medio | Interpretación |
|---|---|---|---|
| 1 | 47 | F1 bajo-medio, F2 alto | Secciones con mayor presencia de población extranjera, pero sin especial envejecimiento |
| 2 | 84 | F1 bajo-medio, F2 bajo | Perfil mayoritario, con menor presencia extranjera y envejecimiento no elevado |
| 3 | 12 | F1 alto, F2 cercano a cero | Secciones más envejecidas, con vulnerabilidad demográfica asociada a mayores |
| 4 | 1 | F1 muy bajo, F2 muy alto | Caso singular o atípico, con muy alta extranjería y bajo envejecimiento |
Dendrograma con 4 clústeres
plot(
cluster_hc,
labels = FALSE,
hang = -1,
main = "Dendrograma de secciones censales - método average",
xlab = "Secciones censales",
sub = ""
)
rect.hclust(
cluster_hc,
k = k_alt,
border = 2:(k_alt + 1)
)Gráfico de los clústeres sobre los factores
ggplot(
resultados_cluster_alt,
aes(
x = F1_Envejecimiento,
y = F2_Extranjeria,
color = Cluster
)
) +
geom_point(size = 2.5, alpha = 0.85) +
geom_vline(xintercept = 0, linetype = "dashed", color = "gray60") +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray60") +
labs(
title = "Clústeres de secciones censales de Santander",
subtitle = "Solución alternativa de 4 clústeres. Distancia Manhattan y método average",
x = "Factor 1: Envejecimiento",
y = "Factor 2: Extranjería",
color = "Clúster"
) +
theme_minimal(base_size = 12)La solución alternativa de cuatro clústeres permite obtener una segmentación más informativa de las secciones censales de Santander que la solución inicial de dos grupos. Aunque la solución de dos clústeres presentaba una silueta media elevada, generaba una partición muy desequilibrada, separando una única sección censal del resto. Por ello, se optó por explorar una solución de cuatro clústeres, más útil desde el punto de vista interpretativo.
El clúster 2 agrupa la mayoría de las secciones censales y representa un perfil sociodemográfico medio, con puntuaciones bajas en extranjería y sin un envejecimiento especialmente elevado. El clúster 1 reúne secciones con mayor presencia relativa de población extranjera, pero sin destacar por envejecimiento. El clúster 3 identifica las secciones con mayor vulnerabilidad demográfica asociada al envejecimiento, al presentar una media claramente elevada en el factor de envejecimiento. Finalmente, el clúster 4 está formado por una única sección, caracterizada por una puntuación muy alta en extranjería y baja en envejecimiento, por lo que debe interpretarse como un caso singular o atípico.
En conjunto, esta clasificación permite diferenciar cuatro perfiles territoriales: secciones con mayor extranjería, secciones de perfil medio-bajo en ambas dimensiones, secciones envejecidas y una sección atípica con alta extranjería. Esta solución resulta más adecuada para segmentar las secciones censales de Santander, ya que combina criterios estadísticos con interpretabilidad sustantiva.
Validación interna de las solución de 4 grupos
Además de la silueta media, se utilizan otros índices de validación interna para evaluar la calidad de la solución de clústeres. Para ello se calculan índice de Dunn, que mide la calidad de una partición en clusters comparando dos magnitudes: la separación entre clusters (distancia mínima entre puntos de clusters distintos) y la compacidad interna (diámetro máximo, es decir, la mayor distancia entre dos puntos dentro del mismo cluster). Valores altos indican clusters bien separados y compactos, lo que se considera una buena solución. También se mide la versión robusta de este idicador (Dunn2), que sustituye los operadores sensibles a outliers por promedios: en lugar de tomar la distancia mínima entre clusters usa la distancia media entre puntos de clusters distintos, y en lugar del diámetro máximo usa la distancia intra-cluster promedio.Al igual que Dunn, valores más altos indican una mejor solución: clusters compactos y bien separados.
También se calcula el índice Pearson gamma, el cual se usa para medir la calidad de una clusterización jerárquica comparando cuántas relaciones entre pares de elementos quedan mejor explicadas por la partición en clústeres que por una estructura aleatoria. En esencia, evalúa si los grupos formados son más coherentes internamente y más separados entre sí.
| Índice | Qué mide | Criterio |
|---|---|---|
| Silueta media | Cohesión interna y separación entre grupos | Más alto es mejor |
| Dunn | Separación mínima entre clústeres frente a diámetro interno | Más alto es mejor |
| Dunn2 | Variante menos estricta del índice de Dunn | Más alto es mejor |
| Pearson gamma | Asociación entre distancias y pertenencia a grupos | Más alto es mejor |
# Estadísticos de validación interna
validacion_cluster <- cluster.stats(
d = dist_manhattan,
clustering = grupo_cluster_alt
)
# Tabla comparativa de estadísticos
ks <- 2:8
tabla_validacion <- data.frame(
Numero_clusters = integer(),
Silueta_media = numeric(),
Dunn = numeric(),
Dunn2 = numeric(),
Pearson_gamma = numeric(),
Minimo_secciones_cluster = integer(),
Maximo_secciones_cluster = integer()
)
for (k in ks) {
grupos_k <- cutree(cluster_hc, k = k)
stats_k <- cluster.stats(
d = dist_manhattan,
clustering = grupos_k
)
tam_k <- table(grupos_k)
tabla_validacion <- rbind(
tabla_validacion,
data.frame(
Numero_clusters = k,
Silueta_media = round(stats_k$avg.silwidth, 4),
Dunn = round(stats_k$dunn, 4),
Dunn2 = round(stats_k$dunn2, 4),
Pearson_gamma = round(stats_k$pearsongamma, 4),
Minimo_secciones_cluster = min(tam_k),
Maximo_secciones_cluster = max(tam_k)
)
)
}
kable(
tabla_validacion,
caption = "Comparación de soluciones de clústeres mediante índices de validación interna",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Numero_clusters | Silueta_media | Dunn | Dunn2 | Pearson_gamma | Minimo_secciones_cluster | Maximo_secciones_cluster |
|---|---|---|---|---|---|---|
| 2 | 0.5668 | 0.2722 | 2.5828 | 0.3072 | 1 | 143 |
| 3 | 0.3355 | 0.0711 | 1.6384 | 0.4177 | 1 | 131 |
| 4 | 0.4591 | 0.0904 | 1.6835 | 0.6578 | 1 | 84 |
| 5 | 0.4185 | 0.0966 | 1.5019 | 0.6593 | 1 | 84 |
| 6 | 0.4154 | 0.0895 | 1.6344 | 0.6534 | 1 | 84 |
| 7 | 0.4037 | 0.0895 | 1.6439 | 0.6529 | 1 | 84 |
| 8 | 0.5244 | 0.0925 | 1.5401 | 0.6112 | 1 | 44 |
La comparación de soluciones de clústeres muestra que la partición en dos grupos obtiene la mayor silueta media y los valores más elevados en los índices de Dunn. Sin embargo, esta solución genera una distribución extremadamente desequilibrada, con un clúster formado por 143 secciones censales y otro compuesto por una única sección. Por tanto, aunque estadísticamente detecta una separación clara, su utilidad para segmentar territorialmente Santander es limitada, ya que refleja principalmente la presencia de un caso atípico.
Por este motivo, la elección final del número de clústeres no se basa únicamente en la maximización de la silueta media, sino en una valoración conjunta de distintos criterios: calidad interna del agrupamiento, tamaño de los grupos, estructura del dendrograma e interpretabilidad sustantiva. La solución de cuatro clústeres presenta una silueta media aceptable y un valor elevado de Pearson_gamma, muy próximo al máximo observado. Además, permite diferenciar perfiles sociodemográficos más útiles: secciones con mayor presencia extranjera, secciones de perfil medio, secciones envejecidas y una sección singular o atípica.
En consecuencia, se selecciona la solución de cuatro clústeres como alternativa más adecuada para la segmentación de las secciones censales de Santander. Esta elección sacrifica parcialmente la maximización estricta de algunos índices, pero mejora la utilidad interpretativa y territorial del análisis.
Ejercicio 4 - Multidimensional Scaling
Para este ejercicio cargamos el conjunto de datos “Produc”, que proviene de la librería ““plm” y contiene información económica de distintos estados de Estados Unidos. Trabajaremos con los datos del año 1986 y con las siguientes variables:
- pcap: stock de capital público
- hwy: infraestructuras viarias
- water: infraestructuras de agua y saneamiento
- util: otras infraestructuras públicas
- pc: stock de capital privado
- gsp: producto bruto estatal
- emp: empleo no agrícola
- unemp: tasa de desempleo
## state year region pcap hwy water util pc gsp emp
## 1 ALABAMA 1970 6 15032.67 7325.80 1655.68 6051.20 35793.80 28418 1010.5
## 2 ALABAMA 1971 6 15501.94 7525.94 1721.02 6254.98 37299.91 29375 1021.9
## 3 ALABAMA 1972 6 15972.41 7765.42 1764.75 6442.23 38670.30 31303 1072.3
## 4 ALABAMA 1973 6 16406.26 7907.66 1742.41 6756.19 40084.01 33430 1135.5
## 5 ALABAMA 1974 6 16762.67 8025.52 1734.85 7002.29 42057.31 33749 1169.8
## 6 ALABAMA 1975 6 17316.26 8158.23 1752.27 7405.76 43971.71 33604 1155.4
## unemp
## 1 4.7
## 2 5.2
## 3 4.7
## 4 3.9
## 5 5.5
## 6 7.7
Preparación de datos:
# Seleccionar solo el año 1986 y las variables solicitadas
Produc_1986 <- subset(
Produc,
year == 1986,
select = c(
state,
year,
pcap,
hwy,
water,
util,
pc,
gsp,
emp,
unemp
)
)
# Revisar el resultado
head(Produc_1986)## state year pcap hwy water util pc gsp
## 17 ALABAMA 1986 19723.37 8813.24 2308.99 8601.14 61628.88 48409
## 34 ARIZONA 1986 20212.42 6553.40 2923.15 10735.88 44189.19 46058
## 51 ARKANSAS 1986 9787.20 4652.71 960.66 4173.84 32889.30 28168
## 68 CALIFORNIA 1986 139042.68 43350.54 24592.33 71099.80 363779.70 464550
## 85 COLORADO 1986 18889.54 6148.80 4340.24 8400.50 47485.05 51781
## 102 CONNECTICUT 1986 17290.61 6878.14 3314.55 7097.92 40413.22 61750
## emp unemp
## 17 1463.3 9.8
## 34 1337.8 6.9
## 51 813.8 8.7
## 68 11258.0 6.7
## 85 1408.3 7.4
## 102 1604.2 3.8
## 'data.frame': 48 obs. of 10 variables:
## $ state: Factor w/ 48 levels "ALABAMA","ARIZONA",..: 1 2 3 4 5 6 7 8 9 10 ...
## $ year : int 1986 1986 1986 1986 1986 1986 1986 1986 1986 1986 ...
## $ pcap : num 19723 20212 9787 139043 18890 ...
## $ hwy : num 8813 6553 4653 43351 6149 ...
## $ water: num 2309 2923 961 24592 4340 ...
## $ util : num 8601 10736 4174 71100 8400 ...
## $ pc : num 61629 44189 32889 363780 47485 ...
## $ gsp : int 48409 46058 28168 464550 51781 61750 10072 152269 88827 11672 ...
## $ emp : num 1463 1338 814 11258 1408 ...
## $ unemp: num 9.8 6.9 8.7 6.7 7.4 3.8 4.3 5.7 5.9 8.7 ...
## state year pcap hwy
## ALABAMA : 1 Min. :1986 Min. : 2936 Min. : 1830
## ARIZONA : 1 1st Qu.:1986 1st Qu.: 9461 1st Qu.: 4137
## ARKANSAS : 1 Median :1986 Median : 19306 Median : 8179
## CALIFORNIA : 1 Mean :1986 Mean : 27393 Mean :10736
## COLORADO : 1 3rd Qu.:1986 3rd Qu.: 29882 3rd Qu.:12292
## CONNECTICUT: 1 Max. :1986 Max. :139043 Max. :43351
## (Other) :42
## water util pc gsp
## Min. : 335.5 Min. : 770.8 Min. : 6939 Min. : 7585
## 1st Qu.: 1023.5 1st Qu.: 3042.7 1st Qu.: 28129 1st Qu.: 21183
## Median : 2926.7 Median : 8315.6 Median : 50303 Median : 47956
## Mean : 4366.6 Mean :12290.5 Mean : 72260 Mean : 75459
## 3rd Qu.: 4867.9 3rd Qu.:13173.6 3rd Qu.: 83594 3rd Qu.: 87571
## Max. :24592.3 Max. :75237.1 Max. :363780 Max. :464550
##
## emp unemp
## Min. : 196.3 Min. : 2.800
## 1st Qu.: 579.6 1st Qu.: 5.300
## Median : 1373.2 Median : 6.750
## Mean : 2049.9 Mean : 6.929
## 3rd Qu.: 2586.4 3rd Qu.: 8.275
## Max. :11258.0 Max. :13.000
##
## [1] 48 10
El dataset filtrado contiene 48 observaciones, correspondientes a distintos estados de Estados Unidos en el año 1986, y 10 variables. La variable state actúa como identificador territorial y la variable year toma un único valor, 1986, por lo que no aporta variabilidad al análisis. Las variables restantes son de tipo económico y recogen información sobre capital público (pcap), infraestructuras viarias (hwy), infraestructuras de agua y saneamiento (water), otras infraestructuras públicas (util), capital privado (pc), producto bruto estatal (gsp), empleo no agrícola (emp) y tasa de desempleo (unemp).
La exploración descriptiva muestra que las variables económicas
presentan escalas muy diferentes y una elevada dispersión. Algunas
variables, como pcap, pc, gsp y
emp, tienen valores máximos muy superiores a sus medianas,
lo que sugiere una distribución asimétrica y la presencia de estados con
un tamaño económico claramente mayor. En cambio, unemp se
expresa como tasa y tiene una escala mucho más reducida. Por este
motivo, antes de aplicar el escalado multidimensional resulta necesario
transformar y estandarizar las variables numéricas, evitando que las
distancias entre estados queden dominadas por las variables de mayor
magnitud.
Antes de aplicar el escalado multidimensional se realiza una
transformación de los datos para evitar que las distancias entre estados
quedaran dominadas por las variables de mayor escala. En primer lugar,
se excluirán del análisis las variables state y
year, conservando state únicamente como
identificador territorial. Dado que las variables económicas de volumen
presentan fuertes diferencias de magnitud y asimetría positiva, se
aplicará una transformación logarítmica a las variables
de capital, infraestructuras, producto bruto estatal y empleo. La tasa
de desempleo se mantiene en su escala original al tratarse de una
variable porcentual. Finalmente, todas las variables son
estandarizadas para que contribuyeran de forma comparable al
cálculo de la matriz de distancias. Esta matriz de distancias constituye
la entrada del escalado multidimensional.
# Variables económicas seleccionadas
Produc_1986_num <- Produc_1986[, c(
"pcap",
"hwy",
"water",
"util",
"pc",
"gsp",
"emp",
"unemp"
)]
# Asignar nombres de los estados como identificadores de fila
rownames(Produc_1986_num) <- Produc_1986$state
# Transformación logarítmica para variables de tamaño o volumen
Produc_1986_transf <- Produc_1986_num
variables_log <- c(
"pcap",
"hwy",
"water",
"util",
"pc",
"gsp",
"emp"
)
Produc_1986_transf[, variables_log] <- log(
Produc_1986_transf[, variables_log]
)
# La variable unemp se mantiene en escala original porque ya es una tasa
# Después se estandarizan todas las variables
Produc_1986_scaled <- scale(Produc_1986_transf)
# Comprobación
summary(Produc_1986_scaled)## pcap hwy water util
## Min. :-1.9268 Min. :-1.79322 Min. :-1.8459 Min. :-2.0375
## 1st Qu.:-0.6818 1st Qu.:-0.78623 1st Qu.:-0.8257 1st Qu.:-0.7796
## Median : 0.0801 Median : 0.05191 Median : 0.1363 Median : 0.1472
## Mean : 0.0000 Mean : 0.00000 Mean : 0.0000 Mean : 0.0000
## 3rd Qu.: 0.5457 3rd Qu.: 0.55844 3rd Qu.: 0.6018 3rd Qu.: 0.5682
## Max. : 2.1846 Max. : 2.11510 Max. : 2.0843 Max. : 2.1704
## pc gsp emp unemp
## Min. :-2.11378 Min. :-1.76622 Min. :-1.84821 Min. :-1.8575
## 1st Qu.:-0.58863 1st Qu.:-0.75777 1st Qu.:-0.78053 1st Qu.:-0.7329
## Median : 0.04485 Median : 0.04445 Median : 0.07235 Median :-0.0806
## Mean : 0.00000 Mean : 0.00000 Mean : 0.00000 Mean : 0.0000
## 3rd Qu.: 0.59865 3rd Qu.: 0.63577 3rd Qu.: 0.69772 3rd Qu.: 0.6054
## Max. : 2.20139 Max. : 2.27419 Max. : 2.15033 Max. : 2.7309
## pcap hwy water util pc gsp emp unemp
## 0 0 0 0 0 0 0 0
## pcap hwy water util pc gsp emp unemp
## 1 1 1 1 1 1 1 1
Una vez aplicadas las transformaciones, se presenta el resumen descriptivo de las variables que se utilizarán en el escalado multidimensional. Las variables económicas de volumen fueron transformadas mediante logaritmos para reducir la asimetría y suavizar el efecto de los estados con mayor tamaño económico. Posteriormente, todas las variables fueron estandarizadas, por lo que sus medias se sitúan próximas a 0 y sus desviaciones típicas son iguales a 1. Esta transformación permite que todas las variables contribuyan de forma comparable al cálculo de las distancias entre estados.
Presentación de una tabla descriptiva de las variables empleadas una vez transformadas.
# Convertir la matriz estandarizada en data frame
Produc_1986_scaled_df <- as.data.frame(Produc_1986_scaled)
# Tabla descriptiva de variables transformadas y estandarizadas
tabla_desc_transformadas <- data.frame(
Variable = names(Produc_1986_scaled_df),
Media = round(sapply(Produc_1986_scaled_df, mean), 4),
Desv_tipica = round(sapply(Produc_1986_scaled_df, sd), 4),
Minimo = round(sapply(Produc_1986_scaled_df, min), 4),
Q1 = round(sapply(Produc_1986_scaled_df, quantile, probs = 0.25), 4),
Mediana = round(sapply(Produc_1986_scaled_df, median), 4),
Q3 = round(sapply(Produc_1986_scaled_df, quantile, probs = 0.75), 4),
Maximo = round(sapply(Produc_1986_scaled_df, max), 4)
)
# Eliminar nombres de fila
rownames(tabla_desc_transformadas) <- NULL
# Mostrar tabla formateada
kable(
tabla_desc_transformadas,
caption = "Resumen descriptivo de las variables transformadas y estandarizadas",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Variable | Media | Desv_tipica | Minimo | Q1 | Mediana | Q3 | Maximo |
|---|---|---|---|---|---|---|---|
| pcap | 0 | 1 | -1.9268 | -0.6818 | 0.0801 | 0.5457 | 2.1846 |
| hwy | 0 | 1 | -1.7932 | -0.7862 | 0.0519 | 0.5584 | 2.1151 |
| water | 0 | 1 | -1.8459 | -0.8257 | 0.1363 | 0.6018 | 2.0843 |
| util | 0 | 1 | -2.0375 | -0.7796 | 0.1472 | 0.5682 | 2.1704 |
| pc | 0 | 1 | -2.1138 | -0.5886 | 0.0449 | 0.5986 | 2.2014 |
| gsp | 0 | 1 | -1.7662 | -0.7578 | 0.0445 | 0.6358 | 2.2742 |
| emp | 0 | 1 | -1.8482 | -0.7805 | 0.0724 | 0.6977 | 2.1503 |
| unemp | 0 | 1 | -1.8575 | -0.7329 | -0.0806 | 0.6054 | 2.7309 |
Como puede verse en la anterior tabla, tras la transformación y estandarización de las variables, todas presentan media 0 y desviación típica 1, lo que permite compararlas en una escala común. Este paso resulta necesario antes de aplicar el escalado multidimensional, ya que las variables originales tenían magnitudes muy diferentes y podían distorsionar el cálculo de distancias. Los rangos observados muestran que la transformación logarítmica ha suavizado la dispersión de las variables económicas de volumen, situando la mayoría de los valores entre aproximadamente -2 y 2. La tasa de desempleo, aunque no fue transformada logarítmicamente, también se estandarizó y muestra un valor máximo más elevado, lo que sugiere la presencia de algún estado con desempleo relativamente alto. En conjunto, la tabla confirma que las variables están preparadas para construir una matriz de distancias equilibrada entre estados.
Análisis de la estructura de relación entre variables con una representación adecuada.
Para atender a esta cuestión, se considera que lo más adecuado es analizar la matriz de correlaciones entre las variables transformadas y estandarizadas, y representarla mediante un correlograma. Así puedes ver qué variables se mueven juntas y si existen bloques de variables relacionadas.
# Convertir a data frame las variables transformadas y estandarizadas
Produc_1986_scaled_df <- as.data.frame(Produc_1986_scaled)
# Matriz de correlaciones
cor_produc_1986 <- cor(
Produc_1986_scaled_df,
use = "pairwise.complete.obs"
)
# Tabla de correlaciones
kable(
round(cor_produc_1986, 3),
caption = "Matriz de correlaciones entre variables transformadas y estandarizadas",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| pcap | hwy | water | util | pc | gsp | emp | unemp | |
|---|---|---|---|---|---|---|---|---|
| pcap | 1.000 | 0.977 | 0.972 | 0.985 | 0.958 | 0.978 | 0.966 | 0.144 |
| hwy | 0.977 | 1.000 | 0.934 | 0.929 | 0.958 | 0.951 | 0.932 | 0.238 |
| water | 0.972 | 0.934 | 1.000 | 0.957 | 0.928 | 0.979 | 0.974 | 0.039 |
| util | 0.985 | 0.929 | 0.957 | 1.000 | 0.929 | 0.962 | 0.956 | 0.091 |
| pc | 0.958 | 0.958 | 0.928 | 0.929 | 1.000 | 0.954 | 0.929 | 0.275 |
| gsp | 0.978 | 0.951 | 0.979 | 0.962 | 0.954 | 1.000 | 0.994 | 0.071 |
| emp | 0.966 | 0.932 | 0.974 | 0.956 | 0.929 | 0.994 | 1.000 | 0.030 |
| unemp | 0.144 | 0.238 | 0.039 | 0.091 | 0.275 | 0.071 | 0.030 | 1.000 |
# Correlograma
corrplot(
cor_produc_1986,
method = "color",
type = "lower",
order = "hclust",
addCoef.col = "black",
number.cex = 0.70,
tl.col = "#1F2937",
tl.srt = 35,
tl.cex = 0.85,
diag = FALSE,
col = colorRampPalette(c("#B2182B", "white", "#2166AC"))(200),
mar = c(0, 0, 2, 0),
title = "Estructura de relación entre variables económicas. Año 1986"
)La matriz de correlaciones muestra una estructura relacional muy marcada. Las variables vinculadas al capital público, infraestructuras, capital privado, producto bruto estatal y empleo presentan correlaciones positivas muy elevadas entre sí. Esto indica que comparten una dimensión común relacionada con el tamaño económico y la dotación infraestructural de los estados. En particular, variables como gsp y emp, o pcap y util, muestran asociaciones prácticamente lineales, lo que evidencia una elevada redundancia informativa entre estos indicadores.
En cambio, la tasa de desempleo presenta correlaciones bajas con el resto de variables, lo que sugiere que mide una dimensión distinta, más vinculada a la situación del mercado laboral que al volumen económico o infraestructural de cada estado. Por tanto, unemp puede aportar una fuente de diferenciación adicional dentro del análisis.
De cara al escalado multidimensional, esta estructura implica que las distancias entre estados estarán muy condicionadas por el bloque de variables económicas altamente correlacionadas. En consecuencia, la representación MDS tenderá a ordenar los estados principalmente según una dimensión de tamaño económico e infraestructural, mientras que la tasa de desempleo podrá introducir diferencias secundarias entre estados que, aun siendo similares en tamaño económico, presenten situaciones laborales distintas.
Matiz metodológico:La alta correlación entre varias variables no impide aplicar el MDS, pero sí debe tenerse en cuenta en la interpretación, ya que las variables redundantes pueden reforzar en exceso una misma dimensión latente dentro del cálculo de distancias.
Se aplican MDS clásico con cmdscale(), MDS no métrico con isoMDS() y Sammon con sammon().
A continuación, se realiza el análisis de escalado multidimensional utilizando tres metodologías distintas: un MDS clásico, un MDS no métrico y, por último un MDS mediante el método de Sammon, una variante del escalado no métrico que busca minimizar una función de error o Stress con una característica particular: otorga un mayor peso a las distancias pequeñas originales entre los datos. Esto permite que el mapa resultante preserve con mayor precisión las relaciones de proximidad local entre los individuos u objetos analizados.
Posteriormente, una vez obtenidos los resultados se mide la bondad de los mismos con distintas medidas de bondad (o maldad):
El GOF y las medidas de stress son indicadores utilizados para evaluar la bondad del ajuste en el escalamiento multidimensional, es decir, para comprobar hasta qué punto el mapa obtenido representa adecuadamente las disimilitudes o distancias originales entre los casos.
El GOF, o Goodness of Fit, mide la bondad del ajuste desde la perspectiva contraria: indica qué proporción de la información original queda bien representada en la solución obtenida. Se utiliza especialmente en el MDS clásico y suele calcularse a partir de los autovalores. En este caso, cuanto más alto es el valor, mejor es el ajuste. Valores cercanos a 1 indican que la representación es muy buena.
Las medidas de stress indican el grado de “maldad”, es decir, de error o desajuste de la solución. Comparan las distancias representadas en el espacio reducido con las disimilitudes originales. Por tanto, cuanto menor es el stress, mejor es el ajuste. Un valor cercano a 0 indica una representación muy buena, mientras que valores elevados indican que el mapa no reproduce bien la estructura original de los datos.
En resumen, el stress mide el error de la solución y debe ser bajo, mientras que el GOF mide la calidad de la representación y debe ser alto. Además, el ajuste también puede evaluarse visualmente mediante el diagrama de Shepard, donde una buena solución se observa cuando los puntos siguen aproximadamente una línea diagonal.
# 1. MATRIZ DE DATOS PARA MDS
# Usamos los estados como nombres de fila
rownames(Produc_1986_scaled_df) <- Produc_1986$state
# Matriz de distancias euclídeas
dist_mds <- dist(Produc_1986_scaled_df, method = "euclidean")
# 2. MDS CLÁSICO CON cmdscale()
mds_clasico <- cmdscale(
dist_mds,
k = 2,
eig = TRUE
)
coord_clasico <- as.data.frame(mds_clasico$points)
colnames(coord_clasico) <- c("Dim1", "Dim2")
coord_clasico$state <- rownames(Produc_1986_scaled_df)
coord_clasico$Metodo <- "MDS clásico"
# Bondad de ajuste del MDS clásico
mds_clasico$GOF## [1] 0.9756712 0.9756712
# 3. MDS NO MÉTRICO CON isoMDS()
mds_iso <- isoMDS(
dist_mds,
y = mds_clasico$points,
k = 2,
maxit = 100
)## initial value 1.683819
## iter 5 value 1.457087
## final value 1.452029
## converged
coord_iso <- as.data.frame(mds_iso$points)
colnames(coord_iso) <- c("Dim1", "Dim2")
coord_iso$state <- rownames(Produc_1986_scaled_df)
coord_iso$Metodo <- "MDS no métrico"
# Stress del MDS no métrico
mds_iso$stress## [1] 1.452029
# 4. MDS DE SAMMON CON sammon()
mds_sammon <- sammon(
dist_mds,
y = mds_clasico$points,
k = 2,
niter = 100
)## Initial stress : 0.00371
## stress after 10 iters: 0.00228, magic = 0.500
## stress after 20 iters: 0.00228, magic = 0.500
coord_sammon <- as.data.frame(mds_sammon$points)
colnames(coord_sammon) <- c("Dim1", "Dim2")
coord_sammon$state <- rownames(Produc_1986_scaled_df)
coord_sammon$Metodo <- "Sammon"
# Stress de Sammon
mds_sammon$stress## [1] 0.002282167
# Tabla comparativa de ajuste
resumen_mds <- data.frame(
Metodo = c(
"MDS clásico - cmdscale()",
"MDS no métrico - isoMDS()",
"Sammon - sammon()"
),
Indicador = c(
"GOF",
"Stress",
"Stress"
),
Valor = c(
round(mds_clasico$GOF[1], 4),
round(mds_iso$stress, 4),
round(mds_sammon$stress, 4)
)
)
kable(
resumen_mds,
caption = "Indicadores de ajuste de los métodos de escalado multidimensional",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Metodo | Indicador | Valor |
|---|---|---|
| MDS clásico - cmdscale() | GOF | 0.9757 |
| MDS no métrico - isoMDS() | Stress | 1.4520 |
| Sammon - sammon() | Stress | 0.0023 |
Los indicadores de ajuste obtenidos muestran que la representación bidimensional mediante escalado multidimensional es adecuada. En el MDS clásico, el valor de bondad de ajuste es muy elevado, \(GOF = 0.9757\), lo que indica que las dos dimensiones retenidas conservan una proporción muy alta de la estructura de distancias original entre los estados. Por su parte, el MDS no métrico presenta un valor de stress bajo, 1.4520, lo que sugiere que la ordenación relativa de las disimilitudes entre estados se reproduce correctamente en el espacio bidimensional.
El método de Sammon obtiene un stress especialmente reducido, 0.0023, lo que indica una representación muy fiel de las distancias, especialmente entre estados próximos. En conjunto, los tres métodos ofrecen resultados consistentes y apoyan la utilización de una solución bidimensional para representar las semejanzas y diferencias económicas entre los estados en 1986.
No obstante, los indicadores no son directamente comparables entre sí, ya que el MDS clásico utiliza una medida de bondad de ajuste, mientras que isoMDS() y sammon() utilizan medidas de stress. En el primer caso, valores más altos indican mejor ajuste; en los otros dos, valores más bajos indican mejor representación.
Se genera un gráfico comparativo de las soluciones obtenidas.
Se realiza un gráfico comparativo que permita observar la estructura de proximidad entre estados bajo los tres métodos de escalado multidimensional previamente ejecutados. La interpretación debe centrarse en la cercanía relativa entre estados y en la formación de agrupaciones, más que en el valor absoluto de los ejes, ya que la orientación y escala de las dimensiones puede variar entre métodos. En conjunto, la comparación permite valorar si las soluciones obtenidas son consistentes en la representación de las similitudes económicas entre estados.
# Gráfico comparativo
coord_todos_mds <- rbind(
coord_clasico,
coord_iso,
coord_sammon
)
ggplot(
coord_todos_mds,
aes(
x = Dim1,
y = Dim2,
label = state
)
) +
geom_point(
size = 2.2,
alpha = 0.85,
color = "#1F4E79"
) +
geom_text(
size = 2.3,
vjust = -0.6,
check_overlap = TRUE
) +
facet_wrap(
~ Metodo,
scales = "free"
) +
labs(
title = "Comparación de soluciones de escalado multidimensional",
subtitle = "MDS clásico, MDS no métrico y método de Sammon",
x = "Dimensión 1",
y = "Dimensión 2"
) +
theme_minimal(base_size = 12)
Los tres mapas muestran una estructura muy parecida. Esto es importante
porque indica que la solución es estable y no depende excesivamente del
método utilizado.
En los tres gráficos se observa que algunos estados aparecen claramente alejados del núcleo central. Por ejemplo, California y New York se sitúan hacia la derecha, lo que probablemente refleja su mayor tamaño económico, mayor empleo, mayor producto bruto estatal y mayor dotación de capital e infraestructuras. Esto es coherente con la matriz de correlaciones, donde las variables económicas de volumen estaban muy fuertemente asociadas entre sí.
También aparecen agrupaciones de estados más pequeños o con menor volumen económico hacia la zona izquierda del gráfico. En la parte inferior aparecen estados como Mississippi, West Virginia o Louisiana, que se diferencian del resto en la segunda dimensión. Esta segunda dimensión podría estar recogiendo diferencias vinculadas a la tasa de desempleo o a una estructura económica menos alineada con el eje principal de tamaño económico.
No obstante, conviene recordar que en MDS los ejes no se interpretan de forma directa como variables originales. Lo relevante es la proximidad relativa entre estados, no tanto la orientación exacta del gráfico. Además, los mapas pueden rotarse o reflejarse sin cambiar su significado.
Se selecciona una solución principal, interpretando económicamente las dos dimensiones del mapa.
Se selecciona como solución principal el MDS clásico mediante cmdscale(), dado que presenta una bondad de ajuste muy elevada (GOF = 0.9757) y ofrece una representación bidimensional clara y consistente con las soluciones obtenidas mediante MDS no métrico y Sammon. Además, desde el criterio de parsimonia, resulta preferible al no requerir procedimientos iterativos de optimización ni transformaciones adicionales de las distancias, proporcionando una solución estable y fácilmente interpretable que reproduce adecuadamente la estructura de proximidades observada en los datos. Aunque el método de Sammon obtiene un valor de stress extremadamente reducido y el MDS no métrico también ofrece un ajuste satisfactorio, ambos generan configuraciones muy similares a la obtenida mediante el MDS clásico, sin aportar una mejora sustancial en la interpretación económica de los resultados. Por ello, se opta por el modelo más sencillo que explica adecuadamente la estructura de los datos.
La primera dimensión del mapa puede interpretarse como un eje de tamaño económico y dotación de capital e infraestructuras, separando a los estados de mayor volumen económico, como California, New York o Texas, de estados de menor dimensión económica. La segunda dimensión introduce una diferenciación adicional vinculada al perfil económico-laboral de los estados, separando algunos estados del sur y de menor dinamismo relativo de otros estados con perfiles económicos más favorables. En conjunto, el mapa permite representar de forma sintética las semejanzas y diferencias económicas entre los estados en 1986.
La interpretación de los ejes en MDS debe hacerse con prudencia, ya que las dimensiones no son variables observadas directamente. Lo más relevante es la posición relativa entre estados y la formación de agrupaciones o separaciones dentro del mapa.
Se identifican aquellos estados que aparecen como más próximos y cuáles son los más alejados.
Para contestar esta cuestión de forma rigurosa, lo mejor es no identificar los estados visualmente observando el gráfico, sino calcular las distancias entre estados en la solución MDS seleccionada, que en nuestro caso ha sido el MDS clásico.
# Top 10 pares de estados más cercanos y más alejados en la solución MDS clásica
# Coordenadas de la solución principal
coord_principal <- coord_clasico[, c("Dim1", "Dim2")]
rownames(coord_principal) <- coord_clasico$state
# Matriz de distancias euclídeas en el mapa MDS
dist_mapa <- as.matrix(dist(coord_principal))
# Eliminar la diagonal
diag(dist_mapa) <- NA
# Convertir matriz de distancias a tabla
tabla_distancias <- as.data.frame(as.table(dist_mapa))
colnames(tabla_distancias) <- c("Estado_1", "Estado_2", "Distancia")
# Eliminar NA
tabla_distancias <- tabla_distancias[!is.na(tabla_distancias$Distancia), ]
# Eliminar pares duplicados simétricos
tabla_distancias <- tabla_distancias[
as.character(tabla_distancias$Estado_1) < as.character(tabla_distancias$Estado_2),
]
# Top 10 estados más cercanos
pares_mas_cercanos <- tabla_distancias[
order(tabla_distancias$Distancia),
][1:10, ]
# Añadir columna de posición
pares_mas_cercanos <- data.frame(
Puesto = 1:nrow(pares_mas_cercanos),
pares_mas_cercanos
)
rownames(pares_mas_cercanos) <- NULL
kable(
pares_mas_cercanos,
caption = "Diez pares de estados más cercanos en la solución MDS clásica",
align = "c",
row.names = FALSE,
digits = 3
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Puesto | Estado_1 | Estado_2 | Distancia |
|---|---|---|---|
| 1 | INDIANA | WISCONSIN | 0.043 |
| 2 | MINNESOTA | NORTH_CAROLINA | 0.118 |
| 3 | NORTH_CAROLINA | VIRGINIA | 0.147 |
| 4 | ALABAMA | KENTUCKY | 0.183 |
| 5 | ARIZONA | COLORADO | 0.204 |
| 6 | MINNESOTA | VIRGINIA | 0.248 |
| 7 | KANSAS | SOUTH_CAROLINA | 0.255 |
| 8 | DELAWARE | SOUTH_DAKOTA | 0.291 |
| 9 | OKLAHOMA | OREGON | 0.292 |
| 10 | DELAWARE | RHODE_ISLAND | 0.298 |
# Top 10 estados más alejados
pares_mas_alejados <- tabla_distancias[
order(tabla_distancias$Distancia, decreasing = TRUE),
][1:10, ]
# Añadir columna de posición
pares_mas_alejados <- data.frame(
Puesto = 1:nrow(pares_mas_alejados),
pares_mas_alejados
)
rownames(pares_mas_alejados) <- NULL
kable(
pares_mas_alejados,
caption = "Diez pares de estados más alejados en la solución MDS clásica",
align = "c",
row.names = FALSE,
digits = 3
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Puesto | Estado_1 | Estado_2 | Distancia |
|---|---|---|---|
| 1 | CALIFORNIA | VERMONT | 10.729 |
| 2 | NEW_YORK | VERMONT | 10.042 |
| 3 | TEXAS | VERMONT | 9.759 |
| 4 | CALIFORNIA | DELAWARE | 9.640 |
| 5 | CALIFORNIA | RHODE_ISLAND | 9.558 |
| 6 | CALIFORNIA | SOUTH_DAKOTA | 9.558 |
| 7 | CALIFORNIA | IDAHO | 9.430 |
| 8 | CALIFORNIA | NEW_HAMPSHIRE | 9.381 |
| 9 | CALIFORNIA | NORTH_DAKOTA | 9.288 |
| 10 | CALIFORNIA | WYOMING | 9.132 |
Ejercicio 5 - Correlación Canónica
Para este ejercicio cargamos el conjunto de datos “Snmesp”, que proviene de la librería ““plm” y contiene información de empresas españolas. Trabajaremos con los datos del año 1990 y con las siguientes variables:
- n: logaritmo del empleo,
- w: logaritmo de los salarios,
- y: logaritmo de la producción real,
- i: logaritmo de los consumos intermedios,
- k: logaritmo del capital real,
- f: flujo de caja real.
Se definen los siguientes bloques de variables:
- Bloque de producción: y, i, k.
- Bloque laboral-financiero: n, w, f.
# Cargar dataset
data("Snmesp", package = "plm")
# Filtrar año 1990 y seleccionar variables
Snmesp_1990 <- subset(
Snmesp,
year == 1990,
select = c(firm, year, y, i, k, n, w, f)
)
# Eliminar posibles valores perdidos
Snmesp_1990 <- na.omit(Snmesp_1990)
# Definir bloques
bloque_produccion <- Snmesp_1990[, c("y", "i", "k")]
bloque_laboral_financiero <- Snmesp_1990[, c("n", "w", "f")]
# Estandarizar bloques
X_prod <- scale(bloque_produccion)
Y_labfin <- scale(bloque_laboral_financiero)
# Comprobación
dim(Snmesp_1990)## [1] 738 8
## firm year y i
## Min. : 1.0 Min. :1990 Min. : 3.730 Min. : 2.763
## 1st Qu.:185.2 1st Qu.:1990 1st Qu.: 6.168 1st Qu.: 5.662
## Median :369.5 Median :1990 Median : 7.131 Median : 6.646
## Mean :369.5 Mean :1990 Mean : 7.148 Mean : 6.678
## 3rd Qu.:553.8 3rd Qu.:1990 3rd Qu.: 8.068 3rd Qu.: 7.604
## Max. :738.0 Max. :1990 Max. :11.807 Max. :11.621
## k n w f
## Min. : 1.701 Min. :2.303 Min. :-0.7453 Min. :-1750.87
## 1st Qu.: 5.368 1st Qu.:3.951 1st Qu.: 0.5174 1st Qu.: 13.38
## Median : 6.560 Median :4.868 Median : 0.7483 Median : 48.43
## Mean : 6.563 Mean :4.897 Mean : 0.7382 Mean : 217.17
## 3rd Qu.: 7.615 3rd Qu.:5.732 3rd Qu.: 0.9829 3rd Qu.: 173.54
## Max. :12.207 Max. :8.796 Max. : 1.8965 Max. : 7441.86
Se trabaja únicamente con las observaciones correspondientes al año
1990. Las variables se dividen en dos bloques: un bloque de producción,
formado por y, i y k, y un bloque
laboral-financiero, formado por n, w y
f. Antes de aplicar la correlación canónica, las variables
se estandarizan para evitar que las diferencias de escala afecten a la
estimación de las combinaciones lineales.
Descripción inicial de ambos bloques (variables previo escalado)
# Función descriptiva
tabla_descriptiva <- function(datos) {
data.frame(
Variable = names(datos),
Media = round(sapply(datos, mean), 4),
Desv_tipica = round(sapply(datos, sd), 4),
Minimo = round(sapply(datos, min), 4),
Q1 = round(sapply(datos, quantile, probs = 0.25), 4),
Mediana = round(sapply(datos, median), 4),
Q3 = round(sapply(datos, quantile, probs = 0.75), 4),
Maximo = round(sapply(datos, max), 4),
row.names = NULL
)
}
desc_produccion <- tabla_descriptiva(bloque_produccion)
desc_labfin <- tabla_descriptiva(bloque_laboral_financiero)
kable(
desc_produccion,
caption = "Resumen descriptivo del bloque de producción",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(0, bold = TRUE, color = "white", background = "#1F4E79")| Variable | Media | Desv_tipica | Minimo | Q1 | Mediana | Q3 | Maximo |
|---|---|---|---|---|---|---|---|
| y | 7.1476 | 1.4145 | 3.7302 | 6.1680 | 7.1313 | 8.0681 | 11.8069 |
| i | 6.6780 | 1.4759 | 2.7628 | 5.6620 | 6.6462 | 7.6039 | 11.6209 |
| k | 6.5628 | 1.7754 | 1.7015 | 5.3678 | 6.5605 | 7.6147 | 12.2068 |
kable(
desc_labfin,
caption = "Resumen descriptivo del bloque laboral-financiero",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(0, bold = TRUE, color = "white", background = "#1F4E79")| Variable | Media | Desv_tipica | Minimo | Q1 | Mediana | Q3 | Maximo |
|---|---|---|---|---|---|---|---|
| n | 4.8968 | 1.2226 | 2.3026 | 3.9512 | 4.8675 | 5.7317 | 8.7956 |
| w | 0.7382 | 0.3676 | -0.7453 | 0.5174 | 0.7483 | 0.9829 | 1.8965 |
| f | 217.1690 | 683.5622 | -1750.8730 | 13.3779 | 48.4316 | 173.5415 | 7441.8620 |
# GRÁFICOS BLOQUE PRODUCCIÓN
# Histogramas
par(mfrow = c(2, 3))
for (v in names(bloque_produccion)) {
hist(bloque_produccion[[v]],
main = paste("Histograma de", v),
xlab = v,
col = "blue",
border = "white")
}
par(mfrow = c(1, 1))# Boxplots para detectar dispersión y posibles valores extremos
boxplot(bloque_produccion,
main = "Distribución de variables bloque producción",
las = 2,
col = "blue")# GRÁFICOS BLOQUE LABORAL-FINANCIERO
# Histogramas
par(mfrow = c(2, 3))
for (v in names(bloque_laboral_financiero)) {
hist(bloque_laboral_financiero[[v]],
main = paste("Histograma de", v),
xlab = v,
col = "blue",
border = "white")
}
par(mfrow = c(1, 1))# Boxplots para detectar dispersión y posibles valores extremos
boxplot(bloque_laboral_financiero,
main = "Distribución de variables bloque laboral-financiero",
las = 2,
col = "blue")La descripción inicial muestra que las variables del bloque de producción presentan medias y medianas relativamente próximas, lo que sugiere distribuciones moderadamente equilibradas, aunque con diferencias apreciables entre empresas. Dado que y, i y k están expresadas en logaritmos, las diferencias reflejan principalmente variaciones relativas en producción, consumos intermedios y capital real. En el bloque laboral-financiero, el empleo y los salarios presentan también una estructura relativamente estable, especialmente en el caso de w, cuya dispersión es menor.
La variable f, correspondiente al flujo de caja real, presenta un comportamiento claramente diferente. Su media es muy superior a la mediana y la desviación típica es elevada, con presencia tanto de valores negativos como de valores máximos muy altos. Esto indica una fuerte asimetría y la existencia de empresas con flujos de caja extremos. Por este motivo, aunque se mantiene en escala original en la descripción inicial, para la correlación canónica se estandarizan todas las variables. Además, debe interpretarse con cautela el papel de f, ya que sus valores extremos pueden influir en las correlaciones y en las combinaciones canónicas.
Como comprobación de robustez, podría considerarse una transformación arco seno hiperbólico como \(asinh(f)\), que permite tratar valores negativos y reduce la influencia de valores extremos, manteniendo una interpretación similar a una transformación logarítmica para valores elevados.
Se analizan las correlaciones cruzadas entre los dos grupos de variables o bloques.
# CORRELACIONES CRUZADAS ENTRE LOS DOS BLOQUES DE VARIABLES
# Bloque de producción: y, i, k
# Bloque laboral-financiero: n, w, f
# Calcular matriz de correlaciones cruzadas
cor_cruzadas <- cor(
X_prod,
Y_labfin,
use = "pairwise.complete.obs"
)
# TABLA FORMATEADA
kable(
round(cor_cruzadas, 3),
caption = "Correlaciones cruzadas entre el bloque de producción y el bloque laboral-financiero",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
) %>%
column_spec(
1,
bold = TRUE
)| n | w | f | |
|---|---|---|---|
| y | 0.900 | 0.520 | 0.496 |
| i | 0.855 | 0.492 | 0.472 |
| k | 0.820 | 0.484 | 0.441 |
# GRÁFICO DE CORRELACIONES CRUZADAS
corrplot(
cor_cruzadas,
method = "color",
is.corr = FALSE,
addCoef.col = "black",
number.cex = 0.85,
tl.col = "#1F2937",
tl.srt = 35,
tl.cex = 0.9,
col = colorRampPalette(
c("#B2182B", "white", "#2166AC")
)(200),
mar = c(0, 0, 2, 0),
title = "Correlaciones cruzadas entre bloques"
)La matriz de correlaciones cruzadas muestra
asociaciones positivas entre todas las variables del bloque de
producción y las variables del bloque laboral-financiero. Las relaciones
más intensas se observan entre las variables productivas y el empleo
(n), especialmente entre producción real y empleo
(r = 0.900), consumos intermedios y empleo (r = 0.855)
y capital real y empleo (r = 0.820). Esto indica que las
empresas con mayor dimensión productiva tienden también a presentar
mayores niveles de empleo.
Las correlaciones con los salarios (w) y con el flujo de
caja real (f) son también positivas, aunque de intensidad
moderada. Por tanto, las empresas más productivas tienden a mostrar
mayores salarios y mayor flujo de caja, pero estas relaciones son menos
directas que la existente con el empleo. En conjunto, los resultados
anticipan la existencia de una relación multivariante relevante entre
ambos bloques, que será sintetizada posteriormente mediante la
correlación canónica.
Correlación Canónica clásica con cancor().
Se aplica una correlación canónica clásica mediante cancor() sobre los dos bloques previamente estandarizados. El objetivo es obtener combinaciones lineales del bloque de producción y del bloque laboral-financiero que maximicen la correlación entre ambos conjuntos de variables. Las correlaciones canónicas indican la intensidad de la relación entre cada par de variables canónicas, mientras que las cargas permiten interpretar qué variables contribuyen más a cada dimensión.
# Aplicar correlación canónica clásica
cca_1990 <- cancor(
x = X_prod,
y = Y_labfin
)
# Ver resultados principales
cca_1990## $cor
## [1] 0.970441254 0.034395533 0.006036322
##
## $xcoef
## [,1] [,2] [,3]
## y -0.093068210 0.07691265 -0.25899607
## i 0.060269887 -0.01226041 0.25278788
## k -0.002497595 -0.07145545 0.02071595
##
## $ycoef
## [,1] [,2] [,3]
## n -0.030464156 -0.00300201 0.02904165
## w -0.010004692 -0.02618981 -0.02717680
## f -0.003901059 0.03310731 -0.02409966
##
## $xcenter
## y i k
## 2.503173e-16 8.988947e-17 -7.223431e-17
##
## $ycenter
## n w f
## -1.696891e-16 -1.852605e-17 1.129686e-17
# Graficamos las correlaciones canónicas
cor_can <- cca_1990$cor
barplot(
cor_can,
names.arg = paste0("Dimensión ", seq_along(cor_can)),
ylim = c(0, 1),
col = "#1F4E79",
main = "Correlaciones canónicas",
ylab = "Correlación canónica"
)
abline(h = 0.5, lty = 2, col = "grey40")
Los resultados de la correlación canónica clásica muestran que la
relación entre el bloque productivo (y,i,k) y el bloque
laboral-financiero (n,w,f) se concentra casi
exclusivamente en la primera dimensión canónica. La primera correlación
canónica alcanza un valor muy elevado, \(\rho_1 = 0.970\), lo que indica una
asociación lineal muy intensa entre una combinación de las variables
productivas y una combinación de las variables laborales-financieras. En
cambio, la segunda y tercera correlación canónica, con valores de 0.034
y 0.006, son prácticamente nulas, por lo que no aportan dimensiones
interpretativas relevantes adicionales.
En cuanto a los coeficientes canónicos, en el bloque productivo la primera función canónica está principalmente determinada por y e i, mientras que k tiene un peso directo muy reducido. En el bloque laboral-financiero, la primera función canónica aparece dominada por n, seguida de forma mucho más débil por w y f. Esto sugiere que la asociación principal entre ambos bloques se articula especialmente a través de la relación entre la dinámica productiva y la variable laboral n.
No obstante, los coeficientes canónicos deben interpretarse con cautela, ya que pueden verse afectados por la escala de las variables y por la colinealidad interna dentro de cada bloque. Por ello, la interpretación sustantiva debe complementarse con las cargas canónicas y los mapas de variables. En conjunto, el resultado permite concluir que existe una estructura común muy fuerte entre ambos grupos de variables, pero que dicha estructura es esencialmente unidimensional.
Por lo tanto, existe una relación canónica muy fuerte entre producción y variables laborales-financieras, y las otras dos dimensiones son irrelevantes desde el punto de vista interpretativo. El primer eje parece estar asociado sobre todo a la conexión entre y/i en el bloque productivo y n en el bloque laboral-financiero, es decir entre producción real y consumos intermedios ,desde el bloque productivo, con la variable empleo en el bloque laboral-financiero.
Obtención e interpretación de las correlaciones canónicas.
# Correlaciones canónicas
cor_can <- cca_1990$cor
tabla_cor_can <- data.frame(
Funcion_canonica = paste0("Par canónico ", seq_along(cor_can)),
Correlacion_canonica = round(cor_can, 4),
Correlacion_cuadrada = round(cor_can^2, 4)
)
kable(
tabla_cor_can,
caption = "Correlaciones canónicas entre el bloque de producción y el bloque laboral-financiero",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Funcion_canonica | Correlacion_canonica | Correlacion_cuadrada |
|---|---|---|
| Par canónico 1 | 0.9704 | 0.9418 |
| Par canónico 2 | 0.0344 | 0.0012 |
| Par canónico 3 | 0.0060 | 0.0000 |
# Coeficientes canónicos
coef_prod <- as.data.frame(round(cca_1990$xcoef, 4))
coef_labfin <- as.data.frame(round(cca_1990$ycoef, 4))
colnames(coef_prod) <- paste0("U", 1:ncol(coef_prod))
colnames(coef_labfin) <- paste0("V", 1:ncol(coef_labfin))
coef_prod$Variable <- rownames(coef_prod)
coef_labfin$Variable <- rownames(coef_labfin)
coef_prod <- coef_prod[, c("Variable", paste0("U", 1:(ncol(coef_prod) - 1)))]
coef_labfin <- coef_labfin[, c("Variable", paste0("V", 1:(ncol(coef_labfin) - 1)))]
rownames(coef_prod) <- NULL
rownames(coef_labfin) <- NULL
kable(
coef_prod,
caption = "Coeficientes canónicos del bloque de producción",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Variable | U1 | U2 | U3 |
|---|---|---|---|
| y | -0.0931 | 0.0769 | -0.2590 |
| i | 0.0603 | -0.0123 | 0.2528 |
| k | -0.0025 | -0.0715 | 0.0207 |
kable(
coef_labfin,
caption = "Coeficientes canónicos del bloque laboral-financiero",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Variable | V1 | V2 | V3 |
|---|---|---|---|
| n | -0.0305 | -0.0030 | 0.0290 |
| w | -0.0100 | -0.0262 | -0.0272 |
| f | -0.0039 | 0.0331 | -0.0241 |
# Obtener las variables canónicas
U_prod <- as.matrix(X_prod) %*% cca_1990$xcoef
V_labfin <- as.matrix(Y_labfin) %*% cca_1990$ycoef
colnames(U_prod) <- paste0("U", 1:ncol(U_prod))
colnames(V_labfin) <- paste0("V", 1:ncol(V_labfin))
# Comprobar correlaciones entre pares canónicos
cor(U_prod, V_labfin)## V1 V2 V3
## U1 9.704413e-01 6.358396e-16 -7.788648e-16
## U2 -2.460754e-16 3.439553e-02 2.296426e-16
## U3 -9.622802e-16 -1.446228e-16 6.036322e-03
# Cargas canónicas
# Correlación entre variables originales y variables canónicas
cargas_prod <- cor(X_prod, U_prod)
cargas_labfin <- cor(Y_labfin, V_labfin)
kable(
round(cargas_prod, 3),
caption = "Cargas canónicas del bloque de producción",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| U1 | U2 | U3 | |
|---|---|---|---|
| y | -0.967 | 0.102 | 0.235 |
| i | -0.918 | 0.140 | 0.371 |
| k | -0.883 | -0.430 | 0.190 |
kable(
round(cargas_labfin, 3),
caption = "Cargas canónicas del bloque laboral-financiero",
align = "c"
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| V1 | V2 | V3 | |
|---|---|---|---|
| n | -0.959 | 0.085 | 0.271 |
| w | -0.557 | -0.531 | -0.639 |
| f | -0.527 | 0.700 | -0.481 |
A partir de los resultados, la correlación canónica clásica muestra una relación multivariante muy fuerte entre el bloque de producción y el bloque laboral-financiero, pero esa relación queda concentrada casi por completo en el primer par canónico.
- Correlaciones canónicas
La primera correlación canónica es muy elevada:
- Par canónico 1: \(\rho = 0.9704\)
- \(\rho^2 = 0.9418\)
Esto significa que la primera combinación lineal del bloque de producción está fuertemente asociada con la primera combinación lineal del bloque laboral-financiero. La correlación cuadrada indica que ambas variables canónicas comparten aproximadamente un 94,18 % de varianza común.
En cambio, el segundo y tercer par canónico son prácticamente irrelevantes:
- Par canónico 2: \(\rho = 0.0344\)
- Par canónico 3: \(\rho = 0.0060\)
Por tanto, la interpretación debe centrarse casi exclusivamente en la primera función canónica.
- Interpretación de las cargas canónicas
Las cargas del bloque de producción sobre la primera variable canónica son todas muy elevada, siendo dominante la producción (y), seguido de cerca de los consumos intermedios (i) y con posterioridad del capital (k):
- y = -0.967
- i = -0.918
- k = -0.883
Esto indica que la primera variable canónica del bloque productivo representa una dimensión general de tamaño o escala productiva de la empresa, asociada simultáneamente con producción real, consumos intermedios y capital real.
En el bloque laboral-financiero, las cargas sobre la primera variable canónica son:
- n = -0.959
- w = -0.557
- f = -0.527
La variable más importante es el empleo (n), seguida de salarios (w) y flujo de caja (f), con cargas moderadas. Por tanto, la primera variable canónica del segundo bloque puede interpretarse como una dimensión de escala laboral-financiera, especialmente dominada por el empleo.
Es importante aclarar metodológicamente que la interpretación del signo negativo no implica una relación inversa, ya que en correlación canónica, el signo de las variables canónicas es arbitrario: se podrían multiplicar ambas variables por -1 y la interpretación sería la misma. Lo relevante es que las variables de ambos bloques cargan en la misma dirección y que la correlación entre las dos variables canónicas es positiva.
Representación gráfica del primer par de variables canónicas y comentario de su significado.
# Representación gráfica del primer par de variables canónicas
colnames(U_prod) <- paste0("U", 1:ncol(U_prod))
colnames(V_labfin) <- paste0("V", 1:ncol(V_labfin))
# Base para el gráfico
datos_can <- data.frame(
firm = Snmesp_1990$firm,
U1_Produccion = U_prod[, 1],
V1_Laboral_Financiero = V_labfin[, 1]
)
# Correlación del primer par canónico
cor_U1_V1 <- cor(
datos_can$U1_Produccion,
datos_can$V1_Laboral_Financiero
)
# Gráfico
ggplot(
datos_can,
aes(
x = U1_Produccion,
y = V1_Laboral_Financiero
)
) +
geom_point(
size = 2.2,
alpha = 0.70,
color = "#1F4E79"
) +
geom_smooth(
method = "lm",
se = FALSE,
linewidth = 0.9,
color = "#B2182B"
) +
geom_vline(
xintercept = 0,
linetype = "dashed",
color = "gray70"
) +
geom_hline(
yintercept = 0,
linetype = "dashed",
color = "gray70"
) +
labs(
title = "Primer par de variables canónicas",
subtitle = paste0(
"Correlación canónica = ",
round(cor_U1_V1, 3)
),
x = "Primera variable canónica del bloque de producción",
y = "Primera variable canónica del bloque laboral-financiero"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 15),
plot.subtitle = element_text(size = 11, color = "gray30"),
panel.grid.minor = element_blank()
)El gráfico confirma visualmente el resultado numérico. La nube de puntos aparece claramente alineada en sentido positivo, con una correlación canónica de aproximadamente 0.97.
Esto significa que las empresas con mayor puntuación en la dimensión productiva tienden también a presentar mayor puntuación en la dimensión laboral-financiera. En términos sustantivos, las empresas con mayor producción, mayor consumo intermedio y mayor capital suelen estar asociadas con mayor empleo, mayores salarios y mayor flujo de caja.
El gráfico refuerza la idea de que el primer par canónico recoge una dimensión general de tamaño empresarial o escala económica de la empresa.
Construcción de un mapa de variables o representación equivalente que ayude a interpretar las cargas de cada bloque.
# MAPA DE VARIABLES SEGÚN CARGAS CANÓNICAS
# 1. Preparar datos para el mapa de variables
mapa_prod <- data.frame(
Variable = rownames(cargas_prod),
Dim1 = cargas_prod[, 1],
Dim2 = cargas_prod[, 2],
Bloque = "Producción"
)
mapa_labfin <- data.frame(
Variable = rownames(cargas_labfin),
Dim1 = cargas_labfin[, 1],
Dim2 = cargas_labfin[, 2],
Bloque = "Laboral-financiero"
)
mapa_variables <- rbind(mapa_prod, mapa_labfin)
# Círculo de referencia
circulo <- data.frame(
x = cos(seq(0, 2 * pi, length.out = 200)),
y = sin(seq(0, 2 * pi, length.out = 200))
)
# 2. Mapa de variables según cargas canónicas
ggplot() +
geom_path(
data = circulo,
aes(x = x, y = y),
color = "gray70",
linewidth = 0.6
) +
geom_hline(
yintercept = 0,
linetype = "dashed",
color = "gray70"
) +
geom_vline(
xintercept = 0,
linetype = "dashed",
color = "gray70"
) +
geom_segment(
data = mapa_variables,
aes(
x = 0,
y = 0,
xend = Dim1,
yend = Dim2,
color = Bloque
),
arrow = arrow(length = unit(0.18, "cm")),
linewidth = 0.8
) +
geom_point(
data = mapa_variables,
aes(
x = Dim1,
y = Dim2,
color = Bloque
),
size = 3
) +
geom_text_repel(
data = mapa_variables,
aes(
x = Dim1,
y = Dim2,
label = Variable,
color = Bloque
),
size = 4,
max.overlaps = 20
) +
coord_equal(
xlim = c(-1, 1),
ylim = c(-1, 1)
) +
facet_wrap(~ Bloque) +
labs(
title = "Mapa de variables según cargas canónicas",
subtitle = "Cargas de las variables en las dos primeras funciones canónicas",
x = "Primera dimensión canónica",
y = "Segunda dimensión canónica",
color = "Bloque"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 15),
plot.subtitle = element_text(size = 11, color = "gray30"),
strip.text = element_text(face = "bold"),
panel.grid.minor = element_blank()
)El mapa de variables muestra que producción (y), consumos intermedios (i) y capital (k) aparecen muy próximas entre sí y alejadas del origen en la primera dimensión. Esto confirma que el bloque de producción tiene una estructura muy coherente: las tres variables participan conjuntamente en la definición del primer eje canónico.
En el bloque laboral-financiero, empleo (n) aparece también muy alejada del origen en la primera dimensión, lo que confirma su papel central; sin embargro, las variables salarios (w) y flujo de caja (f), pese a que también contribuyen a la primera dimensión, lo hacen con una menor intensidad.
En cuanto a la segunda dimensión, el aspecto más remarcable que puede observarse en el mapa muestra cierta oposición entre flujo de caja (f) y salarios (w), es decir, a mayor flujo de caja menor gasto en salarios tiene una empresa y vicebersa. Esta interpretación tiene especial significado de forma interna en el bloque laboral-financiero. Sin embargo, como la segunda correlación canónica es muy baja (\(\rho =0.0344\)), esa dimensión no debe interpretarse como una relación multivariante sustantiva entre bloques. Por tanto, esta segunda dimensión puede ser útil para describir variación interna dentro de los bloques, pero no una asociación relevante entre los bloques comparados: producción y bloque laboral-financiero.
Por otro lado, interpretando la segunda dimensión del bloque de producción parece estar asociada principalmente al capital (k), que presenta la carga más relevante y de signo opuesto a producción (y) y consumos intermedios (i). Esto, al igual que con el bloque anterior, debería interpretarse como una diferenciación interna entre empresas más intensivas en capital y empresas cuya dimensión productiva se refleja más en producción y consumos intermedios. No obstante, como ya dijimos antes, dado que la segunda correlación canónica es muy baja (r = 0.0344), esta dimensión no debe considerarse una relación sustantiva entre bloques, sino un patrón secundario de interpretación limitada intrabloque.
Se comparan los resultados con una solución mediante RGCCA, indicando si la interpretación principal se mantiene.
La solución RGCCA se especifica con \(tau =
0\) porque el objetivo que establece el enunciado del ejercicio
es comparar sus resultados con los obtenidos mediante una correlación
canónica clásica. En RGCCA, \(tau = 0\)
orienta el modelo a maximizar la correlación entre los componentes de
los bloques, mientras que valores más próximos a 1 desplazan el criterio
hacia la maximización de la covarianza. Por ello, \(tau = 0\) permite construir una solución
conceptualmente más próxima a cancor() y evaluar de forma
directa si la interpretación principal se mantiene.
Esta elección no implica que \(tau = 0\) sea la única especificación posible de RGCCA, sino que es la más adecuada cuando el objetivo es contrastar la estabilidad de la solución canónica clásica. Una especificación alternativa, como tau = 1 o tau = “optimal”, respondería a una pregunta ligeramente distinta: si la interpretación principal se mantiene bajo una solución más regularizada o más orientada a covarianza.
# COMPARACIÓN CCA CLÁSICA vs RGCCA
# 1. Definir bloques para RGCCA
bloques_rgcca <- list(
Produccion = as.data.frame(X_prod),
Laboral_Financiero = as.data.frame(Y_labfin)
)
# Matriz de conexión entre bloques
conexion <- matrix(
c(0, 1,
1, 0),
nrow = 2,
byrow = TRUE
)
rownames(conexion) <- colnames(conexion) <- names(bloques_rgcca)
# 2. Aplicar RGCCA
# tau = 0 aproxima una lógica más cercana a CCA clásica
# ncomp = 1 porque la CCA clásica mostró que solo el primer par es relevante
rgcca_1990 <- rgcca(
blocks = bloques_rgcca,
connection = conexion,
tau = c(0, 0),
ncomp = 1,
scheme = "factorial",
scale = FALSE,
verbose = FALSE
)
summary(rgcca_1990)## Call: method='rgcca', superblock=FALSE, scale=FALSE, scale_block='inertia',
## init='svd', bias=TRUE, tol=1e-08, NA_method='na.ignore', ncomp=c(1,1),
## response=NULL, comp_orth=TRUE
## There are J = 2 blocks.
## The design matrix is:
## Produccion Laboral_Financiero
## Produccion 0 1
## Laboral_Financiero 1 0
##
## The factorial scheme is used.
## Sum_{j,k} c_jk g(cov(X_j a_j, X_k a_k) = 1.8835
##
## The regularization parameter used for Produccion is: 0
## The regularization parameter used for Laboral_Financiero is: 0
# 3. Extraer componentes RGCCA
comp_prod_rgcca <- rgcca_1990$Y$Produccion[, 1]
comp_labfin_rgcca <- rgcca_1990$Y$Laboral_Financiero[, 1]
# 4. Variables canónicas clásicas
U_prod <- as.matrix(X_prod) %*% cca_1990$xcoef
V_labfin <- as.matrix(Y_labfin) %*% cca_1990$ycoef
colnames(U_prod) <- paste0("U", 1:ncol(U_prod))
colnames(V_labfin) <- paste0("V", 1:ncol(V_labfin))
# 5. Alinear signos
# El signo de los componentes es arbitrario
if (cor(comp_prod_rgcca, U_prod[, 1]) < 0) {
comp_prod_rgcca <- -comp_prod_rgcca
}
if (cor(comp_labfin_rgcca, V_labfin[, 1]) < 0) {
comp_labfin_rgcca <- -comp_labfin_rgcca
}
# 6. Comparar correlaciones entre componentes
cor_cca_1 <- cca_1990$cor[1]
cor_rgcca_1 <- cor(comp_prod_rgcca, comp_labfin_rgcca)
cor_prod_cca_rgcca <- cor(U_prod[, 1], comp_prod_rgcca)
cor_labfin_cca_rgcca <- cor(V_labfin[, 1], comp_labfin_rgcca)
tabla_comparacion <- data.frame(
Indicador = c(
"Correlación primer par CCA clásica",
"Correlación componentes RGCCA",
"Correlación componente producción CCA-RGCCA",
"Correlación componente laboral-financiero CCA-RGCCA"
),
Valor = round(
c(
cor_cca_1,
cor_rgcca_1,
cor_prod_cca_rgcca,
cor_labfin_cca_rgcca
),
4
)
)
kable(
tabla_comparacion,
caption = "Comparación entre CCA clásica y RGCCA",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Indicador | Valor |
|---|---|
| Correlación primer par CCA clásica | 0.9704 |
| Correlación componentes RGCCA | 0.9704 |
| Correlación componente producción CCA-RGCCA | 1.0000 |
| Correlación componente laboral-financiero CCA-RGCCA | 1.0000 |
A continuación se comparan las cargas de ambos métodos
# CARGAS CCA CLÁSICA vs CARGAS RGCCA
# Cargas CCA clásica
cargas_prod_cca <- cor(X_prod, U_prod[, 1])
cargas_labfin_cca <- cor(Y_labfin, V_labfin[, 1])
# Cargas RGCCA
cargas_prod_rgcca <- cor(X_prod, comp_prod_rgcca)
cargas_labfin_rgcca <- cor(Y_labfin, comp_labfin_rgcca)
# Tabla bloque producción
tabla_cargas_prod <- data.frame(
Variable = colnames(X_prod),
Carga_CCA = round(as.vector(cargas_prod_cca), 3),
Carga_RGCCA = round(as.vector(cargas_prod_rgcca), 3)
)
# Tabla bloque laboral-financiero
tabla_cargas_labfin <- data.frame(
Variable = colnames(Y_labfin),
Carga_CCA = round(as.vector(cargas_labfin_cca), 3),
Carga_RGCCA = round(as.vector(cargas_labfin_rgcca), 3)
)
kable(
tabla_cargas_prod,
caption = "Comparación de cargas: bloque de producción",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Variable | Carga_CCA | Carga_RGCCA |
|---|---|---|
| y | -0.967 | -0.967 |
| i | -0.918 | -0.918 |
| k | -0.883 | -0.883 |
kable(
tabla_cargas_labfin,
caption = "Comparación de cargas: bloque laboral-financiero",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Variable | Carga_CCA | Carga_RGCCA |
|---|---|---|
| n | -0.959 | -0.959 |
| w | -0.557 | -0.557 |
| f | -0.527 | -0.527 |
# Gráfico de componentes RGCCA
datos_rgcca <- data.frame(
Componente_Produccion = comp_prod_rgcca,
Componente_Laboral_Financiero = comp_labfin_rgcca
)
ggplot(
datos_rgcca,
aes(
x = Componente_Produccion,
y = Componente_Laboral_Financiero
)
) +
geom_point(
size = 2.2,
alpha = 0.70,
color = "#1F4E79"
) +
geom_smooth(
method = "lm",
se = FALSE,
linewidth = 0.9,
color = "#B2182B"
) +
geom_vline(
xintercept = 0,
linetype = "dashed",
color = "gray70"
) +
geom_hline(
yintercept = 0,
linetype = "dashed",
color = "gray70"
) +
labs(
title = "Componentes RGCCA de los dos bloques",
subtitle = paste0(
"Correlación entre componentes = ",
round(cor_rgcca_1, 3)
),
x = "Componente RGCCA del bloque de producción",
y = "Componente RGCCA del bloque laboral-financiero"
) +
theme_minimal(base_size = 12)## `geom_smooth()` using formula = 'y ~ x'
La comparación con RGCCA confirma que la interpretación principal
obtenida mediante la correlación canónica clásica se mantiene. La
correlación entre los componentes RGCCA es 0.9704, igual a
la primera correlación canónica obtenida con cancor().
Además, los componentes de ambos métodos correlacionan perfectamente
entre sí (1.0000 tanto en el bloque de producción como en
el bloque laboral-financiero), lo que indica que ambas técnicas están
recuperando esencialmente la misma dimensión latente.
Las cargas de las variables también son idénticas en ambos enfoques.
En el bloque de producción, y, i y
k presentan cargas elevadas, por lo que el componente
representa una dimensión general de escala productiva. En el bloque
laboral-financiero, la variable con mayor peso sigue siendo el empleo
(n), seguida de los salarios (w) y el flujo de
caja (f) con cargas moderadas. Por tanto, la relación
principal puede interpretarse nuevamente como una asociación entre mayor
dimensión productiva de la empresa y mayor dimensión
laboral-financiera.
En consecuencia, RGCCA no modifica la interpretación sustantiva obtenida con la CCA clásica, sino que la refuerza. Las empresas con mayor producción real, mayores consumos intermedios y mayor capital real tienden también a presentar mayores niveles de empleo, salarios y flujo de caja. La conclusión principal es, por tanto, estable: ambos métodos identifican una dimensión común de tamaño o escala empresarial.
Como matiz metodológico, puede ser útil resaltar que esta coincidencia tan alta se debe en parte a que RGCCA se ha especificado con dos bloques, tau = 0 y una sola componente por bloque, una configuración muy próxima a la correlación canónica clásica. Es por ello que debe presentarse como una confirmación de consistencia, no como una interpretación alternativa claramente distinta.
Ejercicio 6 - Analisis de Correspondencias Simples
Con el conjunto de datos ca::smoke, que recoge una tabla
de contingencia entre cinco grupos de personal de una organizacion
ficticia y cuatro categorias de consumo de tabaco (none, light,
medium, heavy). Con dichos datos realizamos un Analisis de
Correspondencias Simples usando FactoMineR::CA().
Presentación de la tabla de contingencia y descripcion inicial de los perfiles observados.
# Cargar tabla contingencias
data("smoke", package = "ca")
# Convertir a matriz / data frame de contingencia
smoke_tab <- as.data.frame.matrix(smoke)
# Presentar la tabla de contingencias formateada
formattable(smoke_tab)| none | light | medium | heavy | |
|---|---|---|---|---|
| SM | 4 | 2 | 3 | 2 |
| JM | 4 | 3 | 7 | 4 |
| SE | 25 | 10 | 12 | 4 |
| JE | 18 | 24 | 33 | 13 |
| SC | 10 | 6 | 7 | 2 |
La tabla de contingencia muestra diferencias iniciales en los
perfiles de consumo de tabaco según el grupo de personal. En términos
absolutos, el grupo JE es el más numeroso, por lo que
concentra una parte importante de las frecuencias observadas,
especialmente en las categorías medium y
light. No obstante, para interpretar los perfiles conviene
atender a la distribución interna de cada fila, ya que los tamaños de
los grupos son desiguales.
El grupo SE destaca por una mayor proporción
relativa de no fumadores, con 25 casos en la categoría none
sobre un total de 51. También el grupo SC presenta un
perfil relativamente orientado hacia el no consumo. En cambio, los
grupos JM y JE muestran mayor peso relativo de la
categoría medium, y en el caso de JM
también una presencia proporcionalmente más elevada de consumo
heavy, aunque su tamaño muestral es reducido.
En conjunto, la tabla sugiere que el consumo de tabaco no se distribuye de forma homogénea entre los grupos de personal. Algunos grupos parecen más próximos a perfiles de no consumo, mientras que otros se asocian relativamente más con consumos medios o altos. Esta estructura justifica la aplicación de un Análisis de Correspondencias Simples, con el objetivo de representar gráficamente las asociaciones entre grupos de personal y categorías de consumo.
| Grupo | Perfil dominante |
|---|---|
| SM | Mayor peso de none, aunque con pocos casos |
| JM | Mayor peso de medium y proporción relevante de
heavy |
| SE | Perfil claramente orientado a none |
| JE | Perfil más asociado a medium y light |
| SC | Perfil relativamente próximo a none, con bajo
heavy |
Se aplica el Analisis de Correspondencias Simples, se calculan autovalores e inercias y se genera un biplot simetrico de las dos primeras dimensiones.
Se aplica un Análisis de Correspondencias Simples
mediante FactoMineR::CA() sobre la tabla de contingencia
entre grupos de personal y categorías de consumo de tabaco. El biplot
simétrico representa simultáneamente las filas y las columnas en el
espacio definido por las dos primeras dimensiones. La proximidad entre
puntos permitirá interpretar asociaciones relativas entre grupos y
categorías de consumo, mientras que los puntos más alejados del origen
son los que más contribuyen a la estructura de dependencia
observada.
# ANÁLISIS DE CORRESPONDENCIAS SIMPLES Y BIPLOT SIMÉTRICO
# Aplicar Análisis de Correspondencias Simples
ca_smoke <- CA(
smoke_tab,
graph = FALSE
)
ca_smoke## **Results of the Correspondence Analysis (CA)**
## The row variable has 5 categories; the column variable has 4 categories
## The chi square of independence between the two variables is equal to 16.44164 (p-value = 0.1718348 ).
## *The results are available in the following objects:
##
## name description
## 1 "$eig" "eigenvalues"
## 2 "$col" "results for the columns"
## 3 "$col$coord" "coord. for the columns"
## 4 "$col$cos2" "cos2 for the columns"
## 5 "$col$contrib" "contributions of the columns"
## 6 "$row" "results for the rows"
## 7 "$row$coord" "coord. for the rows"
## 8 "$row$cos2" "cos2 for the rows"
## 9 "$row$contrib" "contributions of the rows"
## 10 "$call" "summary called parameters"
## 11 "$call$marge.col" "weights of the columns"
## 12 "$call$marge.row" "weights of the rows"
##
## Call:
## CA(X = smoke_tab, graph = FALSE)
##
## The chi square of independence between the two variables is equal to 16.44164 (p-value = 0.1718348 ).
##
## Eigenvalues
## Dim.1 Dim.2 Dim.3
## Variance 0.075 0.010 0.000
## % of var. 87.756 11.759 0.485
## Cumulative % of var. 87.756 99.515 100.000
##
## Rows
## Iner*1000 Dim.1 ctr cos2 Dim.2 ctr cos2 Dim.3
## SM | 2.673 | -0.066 0.330 0.092 | 0.194 21.356 0.800 | 0.071
## JM | 11.881 | 0.259 8.366 0.526 | 0.243 55.115 0.465 | -0.034
## SE | 38.314 | -0.381 51.201 0.999 | 0.011 0.300 0.001 | -0.005
## JE | 26.269 | 0.233 33.097 0.942 | -0.058 15.177 0.058 | 0.003
## SC | 6.053 | -0.201 7.006 0.865 | -0.079 8.052 0.133 | -0.008
## ctr cos2
## SM 69.433 0.107 |
## JM 25.619 0.009 |
## SE 1.698 0.000 |
## JE 1.205 0.000 |
## SC 2.045 0.001 |
##
## Columns
## Iner*1000 Dim.1 ctr cos2 Dim.2 ctr cos2 Dim.3
## none | 49.186 | -0.393 65.400 0.994 | 0.030 2.934 0.006 | -0.001
## light | 7.059 | 0.099 3.085 0.327 | -0.141 46.317 0.657 | 0.022
## medium | 12.610 | 0.196 16.562 0.982 | -0.007 0.174 0.001 | -0.026
## heavy | 16.335 | 0.294 14.954 0.684 | 0.198 50.575 0.310 | 0.026
## ctr cos2
## none 0.061 0.000 |
## light 27.282 0.016 |
## medium 51.140 0.017 |
## heavy 21.517 0.005 |
# CÁLCULO INERCIA Y AUTOVALORES
# Tabla de autovalores e inercia
tabla_inercia <- data.frame(
Dimension = paste0("Dim ", 1:nrow(ca_smoke$eig)),
Autovalor = round(ca_smoke$eig[, 1], 4),
Porcentaje_inercia = round(ca_smoke$eig[, 2], 2),
Porcentaje_acumulado = round(ca_smoke$eig[, 3], 2)
)
kable(
tabla_inercia,
caption = "Autovalores e inercia explicada por dimensión",
align = "c",
row.names = FALSE
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(
0,
bold = TRUE,
color = "white",
background = "#1F4E79"
)| Dimension | Autovalor | Porcentaje_inercia | Porcentaje_acumulado |
|---|---|---|---|
| Dim 1 | 0.0748 | 87.76 | 87.76 |
| Dim 2 | 0.0100 | 11.76 | 99.51 |
| Dim 3 | 0.0004 | 0.49 | 100.00 |
# Preparar coordenadas de filas y columnas
filas <- data.frame(
Nombre = rownames(ca_smoke$row$coord),
Dim1 = ca_smoke$row$coord[, 1],
Dim2 = ca_smoke$row$coord[, 2],
Tipo = "Grupo de personal"
)
columnas <- data.frame(
Nombre = rownames(ca_smoke$col$coord),
Dim1 = ca_smoke$col$coord[, 1],
Dim2 = ca_smoke$col$coord[, 2],
Tipo = "Consumo de tabaco"
)
datos_biplot <- rbind(filas, columnas)
# Porcentaje de inercia para los ejes
inercia_dim1 <- round(ca_smoke$eig[1, 2], 2)
inercia_dim2 <- round(ca_smoke$eig[2, 2], 2)
# BIPLOT SIMÉTRICO DE LAS DOS PRIMERAS DIMENSIONES
ggplot(
datos_biplot,
aes(
x = Dim1,
y = Dim2,
color = Tipo,
shape = Tipo,
label = Nombre
)
) +
geom_hline(
yintercept = 0,
linetype = "dashed",
color = "gray70"
) +
geom_vline(
xintercept = 0,
linetype = "dashed",
color = "gray70"
) +
geom_point(
size = 3,
alpha = 0.85
) +
geom_text_repel(
size = 4,
max.overlaps = 20,
segment.size = 0.25
) +
labs(
title = "Análisis de Correspondencias Simples",
subtitle = "Biplot simétrico: grupos de personal y consumo de tabaco",
x = paste0("Dimensión 1 (", inercia_dim1, "% de inercia)"),
y = paste0("Dimensión 2 (", inercia_dim2, "% de inercia)"),
color = "",
shape = ""
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 15),
plot.subtitle = element_text(size = 11, color = "gray30"),
legend.position = "bottom",
panel.grid.minor = element_blank()
)El Análisis de Correspondencias Simples muestra que las dos primeras dimensiones explican el 99.51 % de la inercia total, por lo que el plano bidimensional resume casi completamente la estructura de desviaciones observada en la tabla de contingencia. Sin embargo, el contraste Chi-cuadrado de independencia no resulta significativo al 5 % ( \(\chi^2 = 16.44; p = 0.1718\)), por lo que no puede rechazarse estadísticamente la hipótesis de independencia entre grupo de personal y categoría de consumo de tabaco. Además, la existencia de algunas frecuencias esperadas reducidas aconseja interpretar los resultados con prudencia. Por ello, el mapa factorial debe entenderse como una representación exploratoria de perfiles relativos, útil para describir tendencias de asociación, pero no como evidencia concluyente de una relación estadísticamente significativa.
A continuación, con el fin de valorar la fiabilidad de la conclusión de no significación del contraste \(\chi^2\), se revisan las frecuencias esperadas de las celdas de la tabla de contingencia. Esta comprobación es relevante porque la aproximación asintótica del contraste puede verse afectada cuando existen frecuencias esperadas reducidas. Asimismo, se calcula un p-valor mediante simulación de Monte Carlo, como comprobación adicional de robustez.
## Warning in chisq.test(smoke_tab): Chi-squared approximation may be incorrect
## none light medium heavy
## SM 3.476684 2.564767 3.533679 1.424870
## JM 5.689119 4.196891 5.782383 2.331606
## SE 16.119171 11.891192 16.383420 6.606218
## JE 27.813472 20.518135 28.269430 11.398964
## SC 7.901554 5.829016 8.031088 3.238342
## [1] 7
# Contrastar si el p-valor cambia mucho usando simulación de Monte Carlo
chisq.test(smoke_tab, simulate.p.value = TRUE, B = 10000)##
## Pearson's Chi-squared test with simulated p-value (based on 10000
## replicates)
##
## data: smoke_tab
## X-squared = 16.442, df = NA, p-value = 0.1698
Como puede comprobarse, la prueba de frecuencias esperadas genera una advertencia, ya que 7 celdas presentan frecuencias esperadas inferiores a 5, lo que puede afectar a la validez de la aproximación asintótica del \(\chi^2\).
Para comprobar la robustez del resultado, se calculó también el p-valor mediante simulación de Monte Carlo, obteniéndose un valor muy similar (\(p = 0.1766\)). Esto refuerza la conclusión de que no existe evidencia estadística suficiente de asociación significativa entre ambas variables. En consecuencia, esto viene a reforzar que el Análisis de Correspondencias Simples debe interpretarse, en este caso, principalmente como una herramienta descriptiva y exploratoria, útil para representar perfiles relativos de consumo, pero no como una prueba concluyente de dependencia estadística.
Las posiciones de grupos pequeños como SM y JM, así
como de la categoría heavy, deben interpretarse con
especial cautela, porque están asociadas a frecuencias observadas y
esperadas reducidas. Por tanto, aunque el mapa sugiere proximidades
relativas —por ejemplo, SE connone, JE
con medium y JM con heavy, estas
asociaciones deben entenderse como patrones descriptivos, no como
evidencia inferencial fuerte.
Cálculo e interpretación de la inercia total y el porcentaje de variabilidad explicado por las dos primeras dimensiones.
La inercia total del análisis es aproximadamente 0.0852, obtenida como suma de los autovalores de las tres dimensiones (0.0748 + 0.0100 + 0.0004). La primera dimensión explica el 87.76 % de la inercia total, mientras que la segunda dimensión explica el 11.76 %. En conjunto, las dos primeras dimensiones acumulan el 99.51 % de la variabilidad asociada a la tabla de contingencia, por lo que el plano bidimensional ofrece una representación prácticamente completa de la estructura de asociación observada.
La primera dimensión es claramente la más relevante
y separa, sobre todo, la categoría none (ningún consumo),
situada en el lado negativo del eje, de las categorías de
medium (consumo medio) y heavy (consumo
elevado), situadas en el lado positivo. En cuanto a la segunda
dimensión, ayuda a matizar la diferencia entre
light (consumo bajo) y heavy (consumo
elevado), pero tiene mucho menos peso que la primera.
Identificación de qué grupos de personal aparecen mas asociados a la ausencia de consumo y cuáles se sitúan mas proximos a los consumos medios o altos.
Desde el punto de vista de los grupos de personal, el grupo
SE aparece muy próximo a none (ningún consumo),
por lo que puede interpretarse como el grupo más asociado a la ausencia
de consumo. El grupo SC también se sitúa en el lado
negativo de la primera dimensión, aunque de forma menos marcada.
En el lado positivo de la primera dimensión se sitúan los
grupos JE y JM, más próximos a categorías de consumo
más altas. El grupo JE aparece especialmente cercano a
medium (consumo medio), lo que sugiere una asociación
relativa con consumos moderados. Mientras, el grupo JM,
por su posición en el cuadrante superior derecho, aparece más próximo a
heavy (consumo elevado), por lo que puede vincularse
relativamente con los consumos más altos. La categoría
light (consumo bajo) se sitúa en una posición intermedia,
aunque más vinculada a la segunda dimensión.
Se indica qué filas y columnas contribuyen más a la construccion de las dos primeras dimensiones y se resume la interpretacion del mapa.
En cuanto a las contribuciones, la primera dimensión está construida
principalmente por la oposición entre SE y JE en las
filas, y entre none, medium y
heavy en las columnas. En concreto, SE contribuye
de forma muy importante a la Dimensión 1, seguido de
JE; en las columnas destaca especialmente none,
que es la categoría que más contribuye a este eje.
La dimensión 2 está explicada sobre todo por
JM y, en menor medida, por SM y JE en las
filas, mientras que en las columnas, las mayores contribuciones
corresponden a heavy y light.
Por tanto, el mapa resume una oposición principal entre ausencia de consumo y consumo medio/alto, y una segunda diferenciación más débil entre consumo ligero y consumo alto.