TP 5 : Graphiques pour la science des données : suite

Dans un TP précédent, on a vu comment réaliser des graphiques en utilisant les méthodes de la bibliothèque pandas.

Python

Comme toujours avec python, il existe plusieurs manières de réaliser à peu près la même chose. Lors de ce TP, nous avions utilisé les fonctions graphiques du paquetage pandas qui elles-mêmes utilisent celles du paquetage matplotlib. Ce dernier est un paquetage très utile et de grande qualité, très riche en possibilités dont seule une petite partie peut être utilisée via pandas.
On peut utiliser directement matplotlib ce qui donne beaucoup plus de libertés et qui est très pratique lorsque l'on veut réaliser une figure contenant plusieurs graphiques.
Ce court TP a pour objectif de vous présenter cette autre manière de faire.

R

En R, c'est beaucoup plus simple : on va utiliser les fonctions standards.
On pourrait utiliser un paquet dénommé ggplot2 mais avec les commandes standards, on fait déjà beaucoup de choses, en tout cas, tout ce qui est expliqué dans ce TP.
En R, il n'y a que les deux dernières parties (à partir de là) à faire.

Dans ce TP, on utilise le jeu de données olives. Je suppose que vous avez chargé ce jeu de données et que vous avez réalisé les opérations suivantes qui ont été effectuées dans le TP 1 pour les deux premiers points et le TP 3 pour le dernier point :

Graphiques avec matplotlib

Premiers pas

Pour réaliser un graphique, la méthode vue précédemment dans le TP graphiques consiste à faire une instruction telle que :

olives.plot.scatter (x = "linoleic", y = "palmitic", title = "Les olives dans le plan linoleic x palmitic", c = "region", cmap = "tab10")
plt. show()
.
qui donne :

L'autre approche consiste à faire comme suit :
fig, ax = plt.subplots ()
ax. scatter (x = "linoleic", y = "palmitic", c = "region", data = olives, cmap = "tab10")
ax. set_title ("Les olives dans le plan linoleic x palmitic")
ax. set_xlabel ("Acide linoleic")
ax. set_ylabel ("Acide palmitic")
fig. show()



Explication : on commence par créer deux objets à l'aide de la méthode fig, ax = plt.subplots (). Ensuite, on ajoute des éléments à la figure avec des instructions du genre ax. xxx (...) où xxx indique l'élément de la figure dont on veut spécifier la valeur. On termine par fig. show() pour afficher la figure. On peut aussi la mettre dans un fichier avec fig. savefig (nom-du-fichier).

Remarque : les points sont un peu grossiers dans cette figure. On peut spécifier leur taille avec le paramètre s :
fig, ax = plt.subplots ()
ax. scatter (x = "linoleic", y = "palmitic", c = "region", data = olives, cmap = "tab10", s = 3)
ax. set_title ("Les olives dans le plan linoleic x palmitic")
ax. set_xlabel ("Acide linoleic")
ax. set_ylabel ("Acide palmitic")
fig. show()


On peut ajouter une légende pour un code couleur indiquant la région de chaque olive :
fig, ax = plt.subplots ()
ax. scatter (x = "linoleic", y = "palmitic", c = "blue", data = olives [olives.region == 1], label = "Nord", s = 3)
ax. scatter (x = "linoleic", y = "palmitic", c = "red", data = olives [olives.region == 2], label = "Sardaigne", s = 3)
ax. scatter (x = "linoleic", y = "palmitic", c = "green", data = olives [olives.region == 3], label = "Sud", s = 3)
ax. set_title ("Les olives dans le plan linoleic x palmitic")
ax. set_xlabel ("Acide linoleic")
ax. set_ylabel ("Acide palmitique")
ax. legend ()
fig. show()


