Analyse en composantes principales

et autres réductions dimensionnelles

en langage R

Parlons d'abord statistiques multivariées.

Lorsque l'on a plusieurs variables, que faire ? Comment simplifier leur approche ? Faisons un bref récapitulatif :

L'essentiel de cette page !

L'analyse en composante principale (ACP ou PCA en anglais) permet de réduire le nombre de dimensions d'un jeu de données multidimensionnel.

Cette page explique comment réaliser une ACP avec R mais aussi comment visualiser les résultats.

Elle dirige vers d'autres pages pour la mise en forme.

Voici une démarche simplifiée pour (bien) mener une ACP :

1) Il n'est pas obligatoire de faire l'ACP sur toutes les variables disponibles. Il est bon de tester les corrélations/khi2/effet sur la moyenne AVANT afin de ne retenir QUE les variables qui semblent avoir un effet (effet putatif) sur le paramètre qui nous intéresse.

2) Penser à centrer-réduire les données lors de l'ACP si les variables mesurées ne sont pas de même nature

3) Interprétation du diagramme de pareto : les 2 composantes (si on en souhaite 2) doivent intégrer 80% de la variabilité de l'ensemble du jeu de données.

4) Biplot = graphique classique "la composante 2 en fonction de la composante 1" (y = f(x)) + "CERCLE" . Le cercle avec les flèches permettent grosso modo de voir la participation de chaque variable à chaque composante. 2 flèches de directions opposées semblent avoir des effets opposés. Les composantes sont des sortes de super-variables qui regroupent la variabilité de toutes les variables.

5) Interprétation de la distribution des points : on peut faire 1) du regroupement ou 2) des régressions : l'interprétation, le "comme comprendre ces graph" dépend de l'objectif souhaité.

Si en revanche, l'ACP ne donne pas des résultats satisfaisants, il existe d'autres solutions ! Les plus basiques sont expliquées dans cette page. 

Si vous voulez être formé (en Aquitaine) aux autres solutions, plus performantes, n'hésitez-pas à demander un devis !

L'analyse en composantes principales permet, d'après Wikipedia (septembre 2017) de transformer des variables liées entre elles (dites « corrélées » en statistique) en nouvelles variables décorrélées les unes des autres. Ces nouvelles variables sont nommées « composantes principales », ou axes principaux. Elle permet à l'analyste de réduire le nombre de variables et de rendre l'information moins redondante.

Cette page s'intéresse ici non pas à l'ACP en elle-même (présentation succincte rubrique 1) mais à la représentation graphiques des composantes dans le cadre d'une étude ou d'une publication.

1. Réaliser une analyse en composantes principales

Superposer les variables d'une ACP structurant les composantes visibles en scatter-plot R project CRAN

L'analyse des données dans le cadre des statistiques multivariées se fait par plusieurs méthodes dont l'ACP (Analyse en Composantes Principales).

Voici comment réaliser une telle étude.

Réaliser une ACP (exemple d'ACP pas à pas avec R par défaut ou les librairies ade4 et FactoMineR)

Étape 1 - Ouvrir des données

data = read.table("cancers.txt",sep="\t",header=T)

colnames(data)

rownames(data) = data[,1] # On donne à chaque ligne le nom de son cancer (Cancer 1, Cancer 2)

x = data[1:7,-1] ;x # On exclue la colonne 1 qui contenait les noms des cancers.

Dans cet exemple, on ouvre des données simulées 

présentant des résultats moyens de concentration de 10 marqueurs sanguins (M1 à M10) pour 7 catégories de cancers (Cancer 1 à 7).

Chaque catégorie de cancer présente ainsi un taux moyen de marqueurs variable d'une catégorie à l'autre.

On cherche à voir s'il existe des cancers qui présenterait une signature sanguine caractéristique.

Pour cela on réalise une ACP.

Étape 2 - Réaliser l'ACP

Plusieurs librairies permettent de réaliser des ACP avec R.

1) La commande par défaut prcomp() (installé sur R par défaut), renvoie une acp dont la description est délicate à obtenir

2) ade4 (dudi.pca()) permet d'obtenir facilement une acp et sa description. On peut facilement voir un diagramme de Pareto pour sélectionner les composantes qui ont du poids

