• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 12/11/2019

TP : Réalisez une ACP

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

C'est le moment de plonger dans le code !

Nous allons ici analyser les jeux de données que nous avons détaillés dans un précédent chapitre.

Les cours OpenClassrooms que vous avez suivis

Dans les précédents chapitres, nous avons illustré le cercle des corrélations et la projection des individus à l'aide du jeu de données des cours OpenClassrooms que j'ai suivis (que vous pouvez aussi télécharger au chapitre dédié).

Voici donc le code Python qui vous permettra de faire de même avec votre propre échantillon. Ce code est également accessible dans le fichier pca_my_courses.py sur le git :

import pandas as pd
import numpy as np
from sklearn import decomposition
from sklearn import preprocessing
from functions import *

# choix du nombre de composantes à calculer
n_comp = 6

# import de l'échantillon
data = pd.read_csv("my_courses.csv",decimal=".",index_col=0)

# selection des colonnes à prendre en compte dans l'ACP
data_pca = data[["inscription","progression","moyenneDeClasse","duree","difficulte","nbChapitres","ratioQuizEvaluation","nbEvaluations"]]

# préparation des données pour l'ACP
data_pca = data_pca.fillna(data_pca.mean()) # Il est fréquent de remplacer les valeurs inconnues par la moyenne de la variable
X = data_pca.values
names = data["idCours"] # ou data.index pour avoir les intitulés
features = data.columns

# Centrage et Réduction
std_scale = preprocessing.StandardScaler().fit(X)
X_scaled = std_scale.transform(X)

# Calcul des composantes principales
pca = decomposition.PCA(n_components=n_comp)
pca.fit(X_scaled)

# Eboulis des valeurs propres
display_scree_plot(pca)

# Cercle des corrélations
pcs = pca.components_
display_circles(pcs, n_comp, pca, [(0,1),(2,3),(4,5)], labels = np.array(features))

# Projection des individus
X_projected = pca.transform(X_scaled)
display_factorial_planes(X_projected, n_comp, pca, [(0,1),(2,3),(4,5)], labels = np.array(names))

plt.show()

Le jeu de données mystère

C'est maintenant le moment de découvrir ce que représente l'échantillon mystère. Et pour cela, nous allons projeter les individus sur le premier plan factoriel, comme d'habitude !

import pandas as pd
from sklearn import decomposition, preprocessing
from functions import *


# choix du nombre de composantes à calculer
n_comp = 3

# import de l'échantillon
data = pd.read_csv("mystery.csv")
X = data.values

# Réduire n'est ici pas nécessaire car les variables sont exprimées dans la même unité. 
# On se contente juste de centrer les données, ce qui est obligatoire pour une ACP.
X = preprocessing.StandardScaler(with_std=False).fit_transform(X)

# Calcul des composantes principales
pca = decomposition.PCA(n_components= n_comp)
pca.fit(X)

# Eboulis des valeurs propres
display_scree_plot(pca)

# projection des individus
X_projected = pca.transform(X)
display_factorial_planes(X_projected, n_comp, pca, [(0,1),(1,2),(0,2)], alpha = 0.2)

plt.show()

Et voici la projection des individus sur le premier plan factoriel :

 

Dans cet échantillon, chaque individu est un point du chat. Bien entendu, vous aurez rarement un échantillon statistique où les points seront disposés selon la forme d'un animal ou d'un objet. En effet, cet échantillon se rapproche plutôt de ce qu'utilisent les personnes qui pratiquent la modélisation 3D (architectes, vidéastes, ingénieurs en mécanique, physiciens, etc.). Mais, depuis le début, nous avons souvent fait le parallèle entre nos nuages de points de statisticiens et l'inertie des objets réels qu'étudient les physiciens. Alors, continuons ici cette analogie.

Ce qui est affiché ici, c'est une projection orthogonale en 2D d'un objet en 3D.

Les 2 graphiques de dispersion que je vous avais donnés dans ce chapitre étaient aussi des projections 2D de points en 3D. Cependant, sur ceux-ci, nous n'arrivions pas à distinguer la forme du chat. Pourquoi ?

Parce que l'ACP a trouvé la meilleure projection : celle qui montre la plus grande inertie. Autrement dit, si vous voulez prendre une photo de votre chat, le meilleur angle pour avoir le plus de détails possible sera donc de profil, comme vous le voyez ci-dessus.

Sur le premier plan factoriel, on voit que l'axe d'inertie principal d'un chat part du bout de sa queue pour aller jusqu'à sa tête : c'est l'axe des abscisses du graphique.

