Ce TD propose une découverte de la bibliothèque Python de dessin matplotlib
. Avant de commencer, vous allez créer un compte sur le site http://cloud.sagemath.com, qui offre gratuitement, entre autres, la possiblité de créer des notebooks IPython (Python 2).
Une fois connectés, créez un nouveau projet (bouton "create new project"), ouvrez-le, cliquez sur "Create new files in home directory of project", et créez un nouveau notebook en cliquant sur le bouton "IPython Notebook".
Vous pouvez créer autant de notebooks que vous le souaitez dans un projet, ainsi que d'autres outils (feuilles de calcul Sage, terminaux Unix, fichiers LaTeX, ...)
matplotlib
¶Le point de départ pour l'apprentissage de matplotlib
est le tutoriel http://matplotlib.org/users/pyplot_tutorial.html.
Une introduction utile est le tutoriel http://github.com/jrjohansson/scientific-python-lectures.
de J.R. Johansson (robert@riken.jp) http://dml.riken.jp/~rob/
matplotlib
est une bibliothèque Python de dessin très populaire dans le monde scientifique. Parmi ses caractéristiques, il y a :
Elle s'intègre parfaitement avec le notebook IPython. Le notebook typique commence par une ligne de configuration :
# Cette commande active l'affichage des graphiques matplotlib
# dans le notebook. Elle doit apparaître une fois, au début du
# notebook
%matplotlib inline
Ensuite on importe les bibliothèques matplotlib.pyplot
et numpy
. Par commodité et convention, on donne les alias plt
et np
à ces bibliothèques :
import matplotlib.pyplot as plt
import numpy as np
On commence immédiatement avec un exemple: on approxime une parabole par morceaux de droites
plt.plot([0, 1, 4, 9, 16, 25, 36, 49])
[<matplotlib.lines.Line2D at 0x7f0b275ab510>]
Expliquons ce qui se passe ici :
plt.plot
prend en paramètre une liste de points, ce sont les ordonnées,plot.plot
renovie un objet de type lines.Line2D
. IPython sait comment afficher cet objet, grâce à la commande magique %matplotlib
que nous avons saisie touten haut.Voyons maintenant comment donner des abscisses autres que $0, 1, 2, ...$
plt.plot([1, 3, 5, 7], [10, 2, 8, 5], 'r--')
[<matplotlib.lines.Line2D at 0x7f0b27505a50>]
Ceci mérite quelques explications :
r
pour "red", --
pour les tirets. La liste des couleurs et styles disponibles est ici : http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plotMais ce dessin est déformé ! Rémédions !
plt.plot([1, 3, 5, 7], [10, 2, 8, 5], 'go-', linewidth=2)
plt.axis([0,11,0,11])
plt.xlabel('abscisses')
plt.ylabel('ordonnees')
plt.title('des donnees')
<matplotlib.text.Text at 0x7f0b2748d510>
On voit dans cet exemple que les fonctions de plt
s'appliquent de façon incrémentale au dessin.
Voici un dernier exemple avec plusieurs courbes.
plt.plot([0, 1, 4, 9, 16, 25], 'bo', label="parabole")
plt.plot([0, 1, 27, 64, 125], 'rx-.', label="cubique")
plt.legend()
<matplotlib.legend.Legend at 0x7f0b274c37d0>
Dessinnez le graphe de votre nombre d'heures de cours par jour de la semaine (numéroter les jours de 1 à 7).
Dessinner un pentagone (pas nécessairement réguilier).
Dessinner la courbe d'équation $y = x^3$ entre $-10$ et $10$ en utilisant $200$ échantillons (c'est à dire $200$ valeurs pour $x$). Aidez-vous avec une boucle for
.
Les boucles for
ne sont pas la seule façon de construire des gros ensembles de données. Python offre une syntaxe très pratique, dite des compréhensions de listes. En voici un exemple
a = [x**2 for x in range(20) if x % 2 == 0]
a
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324]
numpy
et matplotlib
offrent une syntaxe encore plus simple pour représenter des fonctions mathématiques. Observez le code ci-dessous.
a = np.arange(10)
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
b = range(10)
b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
type(a), type(b)
(numpy.ndarray, list)
np.arange(0, 10, 2)
array([0, 2, 4, 6, 8])
La fonction np.arange(a,b,c)
, comme la fonction range
, renvoie une liste comprise entre a
et b
avec des incréments de c
. Mais l'objet renvoyé n'est pas une liste normale Python : il s'agit d'un array numpy.
Les arrays numpy se comportent différemment des listes Python. Lisez et comprenez ces exemples :
2 * a
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
2 * b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a * a
array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
b * b
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-15-aa818ac5610b> in <module>() ----> 1 b * b TypeError: can't multiply sequence by non-int of type 'list'
Les opérations arithmétiques sont exécutées composante par composante sur les arrays numpy, ceci nous permet de dessinner des fonctions mathématiques avec une syntaxe très intuitive.
Voici la fonction $\sin x$ entre $0$ et $6\pi$.
x = 2 * np.pi * np.arange(0, 3, 0.01) # np.pi est la constante π
y = np.sin(x)
plt.plot(x, y)
[<matplotlib.lines.Line2D at 0x7f0b2727d090>]
Dessinner la fonction $\cos$ entre $-\pi$ et $\pi$.
Dessinner la fonction $y = x^3$ entre $-10$ et $10$ en utilisant un array numpy.
On rappelle que le cercle de rayon $1$ a pour équation $x^2 + y^2 = 1$. La fonction racine carrée s'appelle np.sqrt
. Dessinner un cercle.
On rappelle que le cercle unité est paramétrisé par les fonctions $(\cos(x), \sin(x))$, avec $-\pi\le x\le\pi$. Dessinner un cercle en utilisant les fonctions np.sin
et np.cos
.
On peut afficher plusieurs graphes côte à côte avec la fonction plt.subplots
. Celle ci prend le nombre de lignes, le nombre de colonnes, et le graphe sur lequel travailler. Voici un exemple.
x = 2 * np.pi * np.arange(0, 3, 0.01) # np.pi est la constante π
y = np.sin(x)
plt.subplot(1,2,1)
plt.plot(x, y, 'r--')
plt.subplot(1,2,2)
plt.plot(y, x, 'g*-');
Reproduire le dessin ci-dessous (suggestion la première ressemble méchamment à $e^{-x} \cos(cx)$... à vous de trouver la constante $c$):
Voici quelques autres types de graphe offerts par matplotlib
:
n = np.array([0,1,2,3,4,5])
plt.subplot(2, 2, 1)
plt.scatter(n, -n, s=10*n+1)
plt.title("diffusion")
plt.subplot(2, 2, 2)
plt.step(n, n**2)
plt.title("escalier")
plt.subplot(2, 2, 3)
plt.bar(n, n**2)
plt.title("barres")
plt.subplot(2, 2, 4)
plt.fill_between(x, x**2, x**3, color="green");
plt.title("remplissage");
On peut aussi faire des gâteaux
plt.pie([5, 10, 5, 80], labels=['a', 'b', 'c', 'd'], colors=['r', 'w', 'b', 'y'])
([<matplotlib.patches.Wedge at 0x7f0b26db8050>, <matplotlib.patches.Wedge at 0x7f0b26db8a10>, <matplotlib.patches.Wedge at 0x7f0b26dc23d0>, <matplotlib.patches.Wedge at 0x7f0b26dc2d50>], [<matplotlib.text.Text at 0x7f0b26db85d0>, <matplotlib.text.Text at 0x7f0b26db8fd0>, <matplotlib.text.Text at 0x7f0b26dc2990>, <matplotlib.text.Text at 0x7f0b26dce350>])
Un histogramme est une représentation de la fréquence d'occcurence d'une valeur dans un ensemble.
n = [1, 2, 2, 3, 4, 10, 2, 1]
plt.hist(n)
(array([ 2., 3., 1., 1., 0., 0., 0., 0., 0., 1.]), array([ 1. , 1.9, 2.8, 3.7, 4.6, 5.5, 6.4, 7.3, 8.2, 9.1, 10. ]), <a list of 10 Patch objects>)
Par défaut un histogramme est séparé en au plus 10 plages de valeurs. Cela peut être contrôlé par le paramètre bins
.
n = [x % 30 for x in range(10000) if np.sqrt(x).is_integer()]
print n
plt.subplot(2,1,1)
plt.hist(n)
plt.subplot(2,1,2)
plt.hist(n, bins=30)
plt.show()
[0, 1, 4, 9, 16, 25, 6, 19, 4, 21, 10, 1, 24, 19, 16, 15, 16, 19, 24, 1, 10, 21, 4, 19, 6, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 6, 19, 4, 21, 10, 1, 24, 19, 16, 15, 16, 19, 24, 1, 10, 21, 4, 19, 6, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 6, 19, 4, 21, 10, 1, 24, 19, 16, 15, 16, 19, 24, 1, 10, 21, 4, 19, 6, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 6, 19, 4, 21]
import urllib2
import json
prenoms = urllib2.urlopen('http://opendata.paris.fr/api/records/1.0/download?dataset=liste_des_prenoms_2004_a_2012&format=json')
donnees_brutes = json.load(prenoms)
La variable donnees_brutes
contient la liste des tous les noms enregistrés par la mairie de Paris entre 2004 et 2014 (extrêmes inclus), avec le nombre d'occurrences par année et par prénom.
Il s'agit d'un grosse liste !
len(donnees_brutes)
13546
Chaque élément de la liste est un dictionnaire Python.
donnees_brutes[0]
{u'datasetid': u'liste_des_prenoms_2004_a_2012', u'fields': {u'annee': 2011, u'nombre': 12, u'prenoms': u'Zachary', u'sexe': u'M'}, u'record_timestamp': u'2015-03-16T17:33:33.316694', u'recordid': u'aa8805d011cdcc1b9cf1abcd8febd8946028050b'}
Le seul champs intéressant est fields
, qui contient les champs suivants :
prenoms
: préonom enregistré,annee
: année de l'enregistrement,nombre
: nombre de fois que le nom a été donné,sexe
: genre du prénom : M
, F
et X
(après 2010 X
n'est plus utilisé, et les prénoms unisex sont séparés en enregistrements masculins et enregistrements féminins)donnees_brutes[0]['fields']
{u'annee': 2011, u'nombre': 12, u'prenoms': u'Zachary', u'sexe': u'M'}
Par conséquent, on ne va garder que ce contenu, dans un liste nommée donnees
.
donnees = [d['fields'] for d in donnees_brutes]
donnees[50:60]
[{u'annee': 2012, u'nombre': 5, u'prenoms': u'Annabelle', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 46, u'prenoms': u'Anouk', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 8, u'prenoms': u'Anya', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 10, u'prenoms': u'Ariane', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 10, u'prenoms': u'Aristide', u'sexe': u'M'}, {u'annee': 2012, u'nombre': 7, u'prenoms': u'Arthus', u'sexe': u'M'}, {u'annee': 2012, u'nombre': 7, u'prenoms': u'Assetou', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 6, u'prenoms': u'Assil', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 5, u'prenoms': u'Astou', u'sexe': u'F'}, {u'annee': 2012, u'nombre': 17, u'prenoms': u'Augustine', u'sexe': u'F'}]
Dessinner l'histogramme du nombre d'enregistrements par année.
Dessinner, sur un même graphe, le nombre d'enregistrements par année de ces noms :
Trouver le nom le plus populaire pour chaque année.
Dessiner le nombre de naissances de garçons et de filles en fonction de l'année.