8 Reducción de dimensionalidad

Instalación/carga librerías/datos utilizados

if (!require(highcharter)) install.packages('highcharter') 
library(highcharter) 
if (!require(openxlsx)) install.packages('openxlsx') 
library(openxlsx) 
if (!require(plotly)) install.packages('plotly') 
library(plotly)
if (!require(ggplot2)) install.packages('ggplot2') 
library(ggplot2)
if (!require(tidyverse)) install.packages('tidyverse') 
library(tidyverse)
owid_country <- read.xlsx("https://ctim.ulpgc.es/AEDV/data/owid_country.xlsx",sheet=1) %>%
  as_tibble()

8.1 Introducción

Denotemos por xj, con j=1,..,n, las variables que observamos, que corresponden a columnas de nuestra tabla, (por el ejemplo los indicadores de los países en la tabla owid_country ) y por xij, con i=1,..,m, las observaciones registradas, dadas por las filas de nuestra tabla, (por ejemplo los valores de los indicadores para el país i). Diremos que una variable xj no aporta información nueva a nuestro análisis de datos si se puede obtener a partir de las otras variables. Es decir si

xij=F({xik}kj)i para una cierta función F. Por tanto, podemos quitar xj de nuestro análisis de datos sin perder información. Por ejemplo, en la tabla owid_country se guardan como indicadores el porcentaje de personas mayores de 65 años y el de personas mayores de 70 años. Estos dos indicadores están muy relacionados entre sí. Si usamos el tablero de mando Dashboard3.Rmd, observamos que el factor de correlación es de 0.99, y por tanto, se puede calcular con precisión una en función de la otra a partir de la recta de regresión, que en este caso viene dada por

aged_70_older=0.68aged_65_older0.39 Por tanto, podríamos eliminar el indicador de porcentaje de mayores de 70 años de nuestro análisis de datos sin perder información relevante. Otro ejemplo de variable que podríamos desechar son las variables que poseen un valor constante (en este caso la función F sería una constante). Este tipo de variables se consideran desechables porque no aportan nada a la hora de discriminar entre unas observaciones y otras.

En este tema vamos a estudiar como reducir el número de variables utilizadas sin perder información relevante. A este problema se le denomina reducción de la dimensionalidad.

Una limitación de las técnicas que vamos a analizar es que la relación entre las variables que vamos a estudiar es lineal, es decir sumas de variables multiplicadas por unos coeficientes, eso deja fuera dependencias más complejas como las que vienen dadas por la función logaritmo o la transformación de Box-Cox utilizadas en el cuadro de mandos Dashboard3.Rmd para comparar variables.

8.2 La matriz de correlación

Tal y como vimos en la sección anterior, una manera sencilla de eliminar variables es estudiar la correlación entre pares de ellas y si la correlación es muy cercana a 1, podemos eliminar una de ellas. Para ello se utiliza la matriz de correlación donde cada valor de la matriz corresponde al valor de la correlación entre 2 variables. Por ejemplo en la siguiente gráfica interactiva visualizamos la matriz de correlación de la tabla owid_country. Previamente hay que quitar las variables no numéricas, puesto que la correlación solo se calcula para variables numéricas y además hay que eliminar los valores NA antes de hacer el cálculo.

owid_country %>%
  select(-location,-continent,-iso_code)  %>% # eliminamos de la tabla las variables no-numéricas 
  cor(use='complete.obs') %>% # cálculo matriz correlación eliminando NA previamente
  hchart() # dibujo interactivo matriz de correlación
Created with Highcharts 9.3.1-101populationmedian_agelife_expectancyaged_65_olderaged_70_oldergdp_per_capitextreme_povertycardiovasc_death_ratdiabetes_prevalencefemale_smokersmale_smokershandwashing_facilitieshospital_beds_per_thousandhuman_development_indexpopulationmedian_agelife_expectancyaged_65_olderaged_70_oldergdp_per_capitextreme_povertycardiovasc_death_ratdiabetes_prevalencefemale_smokersmale_smokershandwashing_facilitieshospital_beds_per_thousandhuman_development_index

Figure 8.1: Matriz de correlación usando hchart