L'échantillon en bag of words

Pour réaliser l'ACP de l'échantillon en bag of words, c'est exactement le même principe. Vous retrouverez ce code dans le fichier pca_bag_of_words.py :

import pandas as pd
import numpy as np
from sklearn import decomposition
from sklearn import preprocessing 
from functions import *   

# choix du nombre de composantes à calculer
n_comp = 50

# import de l'échantillon et des informations relatives aux cours
data = pd.read_csv('bag_of_words.csv', index_col = 0)
courses_info = pd.read_csv('courses_info.csv',index_col = 0)

# Theme du ou des parcours auxquels appartient le cours (data, developpement, marketing, etc.)
theme = [courses_info.loc[course_id, "theme"] for course_id in data.index]

# préparation des données pour l'ACP
X = data.values
features = data.columns

# Centrage et Réduction
std_scale = preprocessing.StandardScaler().fit(X)
X_scaled = std_scale.transform(X)

# Calcul des composantes principales
pca = decomposition.PCA(n_components=n_comp)
pca.fit(X_scaled)

# Eboulis des valeurs propres
display_scree_plot(pca)

# Cercle des corrélations
pcs = pca.components_
display_circles(pcs, n_comp, pca, [(0,1),(2,3),(4,5)])
display_circles(pcs, n_comp, pca, [(0,1)], lims=[.0155, .019, 0.053, .057], labels = np.array(features))
display_circles(pcs, n_comp, pca, [(2,3)], lims=[-.035, -.026, -.03,-.016], labels = np.array(features))

# Projection des individus
X_projected = pca.transform(X_scaled)
display_factorial_planes(X_projected, n_comp, pca, [(0,1),(2,3),(4,5)], illustrative_var = theme, alpha = 0.5)

plt.show()

Bon... sauf que là, il y a plus de 9 000 variables. Si l'on affiche le cercle des corrélations avec 9 000 flèches ainsi que leur nom, cela va être le bazar. N'affichons donc pas leur nom ni les extrémités des flèches :

Les flèches sont toutes petites ! Zoomons sur la petite "tache noire" au centre du cercle :

Affichons également la projection des individus, en les colorant selon le thème du cours :

Tout d'abord, on voit que les cours en rouge (cours de développement informatique) ont tendance à se regrouper vers les abscisses importantes, alors que les cours en bleu (cours de ressources humaines) ont tendance à se regrouper vers les abscisses faibles.

De plus, il y a clairement un individu qui se distingue des autres : en haut à droite (en rouge). Pour trouver ce qui le différencie des autres cours, zoomons sur le cercle des corrélations vers les variables qui ont une abscisse et une ordonnée élevées. Pour rappel, chaque variable de cet échantillon correspond à un mot :

On voit ici que le cours auquel nous nous intéressons a probablement de grandes valeurs pour des variables telles :"trigger", "rollback", "myisam", "smallint", "references". Ceux qui ont déjà utilisé les bases de données auront peut-être reconnu quelques mots-clés propres aux bases de données MySQL ! En vérifiant, on s'aperçoit que l'individu rouge en haut à droite est effectivement le cours Administrez vos bases de données avec MySql .

Analysons maintenant le second plan factoriel :

Intéressons-nous ici aux individus verts, qui ont des abscisses et des ordonnées faibles. En zoomant dans le cercle des corrélations vers les flèches pointant vers des abscisses et des ordonnées faibles, on obtient ceci :

Les termes "observations", "aléatoire", "corrélation", "variance", etc., devraient vous évoquer le domaine des statistiques, n'est-ce pas ? On trouve également des mots-clés propres à la librairie scikit-learn (dédiée au machine learning, mais que nous utilisons dans ce cours également) tels "scikit", "fit", "predict". Vous aurez donc deviné qu'il s'agit des cours de data !

Annexe : les fonctions outils

Voici la définition des fonctions utilisées dans ce chapitre, disponible dans le fichier functions.py :

import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
import pandas as pd

