L’objectif de ce document est de transformer une matrice de données initiale X à travers plusieurs étapes (centrage, réduction, calcul de la matrice de corrélation), afin d’effectuer une Analyse en Composantes Principales (ACP).

L’ACP permet de résumer l’information contenue dans un grand nombre de variables en un nombre réduit de composantes principales non corrélées. Ces nouvelles variables maximisent la variance expliquée, facilitant ainsi la visualisation, la compression, ou encore la réduction de dimension pour l’analyse de données.

  1. Pour ramener toutes les variables à la même échelle

Exemple :

Taille : ~170 cm

Poids : ~60 kg

Classe : de 0 à 20

Sans normalisation, les variables ayant des plages numériques plus larges (comme la taille) dominent la variance. ⇒ L’ACP capture principalement l’influence de cette variable dominante.

  1. Pour garantir une contribution égale de toutes les variables Après normalisation, chaque variable a une moyenne de 0 et un écart type de 1. ⇒ Toutes les variables contribuent équitablement aux composantes principales (CP), quelle que soit leur unité d’origine.

  2. Pour éviter la « dominance d’échelle » dans les méthodes basées sur les distances L’ACP, les k-moyennes, les k-NN et les autres méthodes utilisant les distances sont sensibles à l’échelle. ⇒ La normalisation évite les calculs de distance erronés.

  3. Améliorer la stabilité numérique La matrice de covariance ou de corrélation sera mieux conditionnée (moins sujette aux erreurs numériques). ⇒ Calculs de valeurs propres/vecteurs propres ou de SVD plus fiables.

  4. Réduire la multicolinéarité artificielle Les variables de différentes unités peuvent sembler corrélées en raison de l’échelle. ⇒ La standardisation donne une image plus précise des corrélations réelles.

  5. Rendre les coefficients de pondération comparables Les coefficients de pondération de l’ACP (les coefficients de chaque variable dans chaque PC) deviennent indépendants de l’unité, ⇒ ce qui facilite l’interprétation de l’importance de chaque variable.

Conclusion : La standardisation garantit que l’ACP révèle la véritable structure des données, et pas seulement l’effet des échelles des variables, et rend les résultats plus interprétables, comparables et fiables.

1. Creer Matrice X

X <- matrix(c(160, 170, 180, 200,
              45, 90, 60, 90,
              10, 16, 5, 20),
            nrow = 4, ncol = 3, byrow = FALSE)

rownames(X) <- c("A", "B", "C", "D")
colnames(X) <- c("taille", "poid", "note")
X
##   taille poid note
## A    160   45   10
## B    170   90   16
## C    180   60    5
## D    200   90   20

2. Matrice X_bar

Étape : Matrice centrée \(X_{\text{bar}}\)

L’étape de centrage consiste à soustraire la moyenne de chaque variable. Ceci est essentiel pour garantir que l’analyse en composantes principales (ACP) reflète la dispersion réelle des données et non l’effet de leur localisation moyenne.

Sans centrage, l’ACP peut interpréter des moyennes élevées comme des variances importantes, ce qui faussera les résultats.

Par exemple, considérons deux variables :

Taille : 160, 170, 180, 200 → moyenne = 177,5

Poids : 45, 90, 90, 60 → moyenne = 71,25

Bien que les pondérations soient assez proches (faible dispersion), leurs valeurs moyennes sont relativement élevées. Si l’ACP est appliquée directement à des données non centrées, le barycentre des données sera éloigné de l’origine, ce qui forcera les composantes principales à s’aligner par rapport à cette localisation plutôt que de refléter les véritables directions de variation.

Le centrage ramène le centre de masse du nuage de points à l’origine (0,0). Cela permet aux axes principaux (PC1, PC2, etc.) de s’auto-aligner, en se basant uniquement sur les variations relatives entre individus.

Il s’agit d’une étape essentielle pour garantir que les composantes principales détectées correspondent bien aux directions présentant la plus grande variation dans les données.

La graphe pour visualiser

library(FactoMineR)

# Données brutes
X_ex <- data.frame(
  taille = c(160, 170, 180, 200),
  poids  = c(45, 90, 60, 90),
  note   = c(10, 16, 5, 20)
)
rownames(X_ex) <- c("A", "B", "C", "D")

# PCA sans centrage
res_sans <- prcomp(X_ex, center = FALSE, scale. = FALSE)

# PCA avec centrage
res_avec <- prcomp(X_ex, center = TRUE, scale. = FALSE)

# Visualisation
plot(res_sans$x, main = "Sans centrage", col = "red", pch = 19)
text(res_sans$x, labels = rownames(X_ex), pos = 3)

plot(res_avec$x, main = "Avec centrage", col = "blue", pch = 19)
text(res_avec$x, labels = rownames(X_ex), pos = 3)

Objectif

Centrer les données consiste à soustraire la moyenne de chaque colonne.
On obtient ainsi une matrice centrée \(X_{\text{bar}}\) dont chaque variable possède une moyenne nulle.


Formule générale

  • \(X_{\text{bar}} = X - \mu\), où \(\mu\) est le vecteur des moyennes
  • Plus formellement :
    \[ X_{\text{bar}} = H \cdot X, \quad \text{où} \quad H = I_n - \frac{1}{n}\mathbf{1}_n \mathbf{1}_n^{\top} \]

Élément par élément

On peut écrire chaque valeur de \(X_{\text{bar}}\) comme :
\[ x^{\text{bar}}_{ij} = x_{ij} - \bar{x}_j, \quad \text{avec} \quad \bar{x}_j = \frac{1}{n} \sum_{i=1}^{n} x_{ij} \] \(\bar{x}_j\) est la valeur moyenne du colonne j