De la exploración de los valores de correlación, observamos, que aged_65_older y aged_60_older están muy relacionadas, que population y cardiovasc_death_rat están muy poco relacionadas con el resto de variables y que extreme_poverty, tiene, en general, correlación negativa con el resto de variables, es decir que cuando esta variable crece, las otras decrecen.

8.3 Análisis de componentes principales (ACP)

El análisis de componentes principales, ACP, (PCA en inglés) transforma las variables xj en otras nuevas, yj, haciendo combinaciones lineales. El objetivo del ACP es buscar las combinaciones lineales de xj que maximizan la varianza de yj en orden descendente, es decir, y1 es la combinación lineal con mayor varianza posible e yn, es la combinación lineal con menor varianza posible. Cuando la varianza de yj es muy pequeña consideramos que no aporta información relevante y la desechamos, de esta forma reducimos la dimensionalidad quedándonos solo con las variables yj con varianza significativa, que son las que denominamos componentes principales.

Fundamento teórico

Conceptos algebraícos necesarios

Definición: El producto escalar de dos vectores u y v de tamaño n se define como (u,v)=u1v1++unvn. Los vectores siempre los consideraremos en vértical, es decir podemos escribir (u,v)=uTv, donde uT es el vector traspuesto.

Definición: Una matriz cuadrada U es ortonormal (o unitaria) si se cumple UTU=Id, es decir que la traspuesta por ella misma es la matriz identidad. Por tanto, para una matriz ortonormal la inversa es la traspuesta.

Teorema: Los vectores columna de una matriz ortonormal U=(u1,,un) (con uk=(u1k,,unk)T) cumplen que (uk,uk)=1 y si kk entonces (uk,uk)=0 es decir los vectores son perpendiculares.

Definición: el número real λk es un autovalor de la matriz cuadrada A, si existe un vector uk=(u1k,,unk)T, no nulo, denominado autovector, tal que Auk=λkuk. Si uk es autovector, entonces, al multiplicarlo por cualquier número diferente de cero, sigue siendo autovector. Por ello siempre podemos ajustar el autovector para que cumpla (uk,uk)=1.

Teorema: Si la matriz cuadrada A de dimensión n es simétrica, entonces posee n autovalores que podemos ordenar en forma decreciente, λ1λ2λn y además posee una matriz ortonormal de autovectores U=(u1,u2,..,un) formada por los autovectores uk en columnas.

Teorema: Si X es una matriz cualquiera de tamaño mxn, entonces A=XTX es cuadrada de tamaño nxn, simétrica y todos sus autovalores son mayores o iguales que cero.

Teorema: Si yj=(y1j,,ymj)T es una variable con m observaciones y media cero (y1j++ymj=0) entonces su varianza muestral Var(yj) y desviación estándar muestral σj son:

Var(yj)=yTjyjm1=mi=1y2ijm1 σj=mi=1y2ijm1

Fundamento matemático del ACP

Consideremos n variables xj (los vectores columna de nuestra tabla), y sus respectivas observaciones en forma de matriz X=xij de tamaño mxn. Vamos a sustituir X por una nueva colección de variables Y=XU donde U es una matriz ortonormal de tamaño n. Es decir, las nuevas variables yj=Xuj son combinaciones lineales de las anteriores obtenidas a partir de una matriz ortonormal que se pueden expresar como :

y1=u11x1+u21x2++un1xny2=u12x1+u22x2++un2xnyn=u1nx1+u2nx2++unnxn

El análisis por componentes principales es una técnica para elegir la matriz ortonormal U de tal manera que las variables yj=Xuj (las componentes principales) estén ordenadas de forma descendente en función de su varianza, es decir, la primera componente principal y1=Xu1 corresponde a la combinación lineal de las variables originales que tiene la mayor varianza posible, y2=Xu2 sería la combinación ortogonal a la anterior ((u1,u2)=0) con la mayor varianza posible, y3=Xu3 sería la combinación ortogonal a las anteriores ((u1,u3)=0 y (u2,u3)=0) con la mayor varianza posible, y así sucesivamente. Cuanto mayor es la varianza de yj más información contiene la variable en términos, por ejemplo, de distinguir entre las diferentes observaciones. Nótese que una variable con valor constante (es decir varianza cero) no aporta ninguna información para discernir entre unas observaciones y otras. Dado que las variables xj pueden tener magnitudes y varianzas muy distintas, para equilibrar la aportación de cada variable, primero se normalizan, es decir se le resta su media y se divide por la desviación típica. Es decir, consideraremos que las variables xj tienen media cero y desviación típica 1. Si la media de cada variable xj es cero, la media de yj=Xuj que es una combinación lineal de xj también tendrá media cero y por tanto su varianza será

