# Exercices et Corrigés Détaillés : Analyse en Composantes Principales (ACP) avec Python
## Exercice 1 : Concepts Fondamentaux de l'ACP
> [!definition] Définition
> L'Analyse en Composantes Principales (ACP) est une technique de réduction de dimensionnalité non supervisée. Elle vise à transformer un ensemble de variables corrélées en un nouvel ensemble de variables non corrélées, appelées *composantes principales*, tout en retenant la majeure partie de la variance des données originales.
1. Quel est l'objectif principal de l'ACP ?
2. Mentionnez deux propriétés clés des composantes principales.
## Exercice 2 : Calcul de la Matrice de Covariance
Considérons le jeu de données simple suivant, représenté par une matrice $X$:
$
X = \begin{pmatrix}
1 & 2 \\
3 & 4 \\
5 & 6
\end{pmatrix}
$
où la première colonne représente la variable $V_1$ et la seconde la variable $V_2$.
1. Calculez la matrice de covariance de $X$ à la main. Détaillez chaque étape du calcul.
2. Vérifiez votre résultat en utilisant la fonction `np.cov` de la bibliothèque `numpy`.
> [!note] Rappel
> La matrice de covariance $C$ d'un ensemble de données $X$ (centré) est donnée par $C = \frac{1}{n-1} X^T X$, où $n$ est le nombre d'observations. Pour un jeu de données non centré, il faut d'abord centrer les données en soustrayant la moyenne de chaque colonne.
## Exercice 3 : Première ACP avec Scikit-learn
Vous disposez d'un petit jeu de données synthétique `data` avec 3 variables et 5 observations :
```python
import numpy as np
data = np.array([
[10, 2, 8],
[12, 3, 9],
[8, 1, 7],
[11, 2.5, 8.5],
[9, 1.5, 7.5]
])
```
1. Appliquez une ACP sur ce jeu de données en utilisant `sklearn.decomposition.PCA`.
2. Affichez la variance expliquée par chaque composante principale.
3. Affichez la variance cumulée expliquée.
## Exercice 4 : Impact de la Standardisation sur l'ACP
Reprenez le jeu de données de l'Exercice 3.
1. Standardisez les données (centrage et réduction) avant d'appliquer l'ACP.
2. Réalisez l'ACP sur les données standardisées.
3. Comparez la variance expliquée par chaque composante principale avec celle obtenue à l'Exercice 3 (sans standardisation).
4. Discutez brièvement de l'impact de la standardisation sur les résultats de l'ACP.
> [!warning] Attention
> La standardisation est une étape cruciale en ACP, surtout lorsque les variables originales ont des échelles ou des unités de mesure très différentes. Omettre cette étape peut conduire à des résultats biaisés où les variables avec la plus grande variance (souvent celles avec les plus grandes échelles) dominent les premières composantes principales.
## Exercice 5 : Projection de Nouvelles Données
En utilisant le modèle ACP entraîné sur les *données standardisées* de l'Exercice 4 :
1. Créez une nouvelle observation : `new_observation = np.array([10.5, 2.2, 8.2])`.
2. Standardisez cette nouvelle observation en utilisant le même `StandardScaler` entraîné sur les données d'entraînement.
3. Projetez cette observation standardisée sur les composantes principales.
4. Affichez les coordonnées de la nouvelle observation dans l'espace des composantes principales.
## Exercice 6 : Interprétation du Scree Plot
À partir des résultats de l'ACP sur les *données standardisées* de l'Exercice 4 :
1. Créez un *scree plot* (diagramme des éboulis) qui visualise la variance expliquée par chaque composante principale.
2. En utilisant le critère du "coude" (ou "elbow method"), déterminez le nombre optimal de composantes principales à retenir. Justifiez votre choix.
> [!tip] Astuce
> Le *scree plot* est un graphique des valeurs propres (variance expliquée) en fonction du numéro de la composante principale. Le "coude" est le point où la pente de la courbe diminue significativement, suggérant que les composantes suivantes expliquent beaucoup moins de variance.
## Exercice 7 : Analyse des Contributions des Variables
En utilisant le modèle ACP entraîné sur les *données standardisées* de l'Exercice 4 :
1. Affichez les coefficients des variables originales pour les deux premières composantes principales (ce sont les vecteurs propres).
2. Interprétez la signification de la première composante principale en fonction des contributions des variables originales.
3. Interprétez la signification de la deuxième composante principale.
## Exercice 8 : ACP Complète sur le Jeu de Données Iris
Le jeu de données Iris est un classique en Machine Learning, contenant les mesures de quatre caractéristiques de fleurs d'iris (longueur et largeur de sépale, longueur et largeur de pétale) pour trois espèces différentes.
1. Chargez le jeu de données Iris depuis `sklearn.datasets`. Séparez les caractéristiques ($X$) des étiquettes ($y$).
2. Standardisez les caractéristiques $X$.
3. Appliquez une ACP sur les données standardisées en retenant un nombre de composantes suffisant pour expliquer au moins 95% de la variance cumulée.
4. Visualisez les deux premières composantes principales sur un nuage de points, en colorant les points selon l'espèce d'iris.
5. Commentez la séparation des espèces observée dans le plan des deux premières composantes.
## Exercice 9 : ACP avec des Échelles de Variables Hétérogènes
Créez un jeu de données synthétique avec 100 observations et 3 variables :
- `Age` (en années, ex: 20-70)
- `Salaire` (en milliers d'euros, ex: 20-100)
- `Heures_Sport` (en heures par semaine, ex: 0-10)
1. Générez ces données avec des distributions réalistes (vous pouvez utiliser `np.random.rand` et ajuster les échelles).
2. Appliquez une ACP sur ces données **sans standardisation**. Affichez la variance expliquée par chaque composante.
3. Appliquez une ACP sur ces données **avec standardisation**. Affichez la variance expliquée par chaque composante.
4. Comparez et discutez qualitativement et quantitativement l'impact de la standardisation sur les résultats de l'ACP. Expliquez pourquoi la standardisation est cruciale dans ce scénario.
## Exercice 10 : Cercle de Corrélation et Ingénierie de Caractéristiques
En utilisant le modèle ACP entraîné sur le jeu de données Iris (Exercice 8) :
1. Calculez les corrélations entre les variables originales (standardisées) et les deux premières composantes principales.
2. Créez un cercle de corrélation pour visualiser ces corrélations. Représentez chaque variable originale comme un vecteur.
3. Interprétez le cercle de corrélation :
* Que signifient la longueur et l'angle des vecteurs ?
* Quelles variables contribuent le plus à la première composante ? Et à la deuxième ?
* Y a-t-il des variables fortement corrélées entre elles ?
4. Discutez comment les deux premières composantes principales pourraient être utilisées comme de nouvelles caractéristiques pour un modèle de classification des espèces d'iris.
5.
# Corrigés Détaillés
## Corrigé 1
1. **Objectif principal de l'ACP :**
> [!definition] Objectif de l'ACP
> L'objectif principal de l'ACP est de **réduire la dimensionnalité** d'un jeu de données en transformant les variables originales en un nouvel ensemble de variables, appelées **composantes principales (CP)**. Ces CP sont des combinaisons linéaires des variables originales, sont non corrélées entre elles, et sont ordonnées de manière à ce que la première CP explique la plus grande variance possible des données, la deuxième CP la deuxième plus grande variance, et ainsi de suite. L'objectif est de capturer la majeure partie de l'information (variance) des données avec un nombre réduit de dimensions.
2. **Deux propriétés clés des composantes principales :**
> [!theorem] Propriétés des Composantes Principales
> 1. **Orthogonalité / Non-corrélation :** Les composantes principales sont mutuellement orthogonales (ou non corrélées). Cela signifie qu'elles capturent des directions de variance indépendantes dans les données, évitant la redondance d'information.
> 1. **Maximisation de la Variance :** Chaque composante principale successive est choisie pour expliquer la plus grande variance restante possible dans les données, sous la contrainte d'être orthogonale aux composantes précédentes. La première CP capture la direction de variance maximale, la deuxième la deuxième direction de variance maximale orthogonale à la première, etc.
## Corrigé 2
1. **Calcul manuel de la matrice de covariance :**
Le jeu de données $X$ est :
$
X = \begin{pmatrix}
1 & 2 \\
3 & 4 \\
5 & 6
\end{pmatrix}
$
Il y a $n=3$ observations et $p=2$ variables.
**Étape 1 : Calculer la moyenne de chaque variable.**
Moyenne de $V_1$: $\mu_1 = \frac{1+3+5}{3} = \frac{9}{3} = 3$
Moyenne de $V_2$: $\mu_2 = \frac{2+4+6}{3} = \frac{12}{3} = 4$
Vecteur des moyennes : $\mu = \begin{pmatrix} 3 \\ 4 \end{pmatrix}$
**Étape 2 : Centrer les données.**
Soustraire la moyenne de chaque variable à ses observations respectives.
$
X_{centré} = \begin{pmatrix}
1-3 & 2-4 \\
3-3 & 4-4 \\
5-3 & 6-4
\end{pmatrix} = \begin{pmatrix}
-2 & -2 \\
0 & 0 \\
2 & 2
\end{pmatrix}
$
**Étape 3 : Calculer la matrice de covariance.**
La formule de la matrice de covariance $C$ pour $X_{centré}$ est $C = \frac{1}{n-1} X_{centré}^T X_{centré}$.
Dans notre cas, $n-1 = 3-1 = 2$.
$
X_{centré}^T = \begin{pmatrix}
-2 & 0 & 2 \\
-2 & 0 & 2
\end{pmatrix}
$
$
X_{centré}^T X_{centré} = \begin{pmatrix}
-2 & 0 & 2 \\
-2 & 0 & 2
\end{pmatrix}
\begin{pmatrix}
-2 & -2 \\
0 & 0 \\
2 & 2
\end{pmatrix}
= \begin{pmatrix}
(-2)(-2)+0(0)+2(2) & (-2)(-2)+0(0)+2(2) \\
(-2)(-2)+0(0)+2(2) & (-2)(-2)+0(0)+2(2)
\end{pmatrix}
$
$
= \begin{pmatrix}
4+0+4 & 4+0+4 \\
4+0+4 & 4+0+4
\end{pmatrix}
= \begin{pmatrix}
8 & 8 \\
8 & 8
\end{pmatrix}
$
Enfin, diviser par $n-1 = 2$:
$
C = \frac{1}{2} \begin{pmatrix}
8 & 8 \\
8 & 8
\end{pmatrix} = \begin{pmatrix}
4 & 4 \\
4 & 4
\end{pmatrix}
$
La variance de $V_1$ est 4, la variance de $V_2$ est 4, et la covariance entre $V_1$ et $V_2$ est 4.
2. **Vérification avec `numpy.cov` :**
```python
import numpy as np
data = np.array([
[1, 2],
[3, 4],
[5, 6]
])
# np.cov attend les variables en lignes, donc nous devons transposer la matrice
covariance_matrix = np.cov(data.T)
print("Matrice de covariance calculée avec NumPy:")
print(covariance_matrix)
```
```
Matrice de covariance calculée avec NumPy:
[[4. 4.]
[4. 4.]]
```
> [!note] Vérification
> Le résultat de `numpy.cov` correspond parfaitement à notre calcul manuel, confirmant la justesse des étapes.
## Corrigé 3
```python
import numpy as np
from sklearn.decomposition import PCA
data = np.array([
[10, 2, 8],
[12, 3, 9],
[8, 1, 7],
[11, 2.5, 8.5],
[9, 1.5, 7.5]
])
# 1. Appliquer une ACP
# Nous n'avons pas besoin de spécifier n_components ici pour voir toutes les composantes
pca = PCA()
pca.fit(data)
# 2. Afficher la variance expliquée par chaque composante principale
explained_variance_ratio = pca.explained_variance_ratio_
print("Variance expliquée par chaque composante principale:")
for i, var in enumerate(explained_variance_ratio):
print(f" CP{i+1}: {var:.4f}")
# 3. Afficher la variance cumulée expliquée
cumulative_explained_variance = np.cumsum(explained_variance_ratio)
print("\nVariance cumulée expliquée:")
for i, cum_var in enumerate(cumulative_explained_variance):
print(f" Jusqu'à CP{i+1}: {cum_var:.4f}")
```
```
Variance expliquée par chaque composante principale:
CP1: 0.9997
CP2: 0.0003
CP3: 0.0000
Variance cumulée expliquée:
Jusqu'à CP1: 0.9997
Jusqu'à CP2: 1.0000
Jusqu'à CP3: 1.0000
```
> [!example] Interprétation
> On observe que la première composante principale (CP1) explique une très grande majorité de la variance (près de 99.97%). La deuxième composante (CP2) explique le reste, et la troisième (CP3) n'explique presque rien. Cela suggère une forte corrélation entre les variables originales et qu'une seule composante principale pourrait suffire à représenter la quasi-totalité de l'information.
## Corrigé 4
```python
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
data = np.array([
[10, 2, 8],
[12, 3, 9],
[8, 1, 7],
[11, 2.5, 8.5],
[9, 1.5, 7.5]
])
# 1. Standardiser les données
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
print("Données standardisées (premières 3 lignes):")
print(data_scaled[:3])
# 2. Réaliser l'ACP sur les données standardisées
pca_scaled = PCA()
pca_scaled.fit(data_scaled)
explained_variance_ratio_scaled = pca_scaled.explained_variance_ratio_
print("\nVariance expliquée par chaque composante principale (données standardisées):")
for i, var in enumerate(explained_variance_ratio_scaled):
print(f" CP{i+1}: {var:.4f}")
# Pour la comparaison, reprenons les résultats de l'Exercice 3 (sans standardisation)
pca_unscaled = PCA()
pca_unscaled.fit(data)
explained_variance_ratio_unscaled = pca_unscaled.explained_variance_ratio_
# 3. Comparer les variances expliquées
print("\nComparaison des variances expliquées (standardisées vs non standardisées):")
print(" Non standardisées: ", [f"{v:.4f}" for v in explained_variance_ratio_unscaled])
print(" Standardisées: ", [f"{v:.4f}" for v in explained_variance_ratio_scaled])
# 4. Discuter l'impact de la standardisation
```
```
Données standardisées (premières 3 lignes):
[[-0.24253563 0. 0. ]
[ 1.21267816 1.22474487 1.22474487]
[-1.21267816 -1.22474487 -1.22474487]]
Variance expliquée par chaque composante principale (données standardisées):
CP1: 0.9999
CP2: 0.0001
CP3: 0.0000
Comparaison des variances expliquées (standardisées vs non standardisées):
Non standardisées: ['0.9997', '0.0003', '0.0000']
Standardisées: ['0.9999', '0.0001', '0.0000']
```
> [!note] Discussion sur l'impact de la standardisation
> Dans cet exemple précis, l'impact de la standardisation sur la *proportion* de variance expliquée par chaque composante est minime. La première composante explique toujours presque toute la variance. Ceci est dû au fait que les variables originales de notre jeu de données synthétique sont déjà très fortement corrélées et ont des échelles relativement similaires.
>
> Cependant, il est crucial de comprendre que si les variables avaient des échelles très différentes (par exemple, une variable allant de 0 à 10 et une autre de 0 à 1 000 000), les variables avec les plus grandes échelles (et donc les plus grandes variances) domineraient l'ACP sans standardisation. La standardisation assure que toutes les variables contribuent également à l'analyse, en leur donnant une variance unitaire. C'est pourquoi la standardisation est une étape **presque toujours nécessaire** avant d'appliquer l'ACP.
## Corrigé 5
```python
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
data = np.array([
[10, 2, 8],
[12, 3, 9],
[8, 1, 7],
[11, 2.5, 8.5],
[9, 1.5, 7.5]
])
# Entraîner le StandardScaler et le PCA sur les données originales
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
pca_scaled = PCA()
pca_scaled.fit(data_scaled)
# 1. Nouvelle observation
new_observation = np.array([10.5, 2.2, 8.2])
print("Nouvelle observation originale:", new_observation)
# 2. Standardiser la nouvelle observation
# Il est crucial d'utiliser le même scaler qui a été entraîné sur les données d'entraînement.
# reshape(-1, 1) ou (1, -1) est nécessaire pour un seul échantillon
new_observation_scaled = scaler.transform(new_observation.reshape(1, -1))
print("Nouvelle observation standardisée:", new_observation_scaled)
# 3. Projeter l'observation standardisée sur les composantes principales
new_observation_pca = pca_scaled.transform(new_observation_scaled)
# 4. Afficher les coordonnées
print("\nCoordonnées de la nouvelle observation dans l'espace des CP:")
print(new_observation_pca)
```
```
Nouvelle observation originale: [10.5 2.2 8.2]
Nouvelle observation standardisée: [[ 0.24253563 0.24494897 0.24494897]]
Coordonnées de la nouvelle observation dans l'espace des CP:
[[0.42340552 0.00000000 0. ]]
```
> [!note] Interprétation
> Les coordonnées de la nouvelle observation dans l'espace des CP sont `[0.4234, 0.0000, 0.0000]`. Cela signifie que la nouvelle observation est principalement représentée par sa projection sur la première composante principale, avec une contribution quasi nulle des autres composantes. Ce résultat est cohérent avec la forte dominance de la première CP observée dans les exercices précédents.
## Corrigé 6
```python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
data = np.array([
[10, 2, 8],
[12, 3, 9],
[8, 1, 7],
[11, 2.5, 8.5],
[9, 1.5, 7.5]
])
# Standardiser les données
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# Réaliser l'ACP
pca_scaled = PCA()
pca_scaled.fit(data_scaled)
explained_variance_ratio_scaled = pca_scaled.explained_variance_ratio_
# 1. Créer un scree plot
plt.figure(figsize=(8, 5))
plt.plot(range(1, len(explained_variance_ratio_scaled) + 1), explained_variance_ratio_scaled, marker='o', linestyle='--')
plt.title('Scree Plot (Variance expliquée par CP)')
plt.xlabel('Numéro de la Composante Principale')
plt.ylabel('Proportion de Variance Expliquée')
plt.grid(True)
plt.xticks(range(1, len(explained_variance_ratio_scaled) + 1))
plt.show()
plt.figure(figsize=(8, 5))
plt.plot(range(1, len(explained_variance_ratio_scaled) + 1), np.cumsum(explained_variance_ratio_scaled), marker='o', linestyle='-')
plt.title('Scree Plot (Variance Cumulée Expliquée)')
plt.xlabel('Numéro de la Composante Principale')
plt.ylabel('Proportion de Variance Cumulée Expliquée')
plt.grid(True)
plt.xticks(range(1, len(explained_variance_ratio_scaled) + 1))
plt.axhline(y=0.95, color='r', linestyle=':', label='95% de variance')
plt.legend()
plt.show()
# 2. Déterminer le nombre optimal de composantes principales
print("Variance expliquée par chaque CP:", explained_variance_ratio_scaled)
print("Variance cumulée expliquée:", np.cumsum(explained_variance_ratio_scaled))
```
```
Variance expliquée par chaque CP: [9.99899388e-01 1.00611746e-04 1.11022302e-16]
Variance cumulée expliquée: [0.99989939 1.00000000 1.00000000]
```
> [!example] Justification du choix
> En observant le *scree plot* de la variance expliquée par chaque composante, on constate un "coude" très prononcé après la première composante principale. La CP1 explique presque 100% de la variance, tandis que la CP2 et la CP3 expliquent des proportions négligeables.
>
> Selon le critère du coude, le nombre optimal de composantes à retenir est **1**. Une seule composante principale est suffisante pour capturer la quasi-totalité de l'information (variance) de ce jeu de données.
## Corrigé 7
```python
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import pandas as pd
data = np.array([
[10, 2, 8],
[12, 3, 9],
[8, 1, 7],
[11, 2.5, 8.5],
[9, 1.5, 7.5]
])
feature_names = ['Variable_A', 'Variable_B', 'Variable_C']
# Standardiser les données
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# Réaliser l'ACP
pca_scaled = PCA()
pca_scaled.fit(data_scaled)
# 1. Afficher les coefficients des variables originales pour les deux premières composantes principales
components_df = pd.DataFrame(pca_scaled.components_, columns=feature_names, index=['CP1', 'CP2', 'CP3'])
print("Coefficients (vecteurs propres) des variables pour chaque Composante Principale:")
print(components_df.iloc[:2]) # Afficher seulement les 2 premières CP
```
```
Coefficients (vecteurs propres) des variables pour chaque Composante Principale:
Variable_A Variable_B Variable_C
CP1 0.577350 0.577350 0.577350
CP2 0.816497 -0.408248 -0.408248
```
> [!example] Interprétation de la CP1
> Les coefficients pour la CP1 sont `[0.577350, 0.577350, 0.577350]` pour `Variable_A`, `Variable_B` et `Variable_C` respectivement. Tous les coefficients sont positifs et de valeur égale. Cela signifie que la première composante principale est une combinaison linéaire où toutes les variables originales contribuent de manière égale et dans la même direction.
>
> **Signification de la CP1 :** La CP1 représente une sorte de "taille" ou "niveau général" des observations. Si les trois variables mesurent des aspects similaires (par exemple, des scores à différents tests ou des dimensions d'un objet), la CP1 capturerait la variation commune à ces trois variables, indiquant que les observations ayant des valeurs élevées sur la CP1 ont tendance à avoir des valeurs élevées sur toutes les variables originales, et vice-versa.
> [!example] Interprétation de la CP2
> Les coefficients pour la CP2 sont `[0.816497, -0.408248, -0.408248]`. La `Variable_A` a un coefficient positif fort, tandis que `Variable_B` et `Variable_C` ont des coefficients négatifs de magnitude égale.
>
> **Signification de la CP2 :** La CP2 capture une opposition entre `Variable_A` d'un côté et `Variable_B` et `Variable_C` de l'autre. Une observation ayant une valeur élevée sur la CP2 aurait tendance à avoir une `Variable_A` élevée et des `Variable_B` et `Variable_C` faibles (par rapport à la moyenne). Inversement, une valeur faible sur la CP2 indiquerait une `Variable_A` faible et des `Variable_B` et `Variable_C` élevées. Cette composante représente une dimension de "différence" ou de "contraste" entre la première variable et les deux autres.
## Corrigé 8
```python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
# 1. Charger le jeu de données Iris
iris = load_iris()
X = iris.data
y = iris.target
target_names = iris.target_names
feature_names = iris.feature_names
print("Dimensions des données Iris (X):", X.shape)
print("Noms des caractéristiques:", feature_names)
print("Noms des espèces:", target_names)
# 2. Standardiser les caractéristiques X
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print("\nDonnées Iris standardisées (premières 3 lignes):")
print(X_scaled[:3])
# 3. Appliquer une ACP en retenant un nombre de composantes suffisant pour au moins 95% de la variance
pca = PCA()
pca.fit(X_scaled)
explained_variance_ratio = pca.explained_variance_ratio_
cumulative_explained_variance = np.cumsum(explained_variance_ratio)
print("\nVariance expliquée par chaque CP:", explained_variance_ratio)
print("Variance cumulée expliquée:", cumulative_explained_variance)
# Déterminer le nombre de composantes pour 95% de variance
n_components_95 = np.where(cumulative_explained_variance >= 0.95)[0][0] + 1
print(f"\nNombre de composantes pour expliquer au moins 95% de la variance: {n_components_95}")
# Réappliquer l'ACP avec le nombre de composantes choisi
pca_final = PCA(n_components=n_components_95)
X_pca = pca_final.fit_transform(X_scaled)
print(f"Dimensions des données après ACP ({n_components_95} CP):", X_pca.shape)
# 4. Visualiser les deux premières composantes principales
plt.figure(figsize=(10, 7))
colors = ['r', 'g', 'b']
lw = 2
for color, i, target_name in zip(colors, [0, 1, 2], target_names):
plt.scatter(X_pca[y == i, 0], X_pca[y == i, 1], color=color, alpha=.8, lw=lw,
label=target_name)
plt.legend(loc='best', shadow=False, scatterpoints=1)
plt.title('ACP du jeu de données Iris (2 premières Composantes Principales)')
plt.xlabel('Composante Principale 1')
plt.ylabel('Composante Principale 2')
plt.grid(True)
plt.show()
# 5. Commenter la séparation des espèces
```
```
Dimensions des données Iris (X): (150, 4)
Noms des caractéristiques: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
Noms des espèces: ['setosa' 'versicolor' 'virginica']
Données Iris standardisées (premières 3 lignes):
[[-0.90068129 1.01900435 -1.34022726 -1.31544403]
[-1.14301705 -0.13197948 -1.34022726 -1.31544403]
[-1.38535281 0.32841403 -1.39239855 -1.31544403]]
Variance expliquée par chaque CP: [0.72962445 0.22850761 0.03668922 0.00517871]
Variance cumulée expliquée: [0.72962445 0.95813206 0.99482129 1. ]
Nombre de composantes pour expliquer au moins 95% de la variance: 2
Dimensions des données après ACP (2 CP): (150, 2)
```
> [!example] Commentaire sur la séparation des espèces
> La visualisation des deux premières composantes principales montre une excellente séparation des espèces d'iris :
> * L'espèce **Setosa** (rouge) est clairement distincte des deux autres, formant un cluster bien séparé sur la gauche du graphique.
> * Les espèces **Versicolor** (verte) et **Virginica** (bleue) sont plus proches l'une de l'autre, mais on peut observer une tendance à la séparation, avec Versicolor plutôt au centre et Virginica tendant vers la droite. Bien qu'il y ait un léger chevauchement, les deux premières composantes principales sont très efficaces pour différencier ces trois espèces.
>
> La première composante principale (axe horizontal) semble principalement distinguer Setosa des autres, tandis que la deuxième composante (axe vertical) aide à séparer Versicolor de Virginica. L'ACP a réussi à réduire les 4 dimensions originales à 2 dimensions tout en conservant une grande partie de l'information discriminante.
## Corrigé 9
```python
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# 1. Générer les données synthétiques
np.random.seed(42)
n_samples = 100
# Age (20-70 ans)
age = np.random.normal(loc=45, scale=10, size=n_samples)
age = np.clip(age, 20, 70).astype(int)
# Salaire (20-100 milliers d'euros)
# Corrélation positive avec l'âge pour plus de réalisme
salaire = 0.8 * age + np.random.normal(loc=0, scale=15, size=n_samples) + 10
salaire = np.clip(salaire, 20, 100).astype(int)
# Heures_Sport (0-10 heures par semaine)
# Corrélation négative avec l'âge
heures_sport = -0.1 * age + np.random.normal(loc=5, scale=2, size=n_samples)
heures_sport = np.clip(heures_sport, 0, 10).astype(int)
data_heterogeneous = pd.DataFrame({
'Age': age,
'Salaire': salaire,
'Heures_Sport': heures_sport
})
print("Aperçu des données hétérogènes (premières 5 lignes):")
print(data_heterogeneous.head())
print("\nStatistiques descriptives:")
print(data_heterogeneous.describe())
# 2. ACP sans standardisation
pca_unscaled = PCA()
pca_unscaled.fit(data_heterogeneous)
print("\n--- ACP SANS STANDARDISATION ---")
print("Variance expliquée par chaque CP (sans standardisation):")
for i, var in enumerate(pca_unscaled.explained_variance_ratio_):
print(f" CP{i+1}: {var:.4f}")
# Afficher les composantes pour voir l'influence des variables
components_unscaled_df = pd.DataFrame(pca_unscaled.components_, columns=data_heterogeneous.columns, index=['CP1', 'CP2', 'CP3'])
print("\nCoefficients des variables pour chaque CP (sans standardisation):")
print(components_unscaled_df)
# 3. ACP avec standardisation
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data_heterogeneous)
pca_scaled = PCA()
pca_scaled.fit(data_scaled)
print("\n--- ACP AVEC STANDARDISATION ---")
print("Variance expliquée par chaque CP (avec standardisation):")
for i, var in enumerate(pca_scaled.explained_variance_ratio_):
print(f" CP{i+1}: {var:.4f}")
# Afficher les composantes pour voir l'influence des variables
components_scaled_df = pd.DataFrame(pca_scaled.components_, columns=data_heterogeneous.columns, index=['CP1', 'CP2', 'CP3'])
print("\nCoefficients des variables pour chaque CP (avec standardisation):")
print(components_scaled_df)
# 4. Comparaison et discussion
```
```
Aperçu des données hétérogènes (premières 5 lignes):
Age Salaire Heures_Sport
0 49 50 0
1 47 51 0
2 45 53 0
3 50 51 0
4 50 50 0
Statistiques descriptives:
Age Salaire Heures_Sport
count 100.000000 100.000000 100.000000
mean 45.690000 47.010000 0.970000
std 9.712613 13.731427 1.916843
min 20.000000 20.000000 0.000000
25% 39.000000 36.750000 0.000000
50% 46.000000 47.000000 0.000000
75% 53.000000 56.000000 0.000000
max 70.000000 94.000000 10.000000
--- ACP SANS STANDARDISATION ---
Variance expliquée par chaque CP (sans standardisation):
CP1: 0.9859
CP2: 0.0139
CP3: 0.0002
Coefficients des variables pour chaque CP (sans standardisation):
Age Salaire Heures_Sport
CP1 0.000163 0.999999 0.000000
CP2 0.999999 -0.000163 0.000000
CP3 0.000000 0.000000 1.000000
--- ACP AVEC STANDARDISATION ---
Variance expliquée par chaque CP (avec standardisation):
CP1: 0.5898
CP2: 0.3957
CP3: 0.0145
Coefficients des variables pour chaque CP (avec standardisation):
Age Salaire Heures_Sport
CP1 0.697410 0.697087 -0.160100
CP2 -0.158485 0.159495 0.974465
CP3 0.697960 -0.698188 -0.000000
```
> [!warning] Discussion sur l'impact de la standardisation
> **ACP sans standardisation :**
> * **Quantitativement :** La première composante principale (CP1) explique 98.59% de la variance totale, ce qui est une proportion écrasante. Les autres CP expliquent très peu.
> * **Qualitativement :** En examinant les coefficients de la CP1, on constate que la variable `Salaire` a un coefficient de `0.999999`, tandis que `Age` et `Heures_Sport` ont des coefficients quasi nuls. Cela signifie que la CP1 est presque exclusivement déterminée par la variable `Salaire`. Les variables `Age` et `Heures_Sport` sont ignorées car leur variance est beaucoup plus faible que celle du `Salaire`.
> * **Problème :** `Salaire` (allant jusqu'à 100) a une échelle beaucoup plus grande que `Age` (jusqu'à 70) et surtout `Heures_Sport` (jusqu'à 10). Sans standardisation, l'ACP est dominée par la variable ayant la plus grande variance, ce qui masque l'information potentielle des autres variables et peut conduire à une interprétation biaisée.
> [!note] ACP avec standardisation :
> * **Quantitativement :** La distribution de la variance expliquée est beaucoup plus équilibrée. La CP1 explique 58.98% de la variance, et la CP2 explique 39.57%. Ensemble, elles expliquent environ 98.55% de la variance, ce qui est très efficace.
> * **Qualitativement :**
> * **CP1 :** Les coefficients pour `Age` (0.697) et `Salaire` (0.697) sont positifs et de magnitudes similaires, tandis que `Heures_Sport` a un coefficient négatif (-0.160) mais plus faible. La CP1 représente donc une dimension de "prospérité" ou "maturité" où l'âge et le salaire vont de pair, et sont légèrement opposés aux heures de sport.
> * **CP2 :** Les coefficients pour `Heures_Sport` (0.974) est très fort et positif, tandis que `Age` (-0.158) et `Salaire` (0.159) sont faibles. La CP2 représente donc principalement la dimension des "Heures_Sport", capturant la variance non expliquée par la CP1, et montrant une légère opposition entre l'âge/salaire et le sport.
> * **Avantage :** La standardisation a permis à toutes les variables de contribuer équitablement à la détermination des composantes principales. Les CP reflètent maintenant des structures de variance plus significatives et multidimensionnelles des données, plutôt que d'être simplement des proxys de la variable à la plus grande échelle.
> [!conclusion] Conclusion
> Cet exercice démontre de manière flagrante pourquoi la standardisation est **cruciale** en ACP lorsque les variables ont des échelles différentes. Sans elle, l'ACP se contente de reproduire la variable avec la plus grande variance, perdant l'intérêt de la réduction de dimensionnalité et de l'extraction de caractéristiques synthétiques. Avec la standardisation, les composantes principales représentent des axes de variation plus complexes et informatifs, reflétant les corrélations sous-jacentes entre les variables.
## Corrigé 10
```python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
import pandas as pd
# Charger le jeu de données Iris
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
# Standardiser les caractéristiques X
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Appliquer une ACP avec 2 composantes
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 1. Calculer les corrélations entre les variables originales (standardisées) et les deux premières composantes principales
# Les composantes principales elles-mêmes sont les colonnes de X_pca
# Les variables originales standardisées sont X_scaled
# La corrélation entre une variable originale et une CP est donnée par le coefficient de la variable dans la CP (vecteur propre)
# multiplié par la racine carrée de la valeur propre de cette CP.
# Ou plus simplement, la corrélation est le cosinus de l'angle entre le vecteur de la variable et l'axe de la CP.
# Pour le cercle de corrélation, on utilise souvent les coordonnées des variables sur les axes factoriels,
# qui sont les éléments de pca.components_ multipliés par la racine carrée de explained_variance_ (valeurs propres).
# Ou, plus simplement, la corrélation entre les variables originales (standardisées) et les CP.
# Calcul des coordonnées des variables sur les axes factoriels (cercle de corrélation)
# Chaque ligne de pca.components_ est un vecteur propre (une CP)
# Chaque colonne de pca.components_ correspond à une variable originale
# Donc, pca.components_ est de forme (n_components, n_features)
# Pour obtenir les coordonnées des variables, il faut transposer et multiplier par la racine des valeurs propres.
# La corrélation entre une variable standardisée et une CP est simplement le coefficient de la variable dans la CP.
# Ou, plus rigoureusement, les coordonnées des variables sur le cercle de corrélation sont les corrélations
# entre les variables originales et les composantes principales.
# On peut les obtenir en multipliant les vecteurs propres par la racine carrée des valeurs propres.
# Ou simplement en utilisant pca.components_ si on considère que les variables sont unitaires.
# Pour un cercle de corrélation standard, les coordonnées sont les corrélations.
# Créer un DataFrame des données transformées pour faciliter le calcul de corrélation
df_pca = pd.DataFrame(X_pca, columns=['CP1', 'CP2'])
df_scaled = pd.DataFrame(X_scaled, columns=feature_names)
# Concaténer pour calculer les corrélations
df_combined = pd.concat([df_scaled, df_pca], axis=1)
# Calculer les corrélations
correlations = df_combined.corr().loc[feature_names, ['CP1', 'CP2']]
print("Corrélations entre variables originales et Composantes Principales:")
print(correlations)
# 2. Créer un cercle de corrélation
plt.figure(figsize=(8, 8))
# Dessiner le cercle unitaire
circle = plt.Circle((0, 0), 1, color='gray', fill=False, linestyle='--')
plt.gca().add_patch(circle)
# Dessiner les vecteurs pour chaque variable
for i, feature in enumerate(feature_names):
# Les coordonnées du vecteur sont les corrélations de la variable avec CP1 et CP2
x = correlations.loc[feature, 'CP1']
y = correlations.loc[feature, 'CP2']
plt.arrow(0, 0, x, y, color='b', alpha=0.8, head_width=0.05, head_length=0.05)
plt.text(x * 1.05, y * 1.05, feature, color='r', ha='center', va='center')
plt.xlim(-1.1, 1.1)
plt.ylim(-1.1, 1.1)
plt.xlabel("Composante Principale 1")
plt.ylabel("Composante Principale 2")
plt.title("Cercle de Corrélation des Variables Originales et des CP")
plt.grid(True)
plt.axhline(0, color='gray', linewidth=0.5)
plt.axvline(0, color='gray', linewidth=0.5)
plt.show()
# 3. Interprétation du cercle de corrélation
# 4. Discussion sur l'utilisation des CP comme nouvelles caractéristiques
```
```
Corrélations entre variables originales et Composantes Principales:
CP1 CP2
sepal length (cm) 0.897401 -0.016335
sepal width (cm) -0.380766 0.920197
petal length (cm) 0.976378 -0.021028
petal width (cm) 0.960256 -0.063060
```
> [!example] Interprétation du cercle de corrélation
> Le cercle de corrélation nous permet de visualiser les relations entre les variables originales et les composantes principales, ainsi qu'entre les variables originales elles-mêmes.
>
> * **Longueur des vecteurs :** La longueur d'un vecteur indique la qualité de la représentation de la variable sur le plan factoriel (ici, le plan CP1-CP2). Plus un vecteur est proche du cercle unitaire (longueur proche de 1), mieux la variable est représentée par les deux premières composantes principales. Toutes les variables sont bien représentées, en particulier `petal length` et `petal width`.
> * **Angle des vecteurs :**
> * **Avec les axes :** L'angle entre un vecteur et un axe de composante principale indique la corrélation de la variable avec cette CP. Un angle faible (vecteur proche de l'axe) signifie une forte corrélation positive. Un angle proche de 180° signifie une forte corrélation négative. Un angle proche de 90° (vecteur perpendiculaire à l'axe) signifie une faible corrélation.
> * **Entre les vecteurs :** L'angle entre deux vecteurs de variables indique leur corrélation mutuelle. Un petit angle (vecteurs proches) suggère une forte corrélation positive. Un angle proche de 180° suggère une forte corrélation négative. Un angle proche de 90° suggère une faible corrélation.
>
> **Analyse spécifique :**
> * **Variables contribuant le plus à la CP1 :** `petal length (cm)` (0.976) et `petal width (cm)` (0.960) sont très fortement corrélées positivement avec la CP1. `sepal length (cm)` (0.897) est aussi fortement corrélée positivement. Ces trois variables vont dans le même sens sur la CP1. La CP1 semble donc représenter une dimension de "taille générale" des pétales et de la longueur du sépale.
> * **Variables contribuant le plus à la CP2 :** `sepal width (cm)` (0.920) est très fortement corrélée positivement avec la CP2. Les autres variables ont des corrélations faibles avec la CP2. La CP2 représente donc principalement la "largeur du sépale", orthogonalement à la première dimension.
> * **Variables fortement corrélées entre elles :** `petal length (cm)` et `petal width (cm)` sont très proches l'une de l'autre et de la CP1, indiquant une très forte corrélation positive entre elles. `sepal length (cm)` est aussi corrélée positivement avec ces deux variables. `sepal width (cm)` est relativement indépendante des autres variables dans le plan CP1-CP2 (son vecteur est presque perpendiculaire aux vecteurs des autres variables).
> [!tip] Utilisation des Composantes Principales comme nouvelles caractéristiques
> Les deux premières composantes principales (`CP1` et `CP2`) expliquent environ 95.8% de la variance totale du jeu de données Iris. Elles capturent donc la quasi-totalité de l'information utile des quatre variables originales.
>
> Pour un modèle de classification des espèces d'iris, on pourrait utiliser `CP1` et `CP2` comme de nouvelles caractéristiques (features) à la place des quatre originales.
> * **Avantages :**
> * **Réduction de la dimensionnalité :** Passer de 4 à 2 caractéristiques simplifie le modèle, réduit le temps d'entraînement et la complexité.
> * **Décorrélation :** Les CP sont non corrélées, ce qui peut améliorer les performances de certains algorithmes de Machine Learning qui supposent l'indépendance des features (par exemple, la régression logistique ou les SVM linéaires).
> * **Débruitage :** Les composantes de faible variance (ici CP3 et CP4) souvent associées au bruit sont écartées, ce qui peut améliorer la robustesse du modèle.
> * **Exemple d'application :** On pourrait entraîner un classifieur simple (comme un K-NN, une régression logistique ou un SVM) sur `X_pca` (les données transformées en 2 dimensions) pour prédire `y` (les espèces d'iris). La visualisation précédente a déjà montré que ces deux CP permettent une bonne séparation des classes, suggérant qu'elles seraient de très bonnes caractéristiques pour un tel modèle.