\[ X_{\text{bar}} \;=\; \begin{pmatrix} x_{11}-\bar{x}_1 & x_{12}-\bar{x}_2 & \dots & x_{1p}-\bar{x}_p\\ x_{21}-\bar{x}_1 & x_{22}-\bar{x}_2 & \dots & x_{2p}-\bar{x}_p\\ \vdots & \vdots & \ddots& \vdots\\ x_{n1}-\bar{x}_1 & x_{n2}-\bar{x}_2 & \dots & x_{np}-\bar{x}_p \end{pmatrix} \]

Raison

  • Supprime le biais dû à la moyenne des variables
  • Met l’accent sur les variations relatives entre individus
  • Nécessaire pour toute méthode basée sur la variance, comme l’ACP (PCA)

Creer vector 1_n (1,1,…,1,1)

n <- nrow(X); p <- ncol(X)
one_n <- matrix(1, nrow = n, ncol = 1)
one_n
##      [,1]
## [1,]    1
## [2,]    1
## [3,]    1
## [4,]    1

Construction de la matrice de centrage \(H\)

Pour centrer les données, on utilise la matrice de projection suivante :

\[ H = I_n - \frac{1}{n} \mathbf{1}_n \mathbf{1}_n^T \]

  • \(I_n\) est la matrice identité de taille \(n n\)

  • \(_n\) est un vecteur colonne de taille \(n\), rempli de 1 :
    \[ \mathbf{1}_n = \begin{bmatrix} 1 \\ 1 \\ \vdots \\ 1 \end{bmatrix} \]

  • matrice carrée de taille \(n n\) où toutes les cases valent 1 : \[ \mathbf{1}_n \mathbf{1}_n^T = \begin{bmatrix} 1 & 1 & \cdots & 1 \\ 1 & 1 & \cdots & 1 \\ \vdots & \vdots & \ddots & \vdots \\ 1 & 1 & \cdots & 1 \end{bmatrix} \]

Ainsi, la matrice \(H\) permet de soustraire la moyenne de chaque ligne automatiquement, donc :
\[ X_{\text{bar}} = H \cdot X \]

Utilité de la matrice H :

  • Elle automatise la centrage des données
  • Très utilisée en algèbre linéaire, notamment en ACP (PCA), régression, etc.
I_n <- diag(n)
H   <- I_n - (1 / n) * (one_n %*% t(one_n))
H
##       [,1]  [,2]  [,3]  [,4]
## [1,]  0.75 -0.25 -0.25 -0.25
## [2,] -0.25  0.75 -0.25 -0.25
## [3,] -0.25 -0.25  0.75 -0.25
## [4,] -0.25 -0.25 -0.25  0.75

Calcu de X_bar = H * X

X_bar <- H %*% X
X_bar
##      taille   poid  note
## [1,]  -17.5 -26.25 -2.75
## [2,]   -7.5  18.75  3.25
## [3,]    2.5 -11.25 -7.75
## [4,]   22.5  18.75  7.25

3. Calcul de sigma

Variance d’une variable

La variance d’une variable aléatoire \(X_j\), notée \(\text{Var}(X_j)\), mesure la dispersion de ses valeurs autour de sa moyenne.
Elle est définie comme :

\[ \text{Var}(X_j) = \frac{1}{n} \sum_{i=1}^{n} \left(x_{ij} - \bar{x}_j\right)^2 \]

  • Une grande variance signifie que les données sont très dispersées.
  • Une petite variance indique que les données sont proches de la moyenne.

Dans la matrice de corrélation, on utilise la variance pour normaliser la covariance, ce qui permet de rendre les coefficients comparables entre variables.

Calcul de l’écart-type \(\sigma_j\)

Objectif

Calculer l’écart-type (ou l’écart quadratique moyen) de chaque variable \(j\) pour normaliser les données.
Cela permet ensuite de comparer les variables même si elles n’ont pas la même unité ou échelle.


Formule utilisée

L’écart-type \(\sigma_j\) est défini comme :

\[ \sigma_j = \sqrt{\frac{1}{n} \sum_{i=1}^{n} \left( x_{ij} - \bar{x}_j \right)^2} \]

  • \(x_{ij}\) : valeur de l’individu \(i\) pour la variable \(j\)
  • \(\bar{x}_j\) : moyenne de la colonne \(j\)
  • \(n\) : nombre d’individus (lignes)

Interprétation

  • On calcule la variance de chaque colonne
  • Puis on en prend la racine carrée
  • Cela donne une mesure de la dispersion autour de la moyenne pour chaque variable

Raison

  • Permet de réduire les données (étape suivante de PCA)
  • Rend les variables comparables, même si elles ont des échelles très différentes
  • Essentiel dans le cas où une variable domine les autres par sa variance
s_vec <- numeric(p)            

for (j in 1:p) {               # parcourir colonne par colonne
  ssq <- 0                     # (sum of squares)
for (i in 1:n) {
    ssq <- ssq + X_bar[i, j]^2 # ajouter les squares
  }
  s_vec[j] <- sqrt( ssq / (n ) )   # variance de l'échantillon
}
s_vec
## [1] 14.790199 19.485572  5.717298

4. Calcul de X_reduit

Matrice des données réduites : \(X_{\text{réduit}}\)


Le but

La matrice \(X_{\text{réduit}}\) est la version centrée et réduite de la matrice de données initiale \(X\).
Cela signifie que, pour chaque variable (chaque colonne) :

  • Elle a une moyenne nulle → données centrées
  • Elle a un écart-type égal à 1 → données réduites

Formule de réduction

Pour passer de \(X\) à \(X_{\text{réduit}}\), on applique deux étapes :

  1. Centrer :
    \[ X_{\text{bar}} = X - \bar{X} \]

  2. Réduire :
    \[ X_{\text{réduit}} = \frac{X - \bar{X}}{\sigma} \]\(\sigma\) est l’écart-type de chaque colonne.