En plus d'une couleur, on peut encore indiquer la classe par la forme du point :
fig, ax = plt.subplots ()
ax. scatter (x = "linoleic", y = "palmitic", c = "blue", data = olives [olives.region == 1], label = "Nord", marker = "s", s = 16)
ax. scatter (x = "linoleic", y = "palmitic", c = "red", data = olives [olives.region == 2], label = "Sardaigne", marker = "X", s = 16)
ax. scatter (x = "linoleic", y = "palmitic", c = "green", data = olives [olives.region == 3], label = "Sud", marker = "<", s = 16)
ax. set_title ("Les olives dans le plan linoleic x palmitic")
ax. set_xlabel ("Acide linoleic")
ax. set_ylabel ("Acide palmitique")
ax. legend ()
fig. show()


Déformation des figures

Tout cela, c'est très bien, mais cela donne une image totalement déformée du nuage de points. Donc, toutes ces images sont à mettre à la poubelle !
J'explique pourquoi sur un exemple. Je dessine deux cercles avec le code :

Python
import numpy as np
import math
r1 = 3 # rayon du cercle 1
r2 = 5 # rayon du cercle 2
cx1 = 2 # abscisse du centre du cercle 1
cy1 = -1 # ordonnée du centre du cercle 1
cx2 = 0 # abscisse du centre du cercle 2
cy2 = 1.5 # ordonnée du centre du cercle 2
# calcul des points composant chaque cercle
n = 360
x1 = np.zeros ((n))
y1 = np.zeros ((n))
x2 = np.zeros ((n))
y2 = np.zeros ((n))
for i in range (n):
    x1 [i] = r1 * math.cos (2 * i * math.pi / n) + cx1
    y1 [i] = r1 * math.sin (2 * i * math.pi / n) + cy1
    x2 [i] = r2 * math.cos (2 * i * math.pi / n) + cx2
    y2 [i] = r2 * math.sin (2 * i * math.pi / n) + cy2
import matplotlib.pyplot as plt
fig, ax = plt.subplots ()
ax.scatter (x1, y1, c = "blue")
ax.scatter (x2, y2, c = "orange")
ax.set_title ("Deux cercles (!!!)")
fig.show ()
R
r1 <- 3 # rayon du cercle 1
r2 <- 5 # rayon du cercle 2
cx1 <- 2 # abscisse du centre du cercle 1
cy1 <- -1 # ordonnée du centre du cercle 1
cx2 <- 0 # abscisse du centre du cercle 2
cy2 <- 1.5 # ordonnée du centre du cercle 2
# calcul des points composant chaque cercle
n <- 360
x1 <- rep (n, 0)
y1 <- rep (n, 0)
x2 <- rep (n, 0)
y2 <- rep (n, 0)
for (i in 1:n) {
    x1 [i] <- r1 * cos (2 * i * pi / n) + cx1
    y1 [i] <- r1 * sin (2 * i * pi / n) + cy1
    x2 [i] <- r2 * cos (2 * i * pi / n) + cx2
    y2 [i] <- r2 * sin (2 * i * pi / n) + cy2
}
plot (x2, y2, col = "blue", main = "Mauvais",
      xlim = c (min (x1, x2), max (x1, x2)),
      ylim = c (min (y1, y2), max (y1, y2)))
points (x1, y1, col = "orange")

Je l'exécute, et j'obtiens :

Python
R

Que se passe-t-il ? Pourquoi ne voit-on pas deux cercles mais deux ellipses ? Réfléchissez-y avant de continuer. Indice : regardez bien les figures et notamment les axes x et y.




























Si vous avez regardé la figure, vous avez noté que les deux axes ont une amplitude d'environ 10 unités. Or, l'axe des abscisses est bien plus étendu que celui des ordonnées. Donc, la figure est tassée, elle est déformée : cette figure ne représente pas bien la répartition des points dans le plan, elle nous trompe, elle est donc mauvaise.
Et on obtient maintenant :

Python

Pour qu'elle soit correctement affichée, il faut simplement indiquer que l'on veut que les deux axes soient à la même échelle. Cela se fait avec l'instruction ax.set_aspect (1), à placer quelque part avant fig. show ().

R

