Aller au contenu

Interprétez une ACP et découvrez ses cousines

Le calcul du chapitre précédent a produit des valeurs propres, des vecteurs propres et des coordonnées. Mais que disent-ils des villes ? Brest et Rennes se ressemblent-elles vraiment ? Que signifie le second axe ? Et peut-on faire confiance à la position de chaque point sur le graphique ?

Ce chapitre vous donne les outils de lecture d’une ACP : le nuage des individus et ses deux indicateurs (qualité de représentation et contribution), le cercle des corrélations pour les variables, et les variables illustratives pour expliquer les axes. Nous terminerons par un aperçu des méthodes cousines de l’ACP. Le fil rouge reste celui des six villes et de leurs températures par saison.

Rappelons les résultats : le premier axe explique 81,8 % de l’inertie, le second 17,9 %, soit 99,7 % pour le plan. Voici les coordonnées des villes sur ces deux axes, ainsi que la carte qui les représente.

axe 1 (81,8 %)axe 2 (17,9 %)BrestRennesStrasbourgLyonNiceMarseille
VilleF1F_1F2F_2cos² axe 1cos² axe 2cos² planCTR axe 1CTR axe 2
Brest−1,3271,3670,4850,5151,0009,0 %43,6 %
Rennes−1,1120,5330,8090,1860,9956,3 %6,6 %
Strasbourg−2,134−0,9590,8310,1680,99823,2 %21,5 %
Lyon−0,322−0,9490,1030,8930,9950,5 %21,0 %
Nice2,4950,3980,9710,0250,99531,7 %3,7 %
Marseille2,399−0,3910,9700,0260,99629,3 %3,6 %

Le premier axe range les villes de la plus froide (Strasbourg, à gauche) aux plus chaudes (Nice et Marseille, à droite). Le second place Brest tout en haut et Strasbourg et Lyon en bas. Deux villes proches sur la carte, comme Nice et Marseille, ont des profils de températures semblables… à une condition, que nous allons voir.

Une projection peut rapprocher deux points qui sont loin dans l’espace d’origine, comme deux étoiles alignées dans le ciel qui sont en fait à des distances très différentes de la Terre. Pour savoir si un individu est bien représenté sur un axe, on mesure la part de sa distance au centre qui est conservée par la projection :

cos⁡2(i,s)=Fs(i)2d2(O,Mi)\cos^2(i, s) = \frac{F_s(i)^2}{d^2(O, M_i)}

où Fs(i)F_s(i) est la coordonnée de l’individu ii sur l’axe ss et d2(O,Mi)d^2(O, M_i) le carré de sa distance au centre dans l’espace complet. C’est le carré du cosinus de l’angle entre le vecteur OMi→\overrightarrow{OM_i} et l’axe. Les axes étant orthogonaux, les cos² s’additionnent : le cos² d’un plan est la somme des cos² de ses deux axes. Un cos² proche de 1 signifie « bien représenté », proche de 0 « mal représenté ».

Ici, toutes les villes ont un cos² sur le plan supérieur à 0,99 : la carte est fidèle. Mais regardez Lyon : son cos² sur l’axe 1 n’est que de 0,103. Sa coordonnée proche de zéro sur cet axe indique seulement qu’elle n’est ni particulièrement chaude ni particulièrement froide sur l’année ; ce qui la distingue vraiment des autres villes se lit sur l’axe 2 (cos² de 0,893). Juger Lyon sur le seul axe 1, ce serait passer à côté de l’essentiel.

Nicecos² axe 1 = 0,971Lyoncos² axe 1 = 0,103axe 1axe 2cos² = (projection / distance au centre)²plan de l’ACPaxes 3, 4…(invisibles)A : cos² élevéB : cos² faibleprojetés voisinsproches sur la carte, loin en réalité
Le cos² est le carré du cosinus de l'angle entre un individu et l'axe : Nice est presque sur l'axe 1, Lyon presque perpendiculaire. À droite, le piège : B, mal représenté, tombe à côté de A sur la carte alors qu'il en est loin.

La contribution d’un individu à un axe mesure la part de l’inertie de l’axe qui vient de lui :