Pour chaque élément \(x_{ij}\) de la matrice \(X\), l’élément correspondant dans \(X_{\text{réduit}}\) est :

\[ x^{\text{réduit}}_{ij} \;=\; \frac{x_{ij} - \bar{x}_j}{\sigma_j} \]

  • \(\bar{x}_j\) est la moyenne de la colonne \(j\)
  • \(\sigma_j\) est l’écart-type de la colonne \(j\)

En notation matricielle

On peut écrire cette opération de manière compacte :

\[ X_{\text{réduit}} = X_{\text{bar}} \cdot D_s^{-1} \]

où :

  • \(X_{\text{bar}}\) est la matrice centrée
  • \(D_s\) est une matrice diagonale contenant les écarts-types \(\sigma_1, \sigma_2, \dots, \sigma_p\)

\[ D_s = \begin{pmatrix} \sigma_1 & 0 & \dots & 0 \\\\ 0 & \sigma_2 & \dots & 0 \\\\ \vdots & \vdots & \ddots & \vdots \\\\ 0 & 0 & \dots & \sigma_p \end{pmatrix} \]

et
\(D_s^{-1}\) est simplement l’inverse de cette matrice diagonale.


\[ X_{\text{réduit}} \;=\; \begin{pmatrix} \dfrac{x_{11}-\bar{x}_1}{\sigma_1} & \dfrac{x_{12}-\bar{x}_2}{\sigma_2} & \dots & \dfrac{x_{1p}-\bar{x}_p}{\sigma_p}\\ \dfrac{x_{21}-\bar{x}_1}{\sigma_1} & \dfrac{x_{22}-\bar{x}_2}{\sigma_2} & \dots & \dfrac{x_{2p}-\bar{x}_p}{\sigma_p}\\ \vdots & \vdots & \ddots& \vdots\\ \dfrac{x_{n1}-\bar{x}_1}{\sigma_1} & \dfrac{x_{n2}-\bar{x}_2}{\sigma_2} & \dots & \dfrac{x_{np}-\bar{x}_p}{\sigma_p} \end{pmatrix} \]

Pourquoi on fait ça ?

Cela permet :

  • De neutraliser l’effet des unités ou des échelles différentes
  • De garantir que chaque variable contribue équitablement à l’ACP
D_s_inv <- diag(1 / s_vec)
X_reduit <- X_bar %*% D_s_inv
X_reduit
##            [,1]       [,2]       [,3]
## [1,] -1.1832160 -1.3471506 -0.4809964
## [2,] -0.5070926  0.9622504  0.5684503
## [3,]  0.1690309 -0.5773503 -1.3555354
## [4,]  1.5212777  0.9622504  1.2680815

5. Matrice de covariance C

Matrice de covariance \(C\)

Le but

La matrice de covariance (ou matrice des moments d’ordre 2) permet de mesurer comment les variables varient ensemble dans un jeu de données.

En Analyse en Composantes Principales (ACP), elle est essentielle pour : - Comprendre les relations linéaires entre variables - Trouver les directions principales de variation (via les vecteurs propres)


Définition mathématique

À partir de la matrice centrée \(X_{\text{bar}}\), on calcule la matrice de covariance \(C\) comme :

\[ C = \frac{1}{n} X_{\text{bar}}^{\top} X_{\text{bar}} \]

où : - \(X_{\text{bar}}\) est la matrice des données centrées - \(n\) est le nombre d’individus (lignes)

Forme générale de la matrice de covariance

La matrice de covariance \(C\) est une matrice symétrique de taille \(p \times p\), dont les éléments sont les covariances entre les variables :

\[ C = \begin{pmatrix} \sigma_{1}^2 & \text{Cov}(X_1, X_2) & \dots & \text{Cov}(X_1, X_p) \\ \text{Cov}(X_2, X_1) & \sigma_{2}^2 & \dots & \text{Cov}(X_2, X_p) \\ \vdots & \vdots & \ddots & \vdots \\ \text{Cov}(X_p, X_1) & \text{Cov}(X_p, X_2) & \dots & \sigma_{p}^2 \end{pmatrix} \]

  • La diagonale contient les variances : \(\sigma_j^2 = \text{Var}(X_j)\)
  • Les hors-diagonales sont les covariances : \(\text{Cov}(X_i, X_j)\) avec \(i \neq j\) ### Interprétation de la covariance \(\text{Cov}(X_1, X_2)\)

La covariance entre deux variables \(X_1\) et \(X_2\), notée \(\text{Cov}(X_1, X_2)\), mesure dans quelle mesure les deux variables varient ensemble.

\[ \text{Cov}(X_1, X_2) = \frac{1}{n} \sum_{i=1}^{n} (x_{i1} - \bar{x}_1)(x_{i2} - \bar{x}_2) \]

  • Si \(\text{Cov}(X_1, X_2) > 0\) :
    \(X_1\) et \(X_2\) ont tendance à augmenter ensemble → relation linéaire positive.

  • Si \(\text{Cov}(X_1, X_2) < 0\) :
    Quand \(X_1\) augmente, \(X_2\) a tendance à diminuer → relation linéaire négative.

  • Si \(\text{Cov}(X_1, X_2) \approx 0\) :
    Pas de relation linéaire claire entre \(X_1\) et \(X_2\).

Remarque :

La covariance dépend de l’unité et de l’échelle des variables.
On utilise souvent la corrélation (valeurs entre \(-1\) et \(+1\)) pour comparer les variables plus facilement.


Propriétés

  • \(C\) est une matrice symétrique
  • La diagonale contient les variances de chaque variable
  • Les éléments hors diagonale représentent les covariances entre variables