Pour qu'elle soit correctement affichée, il faut simplement indiquer que l'on veut que les deux axes soient à la même échelle. Cela se fait avec le paramètre asp = 1 lors de la fonction plot ().

deux cercles.

Vous n'avez plus qu'à refaire toutes les figures précédentes pour qu'elles soient correctes. Vous allez obtenir :

à comparer aux figures précédentes. Les points sont beaucoup moins étalés le long de l'axe des abscisses.

Figure composée

On peut créer une figure composée de plusieurs sous-figures. Dans ce cas, la figure principale est une matrice de sous-figures. Par exemple, on peut représenter les valeurs de chacun des 8 acides dans une figure composée de 2 lignes de 4 figures comme suit :

Python
fig, ax = plt.subplots (2, 4)
ax [0, 0]. scatter (x = "palmitic", y = "numero", data = olives, c = "region", s = 3)
ax [0, 0]. set_title ("palmitic")
ax [0, 0]. set_aspect (1)
ax [0, 1]. scatter (x = "palmitoleic", y = "numero", data = olives, c = "region", s = 3)
ax [0, 1]. set_title ("palmitoleic")
ax [0, 1]. set_aspect (1)
ax [0, 2]. scatter (x = "stearic", y = "numero", data = olives, c = "region", s = 3)
ax [0, 2]. set_title ("stearic")
ax [0, 2]. set_aspect (1)
ax [0, 3]. scatter (x = "oleic", y = "numero", data = olives, c = "region", s = 3)
ax [0, 3]. set_title ("oleic")
ax [0, 3]. set_aspect (1)
ax [1, 0]. scatter (x = "linoleic", y = "numero", data = olives, c = "region", s = 3)
ax [1, 0]. set_title ("linoleic")
ax [1, 0]. set_aspect (1)
ax [1, 1]. scatter (x = "linolenic", y = "numero", data = olives, c = "region", s = 3)
ax [1, 1]. set_title ("linolenic")
ax [1, 1]. set_aspect (1)
ax [1, 2]. scatter (x = "arachidic", y = "numero", data = olives, c = "region", s = 3)
ax [1, 2]. set_title ("arachidic")
ax [1, 2]. set_aspect (1)
ax [1, 3]. scatter (x = "eicosenoic", y = "numero", data = olives, c = "region", s = 3)
ax [1, 3]. set_title ("eicosenoic")
ax [1, 3]. set_aspect (1)
fig. show ()



Bien sûr, on fera plutôt une boucle :

attributs = list (olives) [3:11]
fig, ax = plt.subplots (2, 4)
for ligne in range (2):
    for colonne in range (4):
        ax [ligne, colonne]. scatter (x = attributs [ligne * 4 + colonne], y = "numero", data = olives, c = "region", s = 3)
        ax [ligne, colonne]. set_title (attributs [ligne * 4 + colonne])
        ax [ligne, colonne]. set_aspect (1)

fig. show ()
R

Pour créer une figure composé de plusieurs sous-figures, on découpe la figure en une grille en indiquant le nombre de lignes et le nombre de colonnes est composée la figure.
Pour cela, on exécute une instruction qui a la forme suivante : op <- par(mfrow=c(nombre-de-lignes, nombre-de-colonnes)).
Ensuite, on exécute les fonctions graphiques classiques. Chacune placera son résultat dans un morceau de la fenêtre, en fonction du découpage que l'on a spécifié. Les sous-graphiques sont remplis dans l'ordre des commandes, du haut à gauche jusqu'en bas à droite, ligne par ligne.
La variable op contient l'ancienne valeur des paramètres graphiques modifiés par la fonction par (). Ainsi, une fois le graphique terminée, on peut restaurer leur encienne valeur. Aussi, une fois le graphique réalisé, on a coûtume de restaurer les paramètres graphiques en faisant par (op).
Cela donne :


Bien sûr, on fait une boucle pour afficher ces 8 graphiques dans la figure.

Pour aller plus loin

Les possibilités offertes par matplotlib sont immenses. On consultera la documentation :