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.
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.
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.
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.
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.
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.
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.
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
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.
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)
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.
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} \]
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
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
\]
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
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 \]
Dans la matrice de corrélation, on utilise la variance pour normaliser la covariance, ce qui permet de rendre les coefficients comparables entre variables.
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.
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} \]
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
Matrice des données réduites : \(X_{\text{réduit}}\)
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) :
Pour passer de \(X\) à \(X_{\text{réduit}}\), on applique deux étapes :
Centrer :
\[
X_{\text{bar}} = X - \bar{X}
\]
Réduire :
\[
X_{\text{réduit}} = \frac{X - \bar{X}}{\sigma}
\] où \(\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} \]
On peut écrire cette opération de manière compacte :
\[ X_{\text{réduit}} = X_{\text{bar}} \cdot D_s^{-1} \]
où :
\[ 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} \]
Cela permet :
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
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)
À 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)
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 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\).
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.
# 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
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.
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
# 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
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()
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)
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.
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\)
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.
\[ \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.
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
Trouver la valeur propre \(_{}\) et le vecteur propre \(v\) d’une matrice \(A\) tels que :
\[ Av = \lambda_{\text{max}} v \]
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
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)} \]
Considérons la matrice suivante : \[ A = \begin{pmatrix} 4 & 2 \\ 1 & 3 \end{pmatrix} \]
On choisit : \[ x^{(0)} = \begin{pmatrix} 1 \\ 0 \end{pmatrix} \]
\[ 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 \]
\[ 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 \]
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 \]
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\)
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.
\[ C v_1 = \lambda_1 v_1 \]
avec Power Iteration classique.
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.
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 \]
À 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}\).
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.
| É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.
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
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.
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.
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, "%)")
)
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
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, "%)"))
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, "%)")
)
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} \] où \(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.