3) factomineR (PCA()) est un package très performant qui permet d'aller bien au-delà de l'ACP.

Méthode par défaut de R (prcomp())

acp1 <- prcomp(x,  scale = F)

acp1

Avec la librairie ade4

install.packages("ade4")

library(ade4)

acp2 <- dudi.pca(x , scannf= F,scale=FALSE,,nf=3) 

acp2

Avec la librairie FactoMineR

install.packages("FactoMineR")

library(FactoMineR)

acp3 = PCA(x, scale.unit=F, ncp=5, graph=T)

acp3

center = T ==> permet de centrer les données (soustraction de la moyenne)scale = T ==> permet de normer les données ==> center et scale = T donnent des données centrées-réduites.
center = T ==> permet de centrer les données (soustraction de la moyenne)scale = T ==> permet de normer les données ==> center et scale = T donnent des données centrées-réduites.scalenf = F ==>  permet d'afficher un graphique indiquant le poids de chaque composante sur la variabilité totale (diagramme de Pareto) ==> On peut alors sélectionner le nombre de composantes que l'on désire conserver.nf = 3 == indique le nombre de composantes à conserver
scale.unit  = T ==> permet de normer les données ==> center et scale = T donnent des données centrées-réduites.graph = T ==> permet d'afficher automatiquement les graphs de visulisation des donnéesncp = 3 == indique le nombre de composantes à conserver

Étape 3 - Contrôler la pertinence de l'ACP par un diagramme de Pareto

Le principe de Pareto suppose que la variabilité globale se concentre sur les premières composantes.

Afin de pouvoir travailler sur seulement quelques composantes, il faut s'assurer que ce principe de Pareto est respecté.

Il est possible de contrôler la pertinence de l'ACP, non déformation du nuage en s'assurant que les composantes retenues couvrent bien 95%, 97.5% ou plus selon son choix de la variabilité du nuage.

La démarche est le suivante :




Dans cet exemple, comme les 2 premières composantes couvrent 95% de la variabilité, on peut réduire ainsi le nombre de dimensions des données à seulement 2 au lieu de 10.

Diagramme de Pareto d&#39;analyse d&#39;une ACP avec R project CRAN

Pareto de contrôle de l'ACP

Les 2 premières composantes couvrent ici plus de 95% de la variabilité.

Méthode par défaut de R

# Installer mon package {KefiR} pour avoir la fonction pareto()


pareto(acp1$sdev)

# Attention : il ne s'agit pas des variances mais des déviations standards : prcomp() ne permet pas une analyse pertinente

Avec la librairie ade4

# Installer mon package {KefiR} pour avoir la fonction pareto()


pareto(acp2$eig, h=95)


# h = 95 : afficher un seuil de cumul de la variance à 95%

Avec la librairie FactoMineR

# Récupérer et copier-coller avant dans la console la fonction pareto()


pareto(acp3$eig[,2], h=95)


# h = 95 : afficher un seuil de cumul de la variance à 95%

Étape 4 - Afficher les données en 2 dimensions - Ex : Axe1 = Composante 1 & Axe2 = Composante2

Une fois que l'on sait sur quelles composantes s'attarder, la première envie est de voir comment vont se répartir les points sur un nombre réduit de dimensions.

Chaque librairie possède ses propres outils de visualisations. Tous sont très limités dans la mise en forme.

Le mieux est donc d'utiliser les commandes de base R comme plot().

Vous trouverez sur ce site une page dédiée aux représentations graphiques avec ade4 (commande s.label()) ou FactoMineR.

On peut aussi se référer aux aides de ces différents packages.

Courbe sur les composantes 1 et 2 (on peut aussi bien représenter d'autres composantes en remplaçant les 1 et 2 par 3 et 4 dans axeX et axeY

# Avec des données ACP issues de prcomp() de R par défaut

axeX <- acp1$x[,1] ; axeY <- acp1$x[,2] 


# Avec des données ACP issues de dudi.pca() de ade4

axeX <- acp2$li[,1] ; axeY <- acp2$li[,2] 


# Avec des données ACP issues de PCA() de FactoMineR

axeX <- acp3$ind$coord[,1] ; axeY <- acp3$ind$coord[,2] 

Tracer le graphique

plot(axeX,axeY,pch=16);grid()

text(axeX,axeY,rownames(x))

Étape 5 - (Optionnelle) -  Visualiser la corrélation et l'influence des différentes variables avec les composantes (diagramme circulaire, radial, en radiant)

Un tel diagramme permet de calculer comment chaque variable intervient dans une composante.

Il est utile pour comprendre le lien entre variables et va donner des résultats équivalents à un clustering sur variables.

Dans cet exemple qui met en relation composante 1 (horizontale) et composante 2 (verticale), on voit que les composantes M4 et M5, longues, jouent fortement.

M7 se superpose à la composante 1 sans en être la seule part.

A) Récupérer les données nécessaire à tracer le diagramme