Pourquoi c’est important ?

  • L’ACP repose sur la décomposition spectrale de cette matrice
  • Les valeurs propres de \(C\) mesurent les variances expliquées par chaque composante principale
# C = (t(X_bar) * X_bar)
C <- (t(X_bar) %*% X_bar) / (n)
C
##         taille     poid    note
## taille 218.750 178.1250 41.8750
## poid   178.125 379.6875 89.0625
## note    41.875  89.0625 32.6875

6. Matrice C_reduit

Matrice de corrélation réduite \(C_{\text{réduit}}\)

La matrice \(C_{\text{réduit}}\), aussi appelée matrice de corrélation, est une version normalisée de la matrice de covariance \(C\).

Elle permet de comparer les relations linéaires entre variables sans être influencé par leurs unités ou échelles.

Forme générale de la matrice de corrélation \(R\)

La matrice de corrélation \(R\) est une matrice symétrique de taille \(p \times p\), dont les éléments mesurent le niveau de relation linéaire entre les variables centrées-réduites.

\[ R = \begin{pmatrix} 1 & \rho_{12} & \dots & \rho_{1p} \\ \rho_{21} & 1 & \dots & \rho_{2p} \\ \vdots & \vdots & \ddots & \vdots \\ \rho_{p1} & \rho_{p2} & \dots & 1 \end{pmatrix} \]

  • La diagonale contient uniquement des 1 : \(\rho_{jj} = 1\), car la corrélation d’une variable avec elle-même est toujours maximale.

    En effet, si \(X_j\) est une variable quelconque, on a :
    \[ \rho_{jj} = \frac{\text{Cov}(X_j, X_j)}{\sigma_j \sigma_j} = \frac{\text{Var}(X_j)}{\sigma_j^2} = 1 \] Cela reflète une corrélation parfaite entre une variable et elle-même.

  • Les éléments hors diagonale sont les coefficients de corrélation de Pearson : \[ \rho_{ij} = \frac{\text{Cov}(X_i, X_j)}{\sigma_i \sigma_j} \]


Cette matrice est utilisée lorsque les données sont standardisées (centrées et réduites), ce qui permet de comparer des variables avec des unités ou échelles différentes.

# Exemple de calcul avec une matrice centrée-réduite
X_reduit <- scale(X)
R <- cor(X_reduit)
R
##           taille      poid      note
## taille 1.0000000 0.6180700 0.4952106
## poid   0.6180700 1.0000000 0.7994492
## note   0.4952106 0.7994492 1.0000000

Pourquoi l’utiliser ?

  • Les variables peuvent avoir des unités différentes (cm, kg, etc.).
  • La matrice de corrélation place toutes les variables sur la même échelle (variance = 1).
  • C’est cette matrice que l’on décompose généralement dans une ACP.

Comment la calculer ?

Il existe deux méthodes équivalentes :

  • Méthode 1 – à partir de la covariance
    \[ C_{\text{réduit}} = D_s^{-1}\, C\, D_s^{-1} \]\(D_s\) est la matrice diagonale des écarts-types \((\sigma_1,\dots,\sigma_p)\).

  • Méthode 2 – directement depuis les données réduites
    \[ C_{\text{réduit}} = \frac{1}{n}\, X_{\text{réduit}}^{\top}\, X_{\text{réduit}} \] avec \(X_{\text{réduit}}\) la matrice centrée-réduite.

Résultat attendu

  • Matrice symétrique.
  • Diagonale = 1 (chaque variable est normalisée).
  • Hors-diagonale = coefficients de corrélation linéaire entre les variables.
# C_reduit = D_s_inv * C * D_s_inv   (đường chéo = 1)
C_reduit <- D_s_inv %*% C %*% D_s_inv
C_reduit
##           [,1]      [,2]      [,3]
## [1,] 1.0000000 0.6180700 0.4952106
## [2,] 0.6180700 1.0000000 0.7994492
## [3,] 0.4952106 0.7994492 1.0000000
# C_reduit2 <-(t(X_reduit) * X_reduit)/n = D_s_inv * (1/n) (t(X_bar) * X_bar) * D_s_inv = C_reduit
C_reduit2 <-(t(X_reduit) %*% X_reduit)/n
C_reduit2
##           taille      poid      note
## taille 0.7500000 0.4635525 0.3714080
## poid   0.4635525 0.7500000 0.5995869
## note   0.3714080 0.5995869 0.7500000

7S. Valeur eigen

Dans cette étape, à l’aide de la matrice de covariance réduite Creduit, nous calculons ses valeurs propres et ses vecteurs propres. L’ACP trouve les vecteurs propres de la matrice de covariance (ou de corrélation). Les vecteurs propres représentent les directions des composantes principales (PC1, PC2, etc.). Les valeurs propres représentent la quantité de variance expliquée dans ces directions.

La première composante principale (PC1) est la direction le long de laquelle la projection des données présente la variance maximale ; autrement dit, les données sont les plus « étalées » le long de cet axe.

library(ggplot2)


points_ex <- data.frame(
  name = c("A", "B", "C", "D"),
  x = c(-1.5, 2.0, -1.0, 2.0),
  y = c(-0.5, -1.0, 1.0, 0.5)
)


pc1_dir <- c(1, -0.5)
pc1_unit <- pc1_dir / sqrt(sum(pc1_dir^2))


project_point <- function(p, dir) {
  s <- sum(p * dir)
  return(s * dir)
}


projections <- t(apply(points_ex[, c("x", "y")], 1, project_point, dir = pc1_unit))
colnames(projections) <- c("x_proj", "y_proj")
points_proj <- cbind(points_ex, projections)
points_proj$name_proj <- paste0(points_proj$name, "'")