def display_circles(pcs, n_comp, pca, axis_ranks, labels=None, label_rotation=0, lims=None):
    for d1, d2 in axis_ranks: # On affiche les 3 premiers plans factoriels, donc les 6 premières composantes
        if d2 < n_comp:

            # initialisation de la figure
            fig, ax = plt.subplots(figsize=(7,6))

            # détermination des limites du graphique
            if lims is not None :
                xmin, xmax, ymin, ymax = lims
            elif pcs.shape[1] < 30 :
                xmin, xmax, ymin, ymax = -1, 1, -1, 1
            else :
                xmin, xmax, ymin, ymax = min(pcs[d1,:]), max(pcs[d1,:]), min(pcs[d2,:]), max(pcs[d2,:])

            # affichage des flèches
            # s'il y a plus de 30 flèches, on n'affiche pas le triangle à leur extrémité
            if pcs.shape[1] < 30 :
                plt.quiver(np.zeros(pcs.shape[1]), np.zeros(pcs.shape[1]),
                   pcs[d1,:], pcs[d2,:], 
                   angles='xy', scale_units='xy', scale=1, color="grey")
                # (voir la doc : https://matplotlib.org/api/_as_gen/matplotlib.pyplot.quiver.html)
            else:
                lines = [[[0,0],[x,y]] for x,y in pcs[[d1,d2]].T]
                ax.add_collection(LineCollection(lines, axes=ax, alpha=.1, color='black'))
            
            # affichage des noms des variables  
            if labels is not None:  
                for i,(x, y) in enumerate(pcs[[d1,d2]].T):
                    if x >= xmin and x <= xmax and y >= ymin and y <= ymax :
                        plt.text(x, y, labels[i], fontsize='14', ha='center', va='center', rotation=label_rotation, color="blue", alpha=0.5)
            
            # affichage du cercle
            circle = plt.Circle((0,0), 1, facecolor='none', edgecolor='b')
            plt.gca().add_artist(circle)

            # définition des limites du graphique
            plt.xlim(xmin, xmax)
            plt.ylim(ymin, ymax)
        
            # affichage des lignes horizontales et verticales
            plt.plot([-1, 1], [0, 0], color='grey', ls='--')
            plt.plot([0, 0], [-1, 1], color='grey', ls='--')

            # nom des axes, avec le pourcentage d'inertie expliqué
            plt.xlabel('F{} ({}%)'.format(d1+1, round(100*pca.explained_variance_ratio_[d1],1)))
            plt.ylabel('F{} ({}%)'.format(d2+1, round(100*pca.explained_variance_ratio_[d2],1)))

            plt.title("Cercle des corrélations (F{} et F{})".format(d1+1, d2+1))
            plt.show(block=False)
        
def display_factorial_planes(X_projected, n_comp, pca, axis_ranks, labels=None, alpha=1, illustrative_var=None):
    for d1,d2 in axis_ranks:
        if d2 < n_comp:
 
            # initialisation de la figure       
            fig = plt.figure(figsize=(7,6))
        
            # affichage des points
            if illustrative_var is None:
                plt.scatter(X_projected[:, d1], X_projected[:, d2], alpha=alpha)
            else:
                illustrative_var = np.array(illustrative_var)
                for value in np.unique(illustrative_var):
                    selected = np.where(illustrative_var == value)
                    plt.scatter(X_projected[selected, d1], X_projected[selected, d2], alpha=alpha, label=value)
                plt.legend()

            # affichage des labels des points
            if labels is not None:
                for i,(x,y) in enumerate(X_projected[:,[d1,d2]]):
                    plt.text(x, y, labels[i],
                              fontsize='14', ha='center',va='center') 
                
            # détermination des limites du graphique
            boundary = np.max(np.abs(X_projected[:, [d1,d2]])) * 1.1
            plt.xlim([-boundary,boundary])
            plt.ylim([-boundary,boundary])
        
            # affichage des lignes horizontales et verticales
            plt.plot([-100, 100], [0, 0], color='grey', ls='--')
            plt.plot([0, 0], [-100, 100], color='grey', ls='--')

            # nom des axes, avec le pourcentage d'inertie expliqué
            plt.xlabel('F{} ({}%)'.format(d1+1, round(100*pca.explained_variance_ratio_[d1],1)))
            plt.ylabel('F{} ({}%)'.format(d2+1, round(100*pca.explained_variance_ratio_[d2],1)))

            plt.title("Projection des individus (sur F{} et F{})".format(d1+1, d2+1))
            plt.show(block=False)

def display_scree_plot(pca):
    scree = pca.explained_variance_ratio_*100
    plt.bar(np.arange(len(scree))+1, scree)
    plt.plot(np.arange(len(scree))+1, scree.cumsum(),c="red",marker='o')
    plt.xlabel("rang de l'axe d'inertie")
    plt.ylabel("pourcentage d'inertie")
    plt.title("Eboulis des valeurs propres")
    plt.show(block=False)
Exemple de certificat de réussite
Exemple de certificat de réussite