Var(yj)=yTjyjm1=uTjXTXujm1 la clave para maximizar la varianza son los autovectores de la matriz A=XTX. Si λj son los autovalores ordenados de A en forma descendente, es decir λ1λ2λn y uj sus correspondientes autovectores, entonces

Var(yj)=uTjXTXujm1=uTjλjujm1=λjuTjujm1=λjm1 por tanto, el procedimiento para calcular las componentes principales de X se puede dividir en los siguientes pasos:

  • Normalizar la variables originales xj para que tenga media cero y varianza 1.

  • Calcular los autovalores λj (ordenados de forma descendente) y autovectores uj de la matriz XTX

  • Las componentes principales son yj=Xuj y su varianza es λjm1

Ejemplo de ACP

Para ilustrar el proceso del cálculo de las componentes principales vamos a tomar un caso sencillo extraído de la tabla owid_country donde usaremos 3 variables (x1=human_development_index, x2=aged_65_older y x3=aged_70_older ) y las observaciones de 5 países (China, Canada, Nigeria, Brazil y Germany). Para calcular el ACP la tabla solo puede tener variables numéricas. Para conservar el nombre de los países se usa la función column_to_rownames que asigna los países como nombres de cada fila, de esta manera se gestionan como un elemento externo a la tabla y no interfieren con el cálculo del ACP.

data <- owid_country %>%
  filter( # filtramos los 5 países que se usarán 
    location == "China" |
    location == "Canada" |
    location == "Nigeria" |
    location == "Brazil" | 
    location == "Germany" 
  ) %>%
  column_to_rownames(var="location") %>%
  select(human_development_index,aged_65_older,aged_70_older) 

A continuación imprimimos las variables normalizadas usando la función scale.

data %>% scale() 
##         human_development_index aged_65_older aged_70_older
## Brazil               -0.1409164    -0.4824390    -0.4932478
## Canada                0.8552170     0.6718445     0.5253853
## China                -0.1652124    -0.1964691    -0.3389525
## Germany               0.9645488     1.2836202     1.4415691
## Nigeria              -1.5136370    -1.2765565    -1.1347540
## attr(,"scaled:center")
## human_development_index           aged_65_older           aged_70_older 
##                  0.7882                 12.0762                  7.8380 
## attr(,"scaled:scale")
## human_development_index           aged_65_older           aged_70_older 
##               0.1646366               7.3049644               5.6320575

Por tanto la matriz X está definida por:

x1x2x3Brazil0.14091640.48243900.4932478Canada0.85521700.67184450.5253853China0.16521240.19646910.3389525Germany0.96454881.28362021.4415691Nigeria1.51363701.27655651.1347540

Calculamos ahora la matriz XTX: XTX=(4.0000000413.8453730113.6828936863.8453730113.9999998443.9565377033.6828936863.9565377034.000000014) A continuación, a mano, o usando cualquier software de cálculo matemático, calculamos la matriz ortonormal U con los autovectores de XTX

U=(0.57084084830.77904104930.25929861060.58455683680.16384313100.79463748530.57657092410.60518631180.5489221234)

Simultáneamente se calculan los autovalores de XTX que son: λ1=11.65763091,λ2=0.3302642764, y λ3=0.0210471581 y por tanto las desviaciones típicas de yj son σj=λj/4, es decir: σ1=1.70716365, σ2=0.287343121, σ3=0.072538194. Las componentes principales quedan entonces como:

y1=0.5708408483x1+0.5845568368x2+0.5765709241x3y2=0.7790410493x10.163843131x20.6051863118x3y3=0.2592986106x10.7946374853x2+0.5489221234x3

y, por tanto, la nueva tabla de valores, Y=XU, da como resultado:

y1y2y3Brazil0.64684619300.2677714730.0760700573Canada1.1838459810.23821605070.02372062965China0.40458749830.10861228440.07277616024Germany2.1321196470.33130713020.02140260729Nigeria2.2645318210.28329275480.000975899266

Cálculo ACP con prcomp

La función prcomp realiza de forma automática todo el proceso para el cálculo de las componentes principales. Vamos a utilizarla para el ejemplo anterior:

pca1 <- prcomp(data,scale = TRUE)

La función prcomp devuelve una estructura (en este ejemplo la hemos llamado pca1) que almacena, en el campo sdev, la desviación típica de las componentes principales yj, en el campo rotation la matriz de autovectores U y en el campo x las componentes principales Y=XU. A continuación se imprimen estos resultados. Se puede observar que los resultados son los mismos que hemos calculado para este ejemplo en la sección anterior.

pca1$sdev # desviaciones típicas de las componentes principales
## [1] 1.70716366 0.28734312 0.05501071
pca1$rotation # matriz U de autovectores de X^TX
##                               PC1        PC2        PC3
## human_development_index 0.5708408  0.7790411  0.2592986
## aged_65_older           0.5845568 -0.1638432 -0.7946375
## aged_70_older           0.5765709 -0.6051863  0.5489222
pca1$x # matriz Y=XU que determina las nuevas variables (componentes principales)
##                PC1        PC2           PC3
## Brazil  -0.6468462  0.2677714  0.0760700585
## Canada   1.1838460  0.2382161 -0.0237206224
## China   -0.4045875  0.1086123 -0.0727761422
## Germany  2.1321196 -0.3313071  0.0214026069
## Nigeria -2.2645318 -0.2832927 -0.0009759008

Visualización ACP

El objetivo principal del ACP es reducir la dimensionalidad quedándonos con las primeras componentes principales que acumulan mayor varianza y desechando el resto. El criterio para decidir con cuantas componentes principales nos quedamos se basa justamente en el análisis de su varianza. Normalmente se analiza la varianza de las componentes principales yj en términos relativos, es decir el porcentaje que representa la varianza de yj respecto a la varianza global dada por la suma de todas las varianzas (jVar(yj)). Para visualizar esto, utilizamos un gráfico de barras interactivo, creado con plotly, con el porcentaje de varianza explicada por cada componente principal.

p <- tibble(
  label=paste("PC",1:length(pca1$sdev)), # creación etiquetas para el eje horizontal
  varPercent = pca1$sdev^2/sum(pca1$sdev^2) * 100 # cálculo porcentaje de varianza explicada
) %>%
  ggplot(aes(x=label,y=varPercent)) + # creación gráfico de barras interactivo
    geom_bar(stat = "identity") +
    labs(x= "Componentes Principales", 
         y= "Porcentaje varianza explicada")
ggplotly(p) 
PC 1PC 2PC 30255075100
Componentes PrincipalesPorcentaje varianza explicada

Figure 8.2: Porcentaje varianza explicada por cada componente principal usando ggplotly

Para decidir con cuantas componentes principales nos quedamos podemos, por ejemplo, quedarnos con las componentes principales que acumulen más de un 90% de la varianza explicada. Otro criterio, sería, por ejemplo, eliminar las componentes principales con la varianza explicada inferior al 2%. Para este ejemplo, con el primer criterio, solo nos quedaríamos con la primera componente principal, y con el segundo criterio con las dos primeras.

Un gráfico habitual para visualizar los resultados del ACP es hacer un gráfico de dispersión donde se pone el resultado de los valores de las dos primeras componentes principales para cada observación, es decir se representan los puntos (yi1,yi2) para i=1,,m, de esta manera se aprecia como están distribuidos los valores de las dos primeras componentes. Además, para visualizar la contribución de cada variable xj a las dos primeras componentes, se dibuja, para cada j un segmento que va desde el (0,0) hasta el (uj1,uj2) para j=1,,n. uj1 es el coeficiente de xj en la combinación lineal de la primera componente y1 y uj2 es el coeficiente de xj en la combinación lineal de la segunda componente y2. Si este segmento se alinea con el eje x, significa que xj aporta poco a la segunda componente, y si además el segmento es de gran magnitud, significa que xj aporta a la primera componente más que el resto de variables. Para realizar este gráfico utilizaremos la función hchart:

hchart(pca1)
Created with Highcharts 9.3.1BrazilBrazilCanadaCanadaChinaChinaGermanyGermanyNigeriaNigeriaobservationsaged_65_olderaged_70_olderhuman_development_index0123-1.25-1-0.75-0.5-0.2500.250.50.751

Figure 8.3: Gráfica ilustrativa de las dos primeras componentes principales usando hchart

A continuación vamos a reproducir el experimento, pero tomando todos los indicadores y para todos los países. Primero preparamos los datos y calculamos el ACP. Como el cálculo del ACP solo permite variables numéricas, tenemos que eliminar las variables no numéricas de la tabla. Para conservar los nombres de los países, en lugar de gestionarlo como variable, lo gestionamos como nombre de cada registro, de esta manera no interfiere en el cálculo del ACP

data <- owid_country %>%
  na.omit() %>% # quitamos los registros con algún NA
  column_to_rownames(var="location") %>% # asignamos el campo location como nombre de las filas 
  select(-continent,-iso_code) # eliminamos las variables no-numéricas 
pca2 <- prcomp(data,scale = TRUE) # calculamos el ACP 

Ahora realizamos la gráfica de barras con el porcentaje de varianza explicada por cada componente principal. Por defecto, la gráfica de barras ordena por orden alfabético las etiquetas del eje x, para que use el orden usado al crear las etiquetas usamos la función fct_inorder que impide que se cambie el orden en el vector de etiquetas.

p <- tibble(
  label=fct_inorder(paste("PC",1:length(pca2$sdev))),
  varPercent = pca2$sdev^2/sum(pca2$sdev^2) * 100
) %>%
  ggplot(aes(x=label,y=varPercent)) +
    geom_bar(stat = "identity") +
    labs(x= "Componentes Principales", 
          y= "Porcentaje varianza explicada"
    )
ggplotly(p)
PC 1PC 2PC 3PC 4PC 5PC 6PC 7PC 8PC 9PC 10PC 11PC 12PC 13PC 1402040
Componentes PrincipalesPorcentaje varianza explicada

Figure 8.4: Porcentaje varianza explicada por cada componente principal usando ggplotly

Por último presentamos la gráfica que ilustra el resultado para las dos primeras componentes

hchart(pca2)
Created with Highcharts 9.3.1ArmeniaArmeniaBeninBeninBurkina FasoBurkina FasoBangladeshBangladeshBosnia and HerzegovinaBosnia and HerzegovinaColombiaColombiaComorosComorosCosta RicaCosta RicaDominican RepublicDominican RepublicAlgeriaAlgeriaEcuadorEcuadorEgyptEgyptEthiopiaEthiopiaGhanaGhanaGambiaGambiaHaitiHaitiIndonesiaIndonesiaIndiaIndiaKazakhstanKazakhstanKenyaKenyaKyrgyzstanKyrgyzstanLaosLaosLiberiaLiberiaMoldovaMoldovaMexicoMexicoMyanmarMyanmarMongoliaMongoliaMozambiqueMozambiqueMalawiMalawiNigerNigerNepalNepalPakistanPakistanParaguayParaguayEl SalvadorEl SalvadorTogoTogoThailandThailandTimorTimorTunisiaTunisiaTanzaniaTanzaniaUgandaUgandaVietnamVietnamYemenYemenSouth AfricaSouth AfricaZambiaZambiaZimbabweZimbabweobservationsaged_65_olderaged_70_oldercardiovasc_death_ratdiabetes_prevalenceextreme_povertyfemale_smokersgdp_per_capithandwashing_facilitieshospital_beds_per_thousandhuman_development_indexlife_expectancymale_smokersmedian_agepopulation-0.4-0.3-0.2-0.100.10.20.30.4-0.6-0.4-0.200.20.4

Figure 8.5: Gráfica ilustrativa de las dos primeras componentes principales usando hchart

Referencias

[Ke23] Zoumana Keita. Principal Component Analysis in R Tutorial, 2023.

[Ku22] Joshua Kunst. Hchart Function, 2022.

[UGA] Data Analysis in the Geosciences (Principal Components Analysis), University of Georgia.