CTR(i,s)=1IFs(i)2λs\mathrm{CTR}(i, s) = \frac{\frac{1}{I} F_s(i)^2}{\lambda_s}

où II est le nombre d’individus et λs\lambda_s la valeur propre de l’axe. Pour chaque axe, les contributions somment à 100 %. Si tous les individus pesaient autant, chacun contribuerait pour 1/I1/I, soit environ 17 % avec six villes. Les individus dont la contribution dépasse nettement cette moyenne sont ceux qui caractérisent l’axe.

Ici, l’axe 1 est construit par Nice (31,7 %), Marseille (29,3 %) et Strasbourg (23,2 %) : l’opposition entre le Sud chaud et l’Est froid. L’axe 2 est dominé par Brest (43,6 %), à lui seul près de la moitié de l’inertie de l’axe.

01020304050contribution à l’inertie de l’axe (%)BrestRennesStrasbourgLyonNiceMarseille9,0 %43,6 %6,3 %6,6 %23,2 %21,5 %0,5 %21,0 %31,7 %3,7 %29,3 %3,6 %contribution moyenne 1/6 ≈ 17 %axe 1axe 2
Les villes au-delà de la ligne des 17 % construisent l'axe : Nice, Marseille et Strasbourg pour l'axe 1, Brest (43,6 %) avant tout pour l'axe 2.

Lisez le nuage des variables : le cercle des corrélations

Section intitulée « Lisez le nuage des variables : le cercle des corrélations »

Pour comprendre ce que mesure chaque axe, on regarde ses liens avec les variables d’origine. On calcule la corrélation entre chaque variable et chaque composante principale. En ACP normée, elle vaut

r(xk,Fs)=λs  uksr(x_k, F_s) = \sqrt{\lambda_s}\; u_{ks}

où uksu_{ks} est la kk-ième coordonnée du vecteur propre usu_s. Chaque variable devient alors une flèche dont les coordonnées sont ses corrélations avec les axes 1 et 2 : c’est le cercle des corrélations.

Variablecorrélation avec l’axe 1corrélation avec l’axe 2
Hiver0,7900,613
Printemps0,979−0,188
Été0,847−0,530
Automne0,9860,151
axe 1axe 2hiverautomneprintempsétélatitudeamplitude

Les flèches pleines sont les variables actives ; les flèches en pointillés sont des variables illustratives, que nous verrons plus bas. La lecture suit quelques règles simples.

  • Une flèche proche du cercle désigne une variable bien projetée : l’essentiel de sa variance est capté par le plan. Ici, les quatre saisons touchent presque le cercle (leurs corrélations au carré, sommées sur les deux axes, dépassent 0,99).
  • Deux flèches proches (angle aigu) désignent des variables corrélées positivement ; opposées, corrélées négativement ; à angle droit, non corrélées. L’angle entre deux flèches approche l’angle entre les variables, dont le cosinus est leur corrélation.
  • L’axe 1 est corrélé positivement à toutes les saisons, de 0,79 à 0,99 : c’est un effet taille. Une ville à droite est chaude toute l’année, une ville à gauche froide toute l’année, par comparaison avec les autres villes.
  • L’axe 2 oppose l’hiver (0,613) à l’été (−0,530). En haut, les villes à l’hiver doux et à l’été frais (Brest) ; en bas, les hivers froids et les étés chauds (Strasbourg, Lyon). L’axe 2 mesure l’amplitude thermique : faible en haut, forte en bas.
import numpy as np
# acp et Z viennent du chapitre précédent ; on refait les valeurs propres « divisées par n »
valeurs_propres = acp.explained_variance_ * (len(Z) - 1) / len(Z)
correlations = acp.components_.T * np.sqrt(valeurs_propres) # une ligne par variable
print(correlations[:, :2].round(3))

Ce cercle a une propriété remarquable, la dualité : les axes qui représentent au mieux le nuage des variables (en maximisant la somme des carrés des corrélations avec les variables) ont exactement les mêmes valeurs propres que ceux du nuage des individus. On vérifie ici que 0,7902+0,9792+0,8472+0,9862≈3,273=λ10{,}790^2 + 0{,}979^2 + 0{,}847^2 + 0{,}986^2 \approx 3{,}273 = \lambda_1. Les deux lectures sont les deux faces d’un même calcul.