Méthode par défaut de R

# Récupérer la description donnant la corrélation de chaque variable par composante

# 1 et 2 pour composantes 1 et 2

poids_var_x <- acp1$rotation[,1]

poids_var_y <- acp1$rotation[,2]

vecnames <- rownames(acp1$rotation)



Avec la librairie ade4

# Récupérer la description donnant la corrélation de chaque variable par composante


poids_var_x <- acp2$c1[,1]

poids_var_y <- acp2$c1[,2]

vecnames <- rownames(acp2$c1)

Avec la librairie FactoMineR

# Récupérer la description donnant la corrélation de chaque variable par composante


# Pas de contenu prêt à l'emploi mais des fonctions font directement le diagramme

B) Optionnel : filtrer les variables pour n'afficher que celles qui ont un poids suffisant

#OPTIONNEL

poids_var_global <- sqrt(poids_var_x^2+poids_var_y^2)

barplot(poids_var_global,names.arg=vecnames)

# FILTRER : Ex : les variables dont le poids est > 0.25

seuil = 0.25   ; abline(h=seuil,col="red",lwd=2)

poids_var_x <- poids_var_x[poids_var_global>seuil]

poids_var_y <-poids_var_y[poids_var_global>seuil]

vecnames <- vecnames[poids_var_global>seuil]


On voit ici que le seuil de 0.25 va éliminer les variables M2, M6 et M9 de l'affichage.

C) Tracer le diagramme

# Tracer un fond blanc orthonormé

plot(c(-1,1),c(-1,1),type="n",xlab="",ylab="",asp=1,axes=F)  ; grid()

# Tracer le cercle

install.packages('plotrix') ; library("plotrix")  ;draw.circle(0,0,1,lwd=3) 

# Tracer les flèches

arrows(0,0,poids_var_x,poids_var_y,length=0.1,angle=17,lwd=2)

# Calculer la position du nom de chaque vecteur // aux pointes des flèches

poids_x_bin <- poids_var_x ; poids_x_bin[poids_x_bin<0]<- -1 ;  poids_x_bin[poids_x_bin>0]<- 1

poids_y_bin <- poids_var_y ; poids_y_bin[poids_y_bin<0]<- -1 ;  poids_y_bin[poids_y_bin>0]<- 1

poids_x <- poids_var_x^2 ; poids_y <- poids_var_y^2; poids_z <- poids_x + poids_y

poids_x <- poids_x / poids_z * poids_x_bin * 0.08 ; poids_y <- poids_y / poids_z * poids_y_bin * 0.08

# Tracer les noms des vecteurs

text(poids_var_x+poids_x,poids_var_y+poids_y,vecnames,cex=0.75,col="red")

Étape 6 - Combiner les étapes 4 et 5 pour visualiser la distributions des résultats selon 2 composantes et superposer la projection de chaque variable. (biplot)

On peut obtenir directement ce graphique avec la fonction biplt() de {KefiR}.

Il suffit tout simplement de réaliser le graphique de l'étape 4 puis de faire le graphique de l'étape 5 en séparant les 2 de la ligne suivante :

par(new = T)

# Graphique issu de l'étape 4 - données de prcomp()

axeX <- acp1$x[,1] ; axeY <- acp1$x[,2] 

plot(axeX,axeY,pch=16,xlim=c(-100,70),ylim=c(-40,70),cex=3,col=c(1:100),

xlab="Composante 1",ylab="Composante 2");grid()

text(axeX,axeY-5,rownames(x))

# Pour superposition des 2 graphiques

par(new = T)

# Graphique issu de l'étape 5 

plot(c(-1,1),c(-1,1),type="n",xlab="",ylab="",asp=1,axes=F) 

arrows(0,0,poids_var_x,poids_var_y,length=0.1,angle=17,lwd=2)

draw.circle(0,0,1,lwd=3)

text(poids_var_x+poids_x,poids_var_y+poids_y,vecnames,cex=0.75,col="red")

Le même diagramme, nettement mois esthétique, peut s'obtenir directement avec la fonction biplot()

biplot(prcomp(dataframe))

Étape 7 - Tracer des ellipses, colorer par catégories

Si les catégories ne sont pas encore définies, on peut réaliser un clustering pour identifier ces catégories (knn, k-means, clustering hiérarchique ascendant (CAH)...).

Sinon, on peut les colorer ou les entourer directement avec une ellipse.

Reprenons les étapes 1 à 6 sur une nouvelle étude, complète.

Voici des résultats obtenus par différents étudiants n'ayant pas tous le même bac. On essaye de voir s'il y a un résultats aux examens et type de bac.

Données téléchargeables ici

# Étape 1 et 2 Code pour faire l'ACP - data_bac.txt

data_bac = read.table(file.choose(),sep="\t",header=T)

colnames(data_bac)

library(ade4)

acp <- dudi.pca(data_bac[,-c(1,2)] , scannf= F,scale=T,center=T,nf=3) ; acp


Biplot maison paramétrable manuelle pour superposer individus sur 2 composantes et la description de ces composantes ACP PCA R project CRAN

Biplot maison, mise en forme fine. Tout est paramétrable manuellement.

# Visualiser les étudiants par bac

plot(acp$li[,1],acp$li[,2],pch=16,col=data_bac$bac,xlab="Composante 1",ylab="Composante 2")


mydi = dist(data_bac[,-c(1,2)])

mytree = cutree((hclust(mydi)), k=5) # 3 catégories ; print(mytree)

# Il suffit de déclarer les catégories dans le paramètres col

plot(acp$li[,1],acp$li[,2], col = mytree,pch=16,cex=1.2,

main="Regroupement des différents types d'étudiant",xlab="Composante 1",ylab="Composante 2")

legend(x="topright", legend=unique(mytree), col=unique(mytree), pch=16,bg="white")

x_ell <- acp$li[,1][mytree==3] ; y_ell <- acp$li[,2][mytree==3] ; xy_ell <- data.frame(x_ell,y_ell)

install.packages("ellipse") ; library("ellipse")

# Ajouter une ellipse (intervalle de confiance à 95%)

lines(ellipse(cov(xy_ell),centre=colMeans(xy_ell),level=0.95),type="l", lty=1, col="green")

# Une fois le graphique tracé

par(new = T) # pour ajouter par-dessus les vecteurs

plot(c(-1,1),c(-1,1),type="n",xlab="",ylab="",asp=1,axes=F) # pour tracer un graphique blanc orthonormé

# optionnel : tracer un cercle

 install.packages('plotrix') ; library("plotrix") ; 

draw.circle(0,0,1)

# Tracer les vecteurs ==> Remarque acp$c1 contient ces descriptions pour les acp faites avec ade4.

arrows(0,0,acp$c1[,1],acp$c1[,2],length=0.1,angle=17)

# Récupérer les noms des vecteurs

vecnames <- rownames(acp$c1)

# Tracer les noms des vecteurs - Remarque, cf. ETAPE 4 ci-dessus pour bien localiser la légende.

text(acp$c1[,1]+0.07,acp$c1[,2]+0.07,vecnames,cex=0.75)

2. Interagir avec des résultats d'ACP en 2D ou 3D

2.0. Prenons un nouvel exemple plus complexe qui justifierait une meilleure interaction

Il s'agit de reprendre l'exemple d'ACP réalisé Étape 7 ci-dessus.


Voici des résultats obtenus par différents étudiants n'ayant pas tous le même bac. On essaye de voir s'il y a un résultats aux examens et type de bac.

Données téléchargeables ici


# Code pour faire l'ACP

data_bac = read.table(file.choose(),sep="\t",header=T)

colnames(data_bac)

library(ade4)

acp <- dudi.pca(data_bac[,-c(1,2)] , scannf= F,scale=FALSE,,nf=3) ; acp

Affichage manuel de données d&#39;ACP R project CRAN (ellipse de confiance, confidenceà

Une catégorie particulière d'étudiants est ainsi entourée en vert, celle des bons élèves qui n'auront pas besoin de soutien.

2.1. Visualiser les résultats en 3D ou établir le lien des composantes avec une autre variable

Étape A - Graphique 3D à 3 axes

Illustrer 3 composantes en 3D, entourer d'un ellipsoïde les points correspondant à une catégorie. C'est possible !  

cf. Réalisation de graphiques 3D avec R et de gif animés.


# Début du graphique

# Installer la librairie 3D nécessaire à la visualisation

install.packages("rgl")  ; library(rgl) # installer puis lancer la librairie

# Tracer le graphique 3D (les bac S)

plot3d(acp$li[,1][data_bac$bac=="S"],acp$li[,2][data_bac$bac=="S"],acp$li[,3][data_bac$bac=="S"],

col="green",type="p",xlab="Dim1",ylab="Dim2",zlab="Dim3")

# Fin du graphique

# Colorer et ajouter d'autres catégories de bacs

points3d(acp$li[,1][data_bac$bac=="STAV"],acp$li[,2][data_bac$bac=="STAV"],acp$li[,3][data_bac$bac=="STAV"],col="red",radius=0.02)

points3d(acp$li[,1][data_bac$bac=="ST2S"],acp$li[,2][data_bac$bac=="ST2S"],acp$li[,3][data_bac$bac=="ST2S"],col="blue",radius=0.02)

spheres3d(acp$li[,1][data_bac$bac=="STL"],acp$li[,2][data_bac$bac=="STL"],acp$li[,3][data_bac$bac=="STL"],col="black",radius=0.02)

# Tracer un ellipsoïde noir pour le bac STL

x = acp$li[,1][data_bac$bac=="STL"] ; y = acp$li[,2][data_bac$bac=="STL"] ; z = acp$li[,3][data_bac$bac=="STL"]

ellipse <- ellipse3d(cov(cbind(x,y,z)), centre=c(mean(x), mean(y), mean(z)), level = 0.95)

plot3d(ellipse, col = "black", alpha = 0.1, add = TRUE, type="shade")

# Tracer un ellipsoïde rouge pour le bac STAV

x = acp$li[,1][data_bac$bac=="STAV"] ; y = acp$li[,2][data_bac$bac=="STAV"] ;z = acp$li[,3][data_bac$bac=="STAV"]

ellipse <- ellipse3d(cov(cbind(x,y,z)), centre=c(mean(x), mean(y), mean(z)), level = 0.95)

plot3d(ellipse, col = "red", alpha = 0.1, add = TRUE, type="shade")

# Réaliser une série de 90 images pour faire un gif animé avec un logiciel comme GIMP, photofiltre, Easy GIF Animator ou gif animator ou Photoscape.

for (i in 1:90) {     rgl.viewpoint(i, 20) ;

    filename <- paste("pic", formatC(i, digits = 1, flag = "0"), ".png", sep = "") 

    rgl.snapshot(filename) } ;getwd()

Étape B - Régression 3D couplée au graphique pour mettre en relation une variable y avec les composantes x1 et x2

cf. L'utilisation de la librairie plotly. Régression 3D, nappes et graphiques 3D interactifs : faire appel à plotly !!!

2.2. Construire sa propre interface pour cliquer sur les points pour les identifier, etc.

  Développer une interface pour pouvoir afficher la légende d'un point en cliquant dessus.

Il est relativement simple de reprendre le script disponible aux liens ci-dessous pour développer une interface pour lire ses données d'ACP par interaction avec les graphiques.

ACP et interface simple R

# à partir d'une acp faite avec ade4

plot(acp$li[,1],acp$li[,2],pch=16, col="#AA1256")

loc_punct(acp$li[,1],acp$li[,2])

La commande loc_punt n'est pas installée sous R, il faut aller récupérer son code :

cf. La commande de localisation d'un point par simple click.

ACP et interface R

# à partir d'une acp faite avec ade4

plot_id_point(acp$li[,1],acp$li[,2],pch=16, col="#12AA56",col_point="red",labels=c(1:100))

La commande plot_id_point n'est pas installée sous R, il faut aller récupérer son code :

 cf. Commande interactive pour cliquer sur chaque point de l'ACP pour afficher ses coordonnées et caractéristiques

2.3. Utiliser la librairie plotly pour créer automatiquement des graphiques html interactifs

cf. Le lien suivant. plotly permet aussi de rendre les ggplot interactif.

3. Aller plus loin dans la mise en forme des résultats d'ACP...

Résultats d&#39;ACP (Analyse en Composantes Principales) avec le logiciel R project CRAN

Simple pour réaliser des ACP, la librairie ade4 ne permet pas de faire de belles mise en forme.

Voir la page d'aide.

Superposer les variables d&#39;une ACP structurant les composantes visibles en scatter-plot R project CRAN

Si factoextra ne permet pas de faire des ACP, elle permet de reprendre les résultats d'autres commandes pour réaliser de magnifiques graphiques facilement mais dont le paramétrage sera forcément limité.

Voir la page d'aide.

Mises en forme  fines avec la librairie FactoMineR

La librairie FactoMineR semble offrir des fonctionnalités équivalentes à factoextra (cf. présentation) avec des intérêts notoires en particulier pour gérer les données manquantes avec missMDA. Une aide hybride entre factoextra et FactoMineR est disponible ici.

 Mise en forme des données de façon performante mais complexe avec ggplot2 et plotly

Cette rubrique reste à rédiger. ggplot et plotly ont un peu le propre langage au sein du langage R mais sont très performants pour réaliser tous les types de graphiques automatiquement (croisement de paramètres, régressions, 3D, nappes, ellipses, interactifs...)

La rotation varimax est une sorte de bidouillage qui va permettre de faire tourner les résultats de l'ACP pour que les "composantes" se superposent approximativement à des variables afin de simplifier l'interprétation des résultats.


Voir la page comment faire une ACP avec rotation varimax.

rotation varimax et ACP avec R project CRAN

le but recherché de cette rotation est une facilitation de l'interprétation. on voit ici qu'il sera possible de définir les personnes selon 2 axes (motivation/enthousiasme versus rigueur/connaissances/savoir-faire). (Exemple artificiel)

4. Lorsque l'ACP trouve ses limites : utiliser d'autres méthodes de réduction des dimensions supervisées ou non

Lorsque l'ACP ne donne pas des résultats satisfaisants, ce n'est pas toujours de la faute des résultats. L'ACP consiste à réduire les dimensions en écrasant un nuage de points.

Comme si on écrasait une mouche, si on s'y prend bien, on conserve 2 composantes principales sur la mouche : la longueur et la largeur.

Mais si on l'écrase de travers ? Le résultat d'ACP peut devenir incompréhensible, comme si la tête se retrouvait au niveau des pattes.

Et si on écrase un nuage de points en forme de serpent enroulé ? Idem.

Ou encore, si la variabilité semble plus forte au sein d'une catégorie identifiée qu'entre des catégories ?

Il existe alors d'autres méthodes dont le tSNE semble le plus prometteur, le cmdscale qui repose sur la distance, la projection de Fisher (fonction LDA()) pour créer une distance entre les catégories...

En dernier lieu, si vous n'obtenez toujours pas des résultats satisfaisants, n'hésitez pas à faire appel à nous pour être formés à d'autres outils non disponibles sur ce site ! Demande de devis gratuit.

L'ACM

Sorte d'ACP pour des données non-numériques (qualitatives), l'ACM (Analyse en Composante Multiple) se fait de la façon suivante :

library(ade4)

acm  <- dudi.acm(data)

On retrouve un exemple d'ACM réalisée avec {FactoMineR} dans la page dédiée à l'AFC.

cmdscale

Faire une ACP à partir de la matrice de distance entre les points.

Permet de travailler avec des données numériques et qualitatives.

data(iris)

mydi <- dist(iris[,-5])

my_cmd <- cmdscale(mydi)

plot(my_cmd,col=iris[,5])

Faire une ACP qui maximise la différence entre les catégories. Attention : méthode supervisée !

Voir la page d'aide.

Une ACP exige parfois de la classification/Catégorisation...

Voir la page d'aide.

Comment faire une réduction dimensionnelle sous python (ACP, tSNE).