ggplot() +
  
  geom_abline(intercept = 0, slope = pc1_unit[2] / pc1_unit[1], color = "red", size = 1.2, linetype = "solid") +
  
  
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "gray") +
  
  
  geom_point(data = points_proj, aes(x = x, y = y), color = "darkgreen", size = 3) +
  geom_text(data = points_proj, aes(x = x + 0.2, y = y, label = name), color = "darkgreen") +
  
  
  geom_segment(data = points_proj, aes(x = x, y = y, xend = x_proj, yend = y_proj), linetype = "dotted") +
  

  geom_point(data = points_proj, aes(x = x_proj, y = y_proj), color = "red", size = 3) +
  geom_text(data = points_proj, aes(x = x_proj + 0.2, y = y_proj - 0.1, label = name_proj), color = "red") +
  
  coord_equal(xlim = c(-3, 3), ylim = c(-2, 2)) +
  labs(title = "Projection sur PC1 : A', B', C', D'", x = "x", y = "y") +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

La deuxième composante (PC2) est orthogonale à PC1 et maximise la variance restante, donnant ainsi un nouvel axe qui capture d’autres dimensions de la variabilité des données. En termes plus simples, PC2 suit le même principe que PC1 mais est perpendiculaire à PC1, il présente donc une perspective différente sur les données.

library(ggplot2)

points_ex <- data.frame(
  name = c("A", "B", "C", "D"),
  x = c(-1.5, 2.0, -1.0, 2.0),
  y = c(-0.5, -1.0, 1.0, 0.5)
)


pc1_dir <- c(1, -0.5)
pc1_unit <- pc1_dir / sqrt(sum(pc1_dir^2))


pc2_unit <- c(-pc1_unit[2], pc1_unit[1])  # Rotate 90 degrees


project_point <- function(p, dir) {
  s <- sum(p * dir)
  return(s * dir)
}


projections_pc2 <- t(apply(points_ex[, c("x", "y")], 1, project_point, dir = pc2_unit))
colnames(projections_pc2) <- c("x_proj", "y_proj")
points_proj2 <- cbind(points_ex, projections_pc2)
points_proj2$name_proj <- paste0(points_proj2$name, "'")


ggplot() +
  geom_abline(intercept = 0, slope = pc2_unit[2] / pc2_unit[1], color = "purple", size = 1.2) +  # PC2

  geom_hline(yintercept = 0, linetype = "dashed", color = "gray") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "gray") +

  geom_point(data = points_proj2, aes(x = x, y = y), color = "blue", size = 3) +
  geom_text(data = points_proj2, aes(x = x + 0.2, y = y, label = name), color = "black") +

  geom_segment(data = points_proj2, aes(x = x, y = y, xend = x_proj, yend = y_proj), linetype = "dashed", color = "gray") +

  geom_point(data = points_proj2, aes(x = x_proj, y = y_proj), color = "red", size = 3) +
  geom_text(data = points_proj2, aes(x = x_proj + 0.2, y = y_proj - 0.1, label = name_proj), color = "red") +

  coord_equal(xlim = c(-3, 3), ylim = c(-2, 2)) +
  labs(title = "Projection sur PC2 : A', B', C', D'", x = "x", y = "y") +
  theme_minimal()

Nombre de composantes principales (PC) possibles

Soit une matrice de données \(X \in \mathbb{R}^{n \times p}\), avec : - \(n\) : le nombre d’individus (lignes) - \(p\) : le nombre de variables (colonnes)


Combien de PC peut-on extraire ?

Le nombre maximal de composantes principales est donné par :

\[ \boxed{\min(n - 1,\; p)} \]

Cela vient du fait que l’on travaille sur une matrice centrée \(X_{\text{bar}}\), dont le rang est au plus \(n - 1\), car on a retiré la moyenne à chaque colonne.


Exemple

Si \(X\) est une matrice \(4 \times 3\) (4 individus, 3 variables) :

\[ \text{Nombre maximal de PC} = \min(4 - 1, 3) = \boxed{3} \]

On ne peut donc extraire que 3 composantes principales :
\(PC_1\), \(PC_2\), \(PC_3\)


Pourquoi \(n - 1\) ?

Lorsqu’on centre les données, la somme des lignes devient nulle.
Cela diminue le rang de la matrice à :

\[ \text{rang}(X_{\text{bar}}) \leq n - 1 \]

PCA recherche des directions linéaires indépendantes (vecteurs propres), donc on ne peut pas dépasser ce rang.


Conclusion

\[ \boxed{ \text{Nombre de PC max} = \min(n - 1,\; p) } \]

Cela garantit que les composantes extraites sont linéairement indépendantes et pertinentes par rapport à la structure des données.

7A. Resoudre EIGEN (Avec la fonction eigen())

eig <- eigen(C_reduit)             #λ La variance maximale projetée sur un axe
valeurs_propres <- eig$values      # eigenvalues (λ) det(C_reduit-λ)=0 
vecteurs_propres <- eig$vectors    # eigenvectors (PC axes) C_re x v = λ x v
                                  # v une vecteur qui ne change pas ta sens quand C_re change
valeurs_propres
## [1] 2.2835972 0.5332636 0.1831392
vecteurs_propres
##            [,1]       [,2]       [,3]
## [1,] -0.5237465  0.8291094  0.1956203
## [2,] -0.6176134 -0.2114121 -0.7575346
## [3,] -0.5867225 -0.5175738  0.6227954
valeurs_propres/sum(valeurs_propres) #pourcentage des valeurs
## [1] 0.76119908 0.17775454 0.06104639

7B. Resoudre EIGEN (Sans la fonction eigen())

Méthode de la puissance itérée

Objectif

Trouver la valeur propre \(_{}\) et le vecteur propre \(v\) d’une matrice \(A\) tels que :

\[ Av = \lambda_{\text{max}} v \]


Hypothèses

  • \(A ^{n n}\) est une matrice carrée
  • \(A\) possède une valeur propre de plus grande valeur absolue, unique
  • Le vecteur initial n’est pas orthogonal au vecteur propre dominant

Algorithme : Power Iteration

1. Initialisation

  • Choisir un vecteur initial aléatoire \(x^{(0)} \in \mathbb{R}^n\) tel que \(\|x^{(0)}\| = 1\)

2. Répéter les étapes suivantes jusqu’à convergence :

  • Multiplication par la matrice : \[ y^{(k)} = A x^{(k)} \]

  • Normalisation du vecteur : \[ x^{(k+1)} = \frac{y^{(k)}}{\|y^{(k)}\|} \]

  • Estimation de la valeur propre (si nécessaire) : \[ \lambda^{(k)} = \frac{(x^{(k+1)})^\top A x^{(k+1)}}{(x^{(k+1)})^\top x^{(k+1)}} = (x^{(k+1)})^\top A x^{(k+1)} \quad \text{(car normalisé)} \]

  • Répéter jusqu’à ce que \(x^{(k+1)} x^{(k)}\) ou \(^{(k)}\) converge


Intuition géométrique

Supposons que \(A\) possède un ensemble de valeurs propres \(\lambda_1, \lambda_2, \dots, \lambda_n\) avec
\(|\lambda_1| > |\lambda_2| \geq \cdots \geq |\lambda_n|\),
et des vecteurs propres associés \(v_1, v_2, \dots, v_n\).

Le vecteur initial \(x^{(0)}\) peut s’écrire comme : \[ x^{(0)} = c_1 v_1 + c_2 v_2 + \cdots + c_n v_n \]

Après \(k\) multiplications avec \(A\), on obtient : \[ x^{(k)} = \frac{A^k x^{(0)}}{\|A^k x^{(0)}\|} = \frac{c_1 \lambda_1^k v_1 + c_2 \lambda_2^k v_2 + \cdots}{\|\cdot\|} \]

Comme \(_1^k\) domine largement les autres termes, on a : \[ x^{(k)} \rightarrow v_1 \quad \text{(la direction du vecteur propre associé à la plus grande valeur propre)} \]


Exemple de calcul détaillé

Considérons la matrice suivante : \[ A = \begin{pmatrix} 4 & 2 \\ 1 & 3 \end{pmatrix} \]

Étape 1 : Initialisation

On choisit : \[ x^{(0)} = \begin{pmatrix} 1 \\ 0 \end{pmatrix} \]


Étape 2 : Première itération

\[ y^{(0)} = A x^{(0)} = \begin{pmatrix} 4 \\ 1 \end{pmatrix}, \quad \|y^{(0)}\| = \sqrt{17} \]

\[ x^{(1)} = \frac{y^{(0)}}{\|y^{(0)}\|} = \frac{1}{\sqrt{17}} \begin{pmatrix} 4 \\ 1 \end{pmatrix} \]

\[ \lambda^{(0)} = (x^{(1)})^\top A x^{(1)} = \frac{1}{17} (4 \cdot 18 + 1 \cdot 7) = \frac{79}{17} \approx 4.647 \]


Étape 3 : Deuxième itération

\[ y^{(1)} = A x^{(1)} = \begin{pmatrix} 18 \\ 7 \end{pmatrix}, \quad \|y^{(1)}\| = \sqrt{373} \]

\[ x^{(2)} = \frac{y^{(1)}}{\|y^{(1)}\|} = \frac{1}{\sqrt{373}} \begin{pmatrix} 18 \\ 7 \end{pmatrix} \]

\[ \lambda^{(1)} = \frac{1}{373}(18 \cdot 94 + 7 \cdot 39) = \frac{1965}{373} \approx 5.267 \]


Conclusion

  • Approximation du vecteur propre dominant : \[ x^{(2)} \approx \frac{1}{\sqrt{373}} \begin{pmatrix} 18 \\ 7 \end{pmatrix} \]

  • Approximation de la plus grande valeur propre : \[ \lambda_{\text{max}} \approx 5.267 \]

  • En continuant les itérations, on converge vers : \[ \lambda_{\text{max}} = 5 \]

🔍 Objectif

Trouver les vecteurs propres \(v_1, v_2, , v_p\) et les valeurs propres associées \(_1, _2, , _p\) de la matrice de covariance \(C\), où :

\[ C v_k = \lambda_k v_k \]

avec \(_1 _2 _p\)


Limite de Power Iteration simple

La méthode Power Iteration ne donne que le vecteur propre dominant \(v_1\), c’est-à-dire celui associé à la valeur propre la plus grande \(_1\).
Si l’on répète naïvement la même méthode, on retombe sur le même vecteur \(v_1\), car celui-ci domine toujours la direction des données.


Trouver PC2, PC3… : méthode correcte

Étape 1 : Trouver PC1 (vecteur propre dominant)

\[ C v_1 = \lambda_1 v_1 \]

avec Power Iteration classique.


Étape 2 : Supprimer l’influence de \(v_1\) sur \(C\)

On utilise une technique appelée deflation, qui consiste à construire une nouvelle matrice \(C_1\) :

\[ C_1 = C - \lambda_1 v_1 v_1^\top \]

Ainsi, \(v_1\) n’est plus un vecteur propre dominant de \(C_1\), et on peut chercher le suivant.


Étape 3 : Trouver \(v_2\)

On applique à nouveau Power Iteration, mais cette fois sur \(C_1\), pour obtenir \(v_2\), le vecteur propre suivant :

\[ C_1 v_2 = \lambda_2 v_2 \]


Étape 4 : Répéter pour obtenir \(v_3, v_4, \)

À chaque étape, on met à jour la matrice :

\[ C_{k+1} = C_k - \lambda_k v_k v_k^\top \]

puis on applique Power Iteration sur cette nouvelle matrice pour obtenir \(v_{k+1}\).


Autre approche : Orthogonalisation

On peut aussi imposer que chaque nouveau vecteur trouvé soit orthogonal aux précédents, par exemple avec la méthode de Gram-Schmidt, ce qui permet d’assurer que les vecteurs propres obtenus sont bien perpendiculaires.


Résumé

Étape Action
1 Utiliser Power Iteration pour obtenir \(v_1\)
2 Déflater la matrice : \[C_1 = C - \lambda_1 v_1 v_1^\top\]
3 Répéter Power Iteration sur \(C_1\) pour trouver \(v_2\)
4+ Continuer jusqu’à obtention de toutes les PC

Cette méthode permet de construire progressivement toutes les composantes principales \(v_1, v_2, v_3, \), même en utilisant uniquement Power Iteration.

Remarques

  • La méthode converge si λ₁ est strictement plus grand que les autres
  • Elle est efficace pour des matrices grandes
  • Elle ne donne qu’une seule valeur propre à la fois
dot_prod <- function(a, b) {
  s <- 0
  for (i in seq_along(a)) s <- s + a[i] * b[i]
  s
}

# Normalisation vectorielle manuelle
norm_vec <- function(v) {
  sqrt(dot_prod(v, v))
}

# Power iteration  (trouver eigenpair max)
power_iter <- function(A, tol = 1e-12, maxit = 50000) {
  p <- ncol(A)
  q <- rep(1, p)
  q <- q / norm_vec(q)
  lam_old <- 0
  
  for (it in 1:maxit) {
    z <- numeric(p)
    for (i in 1:p) {
      s <- 0
      for (j in 1:p) s <- s + A[i, j] * q[j]
      z[i] <- s
    }
    q <- z / norm_vec(z)           #nomaliser 
    
    Az <- numeric(p)           #multiplier
    for (i in 1:p) {
      s <- 0
      for (j in 1:p) s <- s + A[i, j] * q[j]
      Az[i] <- s
    }
    
    lam <- dot_prod(q, Az)
    if (abs(lam - lam_old) < tol) break
    lam_old <- lam
  }
  
  list(value = lam, vector = q)
}

#  Power iteration + deflation
power_eig <- function(A, k = ncol(A), tol = 1e-12) {
  p <- ncol(A)
  lambdas <- numeric(k)
  V <- matrix(0, p, k)
  B <- A
  
  for (m in 1:k) {
    res <- power_iter(B, tol = tol)
    v <- res$vector
    λ <- res$value
    lambdas[m] <- λ
    V[, m] <- v
    
    # deflation B ← B - λ * v v^T
    for (i in 1:p) {
      for (j in 1:p) {
        B[i, j] <- B[i, j] - λ * v[i] * v[j]
      }
    }
  }
  
  ord <- order(lambdas, decreasing = TRUE)
  list(values = lambdas[ord], vectors = V[, ord])
}

eig_man <- power_eig(C_reduit, k = 3)
lambda_7B <- eig_man$values
V_7B      <- eig_man$vectors

#On fait cela pour que les signes soient les mêmes que 7A
for (j in 1:ncol(V_7B)) {
  produit <- dot_prod(vecteurs_propres[, j], V_7B[, j])
  if (produit < 0) V_7B[, j] <- -V_7B[, j]
}

lambda_7B
## [1] 2.2835972 0.5332636 0.1831392
V_7B
##            [,1]       [,2]       [,3]
## [1,] -0.5237466  0.8291092  0.1956189
## [2,] -0.6176134 -0.2114128 -0.7575342
## [3,] -0.5867224 -0.5175737  0.6227962

7C. La comparaison de resultats

Nous allons comparer

Les valeurs propres : λ

Les vecteurs propres : à l’aide de la similarité cosinus

Les résultats sont considérés comme identiques si l’erreur est inférieure à 1e-6.

compare_eig_results <- function(lambda_7A, V_7A, lambda_7B, V_7B, tol = 1e-6) {
  p <- length(lambda_7A)
  
  cat(" Comparer Eigenvalues:\n")
  for (i in 1:p) {
    diff <- abs(lambda_7A[i] - lambda_7B[i])
    cat(sprintf("λ%d: %.6f vs %.6f → ", i, lambda_7A[i], lambda_7B[i]))
    if (diff < tol) {
      cat("Meme\n")
    } else {
      cat(" Pas meme (|Δ| =", round(diff, 6), ")\n")
    }
  }
  
  cat("\n Comparer Eigenvectors (cosine similarity):\n")
  cosine_sim <- function(a, b) {
    s_ab <- sum(a * b)
    s_aa <- sum(a * a)
    s_bb <- sum(b * b)
    s_ab / sqrt(s_aa * s_bb)
  }
  
  for (i in 1:p) {
    sim <- cosine_sim(V_7A[, i], V_7B[, i])
    cat(sprintf("v%d: cosine similarity = %.6f → ", i, sim))
    if (abs(sim) > 1 - tol) {
      cat("Meme\n")
    } else {
      cat("Pas meme\n")
    }
  }
}

compare_eig_results(valeurs_propres, vecteurs_propres, lambda_7B, V_7B)
##  Comparer Eigenvalues:
## λ1: 2.283597 vs 2.283597 → Meme
## λ2: 0.533264 vs 0.533264 → Meme
## λ3: 0.183139 vs 0.183139 → Meme
## 
##  Comparer Eigenvectors (cosine similarity):
## v1: cosine similarity = 1.000000 → Meme
## v2: cosine similarity = 1.000000 → Meme
## v3: cosine similarity = 1.000000 → Meme

Comme nous le voyons les résultats du code 2 méthodes nous donnent des résultats identiques.

8A. PCA

Nous utilisons ici une matrice dont les vecteurs de taille et de poids ne sont pas une combinaison linéaire, ce qui donne un graphique avec 3 ACP. Dans ce cas, le pourcentage de PC1 + PC2 n’est pas égal à 100 %(voir la partie 7A ou la graphe), car PC3 contient le reste.

Dans 2 cas, lorsque nous mettons scale = TRUE (ou scale.unit = TRUE dans FactoMineR), chaque colonne du jeu de données est centrée-réduite.

Même si les données sont centrées-réduites (standardisées) c’est-à-dire que chaque variable a une moyenne = 0 et variance = 1 — la longueur des flèches sur le graphe des variables peut être différente. Chaque flèche représente une variable projetée sur les deux premières composantes principales (PC1 & PC2). Les coordonnées sont les corrélations entre la variable et les axes PC1, PC2. Pendant ce temps, les données ont 3 variables, il y a 3 axes principaux (PC1, PC2, PC3). Comme PC3 représente toujours des valeurs, nous ne pouvons pas utiliser PC1 et PC2 pour représenter toutes les données => longueur de la flèche < 1.

Comparaison de graphes

En raison de la comparaison dans la partie 7C, nous savons que 7A et 7B nous donneront les mêmes graphiques. Et les valeurs que nous voyons sur les graphiques correspondent au pourcentage que nous avons calculé en 7A(0.76119908 0.17775454 0.06104639) PC1(76.12%) et PC2(17.78%).

library(FactoMineR)
res.pca <- PCA(X, scale.unit = TRUE, ncp = 3, graph = FALSE)

#Graphique à colonnes des valeurs propres
barplot(res.pca$eig[, 1],
        main = "Eigenvalues", ylab = "value",
        names.arg = 1:nrow(res.pca$eig))

#Graphique individuel 
plot(res.pca,
     choix  = "ind",
     label  = "ind",          
     title  = "Individuals — PCA (PC1 vs PC2)")

#Variable circulaire 
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
eig <- res.pca$eig  
var1 <- round(eig[1, 2], 2)
var2 <- round(eig[2, 2], 2)

fviz_pca_var(res.pca,
             col.var = "black",
             title = "PCA graph of variables") +
  labs(
    x = paste0("PC1 (", var1, "%)"),
    y = paste0("PC2 (", var2, "%)")
  )

8B. Cas X2

Si nous utilisions une matrice avec un tel propriétaire, nous obtiendrions un graphique avec 2 ACP et PC1 + PC2 = 100 %.

Creer X2

X2 <- matrix(c(
  160, 170, 180, 200,   # taille = poid + 110 => combinasion lineaire
   50,  60,  70,  90,   # poid
   10,  16,   5,  20    # note
), nrow = 4, ncol = 3, byrow = FALSE)

rownames(X2) <- c("A", "B", "C", "D")
colnames(X2) <- c("taille", "poid", "note")

X2
##   taille poid note
## A    160   50   10
## B    170   60   16
## C    180   70    5
## D    200   90   20

PCAdeX2

library(FactoMineR)
res.pca2 <- PCA(X2, scale.unit = TRUE, ncp = 3, graph = FALSE)
eig <- res.pca$eig
va1 <- round(eig[1, 2], 2)
va2 <- round(eig[2, 2], 2)

#Graphique à colonnes des valeurs propres
barplot(res.pca2$eig[, 1],
        main = "Eigenvalues", ylab = "value",
        names.arg = 1:nrow(res.pca2$eig))

library(factoextra)


eig <- res.pca2$eig
pc1 <- round(eig[1, 2], 2)  
pc2 <- round(eig[2, 2], 2)  


fviz_pca_ind(res.pca2,
             label = "ind",        
             col.ind = "black",    
             title = "Individuals — PCA (PC1 vs PC2)") +
  labs(
    x = paste0("PC1 (", pc1, "%)"),
    y = paste0("PC2 (", pc2, "%)")
  )

fviz_pca_var(res.pca2,
             col.var = "black",
             label = "var",
             title = "Variables — PCA (circle of correlations)") +
  labs(x = paste0("PC1 (", pc1, "%)"),
     y = paste0("PC2 (", pc2, "%)"))

8C. Cas X3 – Variables orthogonales

Ici, comme les trois variables sont orthogonales, les trois composantes principales possèdent la même importance (valeurs propres égales). ## Creer X3

X3 <- matrix(c(
   1,  1,  1,
  -1, -1,  1,
   1, -1, -1,
  -1,  1, -1
), nrow = 4, byrow = TRUE)

colnames(X3) <- c("v1", "v2", "v3")
rownames(X3) <- LETTERS[1:4]

##PCAdeX3

library(FactoMineR)
library(factoextra)

# PCA
res.pca3 <- PCA(X3, scale.unit = TRUE, graph = FALSE)
eig <- res.pca3$eig


pc1 <- round(eig[1, 2], 2)
pc2 <- round(eig[2, 2], 2)
pc3 <- round(eig[3, 2], 2)


barplot(eig[, 1],
        names.arg = paste0("PC", 1:3),
        main = "Eigenvalues",
        ylab = "Valeurs propres")

fviz_pca_var(res.pca3,
             col.var = "black",
             label = "var",
             title = "Variables — PCA (circle of correlations)") +
  labs(
    x = paste0("PC1 (", pc1, "%)"),
    y = paste0("PC2 (", pc2, "%)")
  )