Expliquez les axes avec des variables illustratives

Section intitulée « Expliquez les axes avec des variables illustratives »

Une variable illustrative (ou supplémentaire) ne participe pas au calcul des axes : on la projette après coup, pour aider à les interpréter.

Une variable illustrative quantitative se place sur le cercle grâce à ses corrélations avec les composantes. Ajoutons la latitude des six villes : sa corrélation vaut −0,974 avec l’axe 1 et 0,201 avec l’axe 2. La flèche pointe vers la gauche : plus une ville est au nord, plus elle est froide toute l’année. Ajoutons aussi l’amplitude thermique (été moins hiver) : corrélation 0,041 avec l’axe 1 et −0,998 avec l’axe 2. L’interprétation du second axe est confirmée de façon éclatante.

Une variable illustrative qualitative se représente par ses modalités, chacune placée au barycentre des individus qui la portent. Avec une variable « climat », les villes atlantiques (Brest, Rennes) ont pour barycentre le point (−1,22; 0,95)(-1{,}22 ;\, 0{,}95), les villes continentales (Strasbourg, Lyon) le point (−1,23; −0,95)(-1{,}23 ;\, -0{,}95) et les villes méditerranéennes (Nice, Marseille) le point (2,45; 0,00)(2{,}45 ;\, 0{,}00). Les deux premiers climats ne se distinguent pas sur l’axe 1, mais s’opposent nettement sur l’axe 2.

−2−10123axe 1 (81,8 %) : chaud toute l’année →−1,5−1,0−0,50,00,51,01,5axe 2 (17,9 %) : faible amplitude →BrestRennesStrasbourgLyonNiceMarseilleatlantique(−1,22 ; 0,95)continental(−1,23 ; −0,95)méditerranéen(2,45 ; 0,00)
Chaque modalité du climat se place au barycentre de ses villes. Atlantique et continental ont la même abscisse et ne diffèrent que sur l'axe 2 ; le climat méditerranéen se détache sur l'axe 1.

En 1991, une méthode de reconnaissance faciale a popularisé l’ACP sous le nom d’eigenfaces (visages propres). Chaque image de visage est un vecteur de pixels. On calcule le visage moyen ψ\psi, on centre chaque visage (ϕi=xi−ψ\phi_i = x_i - \psi), puis on cherche les vecteurs propres de la matrice de covariance des visages centrés. Ces vecteurs, remis en forme d’image, ressemblent à des visages fantomatiques : ce sont les visages propres. Tout visage s’écrit alors approximativement comme le visage moyen plus une combinaison de quelques dizaines de visages propres, et l’on ne stocke que les poids wk=uk⊤(x−ψ)w_k = u_k^\top (x - \psi).

Le gain est considérable. Stocker 100 000 images de 256×256256 \times 256 pixels (un octet par pixel) demande 6,55×1096{,}55 \times 10^9 octets. Stocker 100 visages propres et, pour chaque image, 100 poids codés sur 4 octets, n’en demande que 256×256×100+100 000×100×4≈4,66×107256 \times 256 \times 100 + 100\,000 \times 100 \times 4 \approx 4{,}66 \times 10^7, soit environ 140 fois moins.

1 Mo10 Mo100 Mo1 Go10 Goplace nécessaire (échelle logarithmique)100 000 imagesbrutes100 visages propres+ 100 poids par image6,55 × 10⁹ octets4,66 × 10⁷ octets : ≈ 140 fois moins
Deux ordres de grandeur gagnés : on ne stocke plus les pixels de chaque visage, seulement ses 100 poids.

Une astuce de calcul rend la méthode praticable : la matrice de covariance AA⊤AA^\top (où AA contient les MM visages centrés en colonnes) a la taille du nombre de pixels au carré, soit plus de 4 milliards de termes. On diagonalise plutôt la petite matrice A⊤AA^\top A, de taille M×MM \times M, et l’on remonte aux vecteurs propres par u=Avu = Av ; ou, plus simplement, on calcule la SVD de AA.

L’ACP ne traite que des variables quantitatives, et seulement des structures linéaires. D’autres méthodes prennent le relais.

  • L’analyse factorielle des correspondances (AFC) étudie un tableau de contingence croisant deux variables qualitatives (par exemple la catégorie socioprofessionnelle et le journal lu). Elle compare les profils des lignes et des colonnes avec la distance du χ2\chi^2, et représente sur une même carte les modalités qui s’attirent.
  • L’analyse des correspondances multiples (ACM) généralise l’AFC à plusieurs variables qualitatives, typiquement les réponses à un questionnaire. Elle travaille sur le tableau disjonctif complet (un encodage one-hot de toutes les réponses).
  • t-SNE (t-distributed stochastic neighbor embedding) est une méthode non linéaire conçue pour la visualisation. Elle cherche une carte en 2D où les voisins proches dans l’espace d’origine restent voisins. Elle révèle très bien des groupes, mais les distances entre groupes éloignés et la taille apparente des groupes ne sont pas fiables, le résultat dépend d’un réglage (la perplexité) et du hasard, et elle ne sait pas projeter de nouveaux individus. UMAP poursuit le même but, plus rapidement.
CritèreACPt-SNE
Typelinéairenon linéaire
Résultatdéterministe (au signe près)dépend du hasard et de la perplexité
Axesinterprétables (cercle des corrélations)sans signification
Distances globalesconservées au mieuxnon fiables
Nouveaux individusse projettent directementil faut tout recalculer
Usage typerésumer, décorréler, compresservisualiser des groupes
  • Sur la carte des individus, deux points proches ne se ressemblent que s’ils sont bien représentés : le cos² mesure cette qualité, et s’additionne sur les axes.
  • La contribution CTR(i,s)=1IFs(i)2/λs\mathrm{CTR}(i, s) = \frac{1}{I}F_s(i)^2 / \lambda_s désigne les individus qui construisent un axe.
  • Le cercle des corrélations place chaque variable selon ses corrélations λs uks\sqrt{\lambda_s}\,u_{ks} avec les axes ; seules les flèches proches du cercle se lisent.
  • Sur les villes, l’axe 1 est un effet taille (chaud ou froid toute l’année), l’axe 2 l’amplitude thermique.
  • Les variables illustratives (quantitatives sur le cercle, qualitatives au barycentre) aident à interpréter sans modifier les axes.
  • L’AFC et l’ACM traitent des variables qualitatives ; t-SNE et UMAP visualisent des structures non linéaires, sans axes interprétables.

Exercice · Lire un cercle des corrélations

facile

Dans une ACP normée sur des performances sportives, le cercle des corrélations montre : « 100 m » en (−0,85; 0,30)(-0{,}85 ;\, 0{,}30), « saut en longueur » en (0,82; −0,25)(0{,}82 ;\, -0{,}25), « 1 500 m » en (−0,10; 0,90)(-0{,}10 ;\, 0{,}90) et « lancer de poids » en (0,15; 0,20)(0{,}15 ;\, 0{,}20). Quelles variables sont bien projetées ? Lesquelles sont corrélées ? Que peut-on dire du lancer de poids ?

Voir la solution

On calcule la somme des carrés des deux coordonnées (la qualité de projection sur le plan) :

  • 100 m : 0,7225+0,09≈0,810{,}7225 + 0{,}09 \approx 0{,}81, bien projeté ;
  • saut en longueur : 0,6724+0,0625≈0,730{,}6724 + 0{,}0625 \approx 0{,}73, assez bien projeté ;
  • 1 500 m : 0,01+0,81=0,820{,}01 + 0{,}81 = 0{,}82, bien projeté ;
  • lancer de poids : 0,0225+0,04≈0,060{,}0225 + 0{,}04 \approx 0{,}06, très mal projeté.

Le 100 m et le saut en longueur pointent dans des directions presque opposées : ils sont corrélés négativement. C’est logique, un bon sprinteur a un temps faible et saute loin. Le 1 500 m fait avec eux des angles d’environ 64° et 113°, loin de 0° comme de 180° : il n’est que faiblement corrélé avec eux. On ne peut rien dire du lancer de poids à partir de ce plan : sa flèche est trop courte, sa variance se trouve sur d’autres axes.

En code

import numpy as np
fleches = {"100 m": (-0.85, 0.30), "longueur": (0.82, -0.25),
"1 500 m": (-0.10, 0.90), "poids": (0.15, 0.20)}
for nom, (x, y) in fleches.items():
print(nom, round(x**2 + y**2, 2)) # qualité : 0.81 0.73 0.82 0.06
def angle(a, b):
a, b = np.array(fleches[a]), np.array(fleches[b])
return np.degrees(np.arccos(a @ b / np.linalg.norm(a) / np.linalg.norm(b)))
# Le produit scalaire des flèches approche la corrélation (variables bien projetées)
for a, b in [("100 m", "longueur"), ("100 m", "1 500 m"), ("longueur", "1 500 m")]:
print(a, b, round(angle(a, b)), round(np.dot(fleches[a], fleches[b]), 2))
# 100 m / longueur : 178 degrés, -0.77
# 100 m / 1 500 m : 64 degrés, 0.36 ; longueur / 1 500 m : 113 degrés, -0.31

Exercice · Calculer un cos² et une contribution

moyen

Dans l’ACP des six villes, Lyon a pour coordonnées F1=−0,322F_1 = -0{,}322 et F2=−0,949F_2 = -0{,}949, et le carré de sa distance au centre vaut d2=1,008d^2 = 1{,}008. Les valeurs propres sont λ1=3,273\lambda_1 = 3{,}273 et λ2=0,714\lambda_2 = 0{,}714. Calculez le cos² de Lyon sur chaque axe et sur le plan, puis ses contributions aux deux axes. Commentez.

Voir la solution
  • cos⁡2(Lyon,1)=0,3222/1,008=0,1037/1,008≈0,103\cos^2(\text{Lyon}, 1) = 0{,}322^2 / 1{,}008 = 0{,}1037 / 1{,}008 \approx 0{,}103.
  • cos⁡2(Lyon,2)=0,9492/1,008=0,9006/1,008≈0,893\cos^2(\text{Lyon}, 2) = 0{,}949^2 / 1{,}008 = 0{,}9006 / 1{,}008 \approx 0{,}893.
  • Sur le plan : 0,103+0,893=0,9960{,}103 + 0{,}893 = 0{,}996 (0,995 avec les valeurs non arrondies).
  • CTR(Lyon,1)=0,1037/63,273≈0,005\mathrm{CTR}(\text{Lyon}, 1) = \frac{0{,}1037 / 6}{3{,}273} \approx 0{,}005, soit 0,5 %.
  • CTR(Lyon,2)=0,9006/60,714≈0,210\mathrm{CTR}(\text{Lyon}, 2) = \frac{0{,}9006 / 6}{0{,}714} \approx 0{,}210, soit 21,0 %.

Lyon est parfaitement représentée sur le plan, mais presque uniquement par l’axe 2 : sa position au milieu de l’axe 1 correspond à une température annuelle moyenne parmi ces villes, tandis que sa forte amplitude thermique (hiver froid, été chaud) la place en bas de l’axe 2, qu’elle contribue à construire pour un cinquième.

En code

F1, F2, d2 = -0.322, -0.949, 1.008 # Lyon
lambda1, lambda2, I = 3.273, 0.714, 6
cos2_1, cos2_2 = F1**2 / d2, F2**2 / d2
print(round(cos2_1, 3), round(cos2_2, 3), round(cos2_1 + cos2_2, 3)) # 0.103 0.893 0.996
print(round(F1**2 / I / lambda1, 3), round(F2**2 / I / lambda2, 3)) # 0.005 0.21
# Avec l'ACP complète (valeurs non arrondies)
import numpy as np
X = np.array([[7.0, 10.5, 16.5, 12.0], [5.5, 11.0, 18.0, 12.0], [1.5, 10.5, 19.5, 10.0],
[3.5, 12.0, 21.5, 12.5], [9.5, 13.5, 23.0, 17.0], [8.0, 14.0, 24.0, 16.0]])
Z = (X - X.mean(axis=0)) / X.std(axis=0)
lambdas, U = np.linalg.eigh(Z.T @ Z / I)
lambdas, U = lambdas[::-1], U[:, ::-1] # ordre décroissant
F = Z @ U
lyon = 3
cos2 = F[lyon, :2] ** 2 / (Z[lyon] ** 2).sum()
print(cos2.round(3), round(cos2.sum(), 3)) # [0.103 0.893] 0.995

Exercice · Démontrer la formule des corrélations

difficile

En ACP normée, montrez que la corrélation entre la variable standardisée zkz_k (la colonne kk de ZZ) et la composante Fs=ZusF_s = Z u_s vaut λs uks\sqrt{\lambda_s}\,u_{ks}. Vérifiez-la pour l’hiver sur l’axe 1, sachant que u1≈(0,437; 0,541; 0,468; 0,545)u_{1} \approx (0{,}437 ;\, 0{,}541 ;\, 0{,}468 ;\, 0{,}545) et λ1=3,273\lambda_1 = 3{,}273.

Voir la solution

Les deux variables sont centrées ; zkz_k a un écart-type de 1 et FsF_s un écart-type de λs\sqrt{\lambda_s} (chapitre 5). La corrélation est donc

r(zk,Fs)=1Izk⊤Fs1×λs=1Izk⊤Zusλsr(z_k, F_s) = \frac{\frac{1}{I} z_k^\top F_s}{1 \times \sqrt{\lambda_s}} = \frac{\frac{1}{I} z_k^\top Z u_s}{\sqrt{\lambda_s}}

Or 1Izk⊤Z\frac{1}{I} z_k^\top Z est la kk-ième ligne de R=1IZ⊤ZR = \frac{1}{I} Z^\top Z, si bien que 1Izk⊤Zus\frac{1}{I} z_k^\top Z u_s est la kk-ième coordonnée de Rus=λsusR u_s = \lambda_s u_s, c’est-à-dire λsuks\lambda_s u_{ks}. Finalement :

r(zk,Fs)=λsuksλs=λs uksr(z_k, F_s) = \frac{\lambda_s u_{ks}}{\sqrt{\lambda_s}} = \sqrt{\lambda_s}\, u_{ks}

Vérification pour l’hiver : 3,273×0,437≈1,809×0,437≈0,791\sqrt{3{,}273} \times 0{,}437 \approx 1{,}809 \times 0{,}437 \approx 0{,}791, ce qui correspond (aux arrondis près) à la valeur 0,790 du tableau. On en déduit aussi que ∑kr(zk,Fs)2=λs∑kuks2=λs\sum_k r(z_k, F_s)^2 = \lambda_s \sum_k u_{ks}^2 = \lambda_s, puisque usu_s est unitaire : c’est la dualité entre les deux nuages.

En code

import numpy as np
X = np.array([[7.0, 10.5, 16.5, 12.0], [5.5, 11.0, 18.0, 12.0], [1.5, 10.5, 19.5, 10.0],
[3.5, 12.0, 21.5, 12.5], [9.5, 13.5, 23.0, 17.0], [8.0, 14.0, 24.0, 16.0]])
I = len(X)
Z = (X - X.mean(axis=0)) / X.std(axis=0)
lambdas, U = np.linalg.eigh(Z.T @ Z / I)
l1, u1 = lambdas[-1], U[:, -1] * np.sign(U[0, -1]) # plus grande valeur propre, signe fixé
F1 = Z @ u1
r_direct = np.corrcoef(Z[:, 0], F1)[0, 1] # corrélation hiver / axe 1
print(round(r_direct, 3), round(np.sqrt(l1) * u1[0], 3)) # 0.79 0.79
print(round(np.sqrt(3.273) * 0.437, 3)) # 0.791 avec les valeurs arrondies
r_toutes = np.sqrt(l1) * u1
print(round((r_toutes ** 2).sum(), 3), round(l1, 3)) # 3.273 3.273 : la dualité