Le vendredi 7 juin 2012, par Félix
Ce document fait un petit tour des capacités du Notebook IPython.
Vous pouvez exécuter chaque cellule en tapant Shift-Enter
(Maj-Entrée
), sauf bien sûr si vous êtes en train de visualiser une version statique de ce notebook via nbviewer.
Les commandes bash de bases sont directement appelables dans IPython :
pwd
u'/home/felix/github/presentation-ipython-mirabellug/notebooks'
ls *
classes_diametre_avec_eclaircies.csv demo_Mirabellug.ipynb evolution_diametre_sans_eclaircies.csv
classes_diametre_sans_eclaircies.csv evolution_diametre_avec_eclaircies.csv mirabellug.jpeg
Leur sortie peut être récupérée dans une liste Python en les faisant précéder d'un point d'exclamation :
files = !ls *
files
['classes_diametre_avec_eclaircies.csv', 'classes_diametre_sans_eclaircies.csv', 'demo_Mirabellug.ipynb', 'evolution_diametre_avec_eclaircies.csv', 'evolution_diametre_sans_eclaircies.csv', 'mirabellug.jpeg']
IPython gère l'autocomplétion (Tab
) et permet d'accéder interactivement à la documentation des méthodes, des classes et des modules. Par exemple, importons le module bisect :
import bisect
On peut obtenir sa documentation (docstring) avec un point d'exclamation :
bisect?
Et tout le code du module avec deux points d'exclamation ;
bisect??
Même chose avec une méthode. Dans ce cas, on peut aussi ouvrir une parenthèse et appuyer sur Tab
pour faire apparaitre une info-bulle :
bisect.bisect_right?
import math
def f(a, b):
return a*math.cos(2*math.pi/b)
f(1, 2)
-1.0
f(1, 0)
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-11-8d538c44df51> in <module>() ----> 1 f(1, 0) <ipython-input-9-ce2ff286f48b> in f(a, b) 1 import math 2 def f(a, b): ----> 3 return a*math.cos(2*math.pi/b) ZeroDivisionError: float division by zero
Les sorties d'un morceau d'un morceau de code s'affichent en temps réel (un peu hors-sujet dans cette partie, mais je ne savais pas où le caser) :
import time
for i in range(10):
print i,
time.sleep(0.5)
0 1 2 3 4 5 6 7 8 9
Le Notebook affiche des images, de l'HTML et même des vidéos.
from IPython.core.display import Image
Image(filename='mirabellug.jpeg') # fichier local
Image('http://ipython.org/_static/IPy_header.png')
from IPython.display import HTML
s = """<table>
<tr>
<th>Header 1</th>
<th>Header 2</th>
</tr>
<tr>
<td>row 1, cell 1</td>
<td>row 1, cell 2</td>
</tr>
<tr>
<td>row 2, cell 1</td>
<td>row 2, cell 2</td>
</tr>
</table>"""
h = HTML(s)
h
Header 1 | Header 2 |
---|---|
row 1, cell 1 | row 1, cell 2 |
row 2, cell 1 | row 2, cell 2 |
HTML("<iframe src=http://www.mirabellug.org width=1000 height=500>")
from IPython.lib.display import YouTubeVideo
YouTubeVideo("t4gjl-uwUHc")
On peut insérer du texte, avec des titres de différents niveaux, des parties en gras, en italique, ou à chasse fixe
.
Mais également des listes :
Des énumérations :
ipython notebook
Et bien sûr, des équations (avec la syntaxe LaTeX, grâce à MathJax). Exemple avec cette formule pour calculer $\pi$ :
Ou la définition de la fonction exponentielle :
Même du code, en indentant le texte:
def rend_pair(x):
"""Ajoute 1 à x si x est impair."""
return x + x % 2
Numpy est une bibliothèque pour le calcul numérique.
Matplotlib est une bibliothèque pour le tracé de graphiques.
Dans IPython, la commande %pylab
importe dans l'espace de noms courant un grand nombre de fonctions de ces deux bibliothèques. L'option inline
permet d'afficher les graphes directement dans le navigateur.
Pour en savoir plus sur les imports réalisés par %pylab
, lisez le script '/usr/lib/pymodules/python2.7/matplotlib/pylab.py'.
%pylab inline
Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline]. For more information, type 'help(pylab)'.
Les deux objets fondamentaux de Matplotlib sont les figures et les axes.
Comme premier exemple, on va créer une figure contenant deux axes. On va tracer dans le premier des fonctions trigonométriques, et dans l'autre des polynômes.
f, (ax1, ax2) = subplots(ncols=2) # renvoie une figure et deux axes associés sur deux colonnes
x = linspace(0, 2*pi, 50) # tableau de 50 points uniformément répartis entre 0 et 2pi
line1, = ax1.plot(x, sin(x), 'b-') # courbe de la fonction sinus, en trait continu bleu
line2, = ax1.plot(x, cos(x), '-', color='#333333') # fonction cosinus, en trait continu et couleur définie par un code rgb
line3, = ax2.plot(x, x**2, 'g-') # parabole, en vert
line4, = ax2.plot(x, 9*x - 4*x**2 + 0.5*x**3, 'k-') # polynôme plus complexe, en noir
On peut modifier a posteriori les propriétés des axes…
ax1.set_title("Sin et cos", fontsize=20)
ax2.set_title(u"Polynômes", fontsize=20)
ax1.set_xlabel("x", fontsize=16)
ax2.set_xlabel("x", fontsize=16)
ax1.set_ylabel("y", fontsize=16)
ax1.grid(True)
ax2.grid(True)
line2.set_color('#00adef')
line3.set_marker('*')
...et réafficher.
f
Pour illustrer la puissance du Matplotlib et d'IPython dans la représentation de données, on va utiliser les résultats du simulateur Fagacée.
Fagacée simule la croissance d'un peuplement forestier en fonction du scénario sylvicole suivi (c'est-à-dire la façon de gérer le peuplement). Dans notre exemple, on a simulé un siècle de croissance suivant deux scénarios différents. Dans le premier, on laisse le peuplement croitre librement ; dans le deuxième on pratique plusieurs éclaircies (éclaircie = abattage de certains arbres pour favoriser la croissance de plus prometteurs).
Les quatres fichiers de données sont :
evolution_diametre_avec_eclaircies.csv
: évolution temporelle des diamètres moyen et dominant (= arbre le plus haut) sans éclairciesevolution_diametre_sans_eclaircies.csv
: idem avec éclairciesclasses_diametre_sans_eclaircies.csv
: répartition des arbres par classes de diamètre au bout d'un siècle (sans éclaircies)classes_diametre_avec_eclaircies.csv
: idem avec éclairciesLa fonction genfromtxt
lit un fichier texte et stocke les données dans un tableau (array) Numpy :
Devol_sans = genfromtxt("evolution_diametre_sans_eclaircies.csv", skiprows=1, names=True, delimiter=";")
Devol_sans['Dmoy']
array([ 1.21562409, 1.48095841, 2.08587895, 2.79736352, 3.66537027, 4.73272782, 6.00902998, 7.42966566, 8.95798988, 10.54303963, 12.14122033, 13.73226845, 15.29127951, 16.80379569, 18.26107646, 19.66222776, 20.99505627, 22.26107041, 23.46855298, 24.59926314, 25.6683903 , 26.68296476, 27.63362762, 28.54106382, 29.4079282 , 30.24796836, 31.04808704, 31.8308487 , 32.58756813])
plot(Devol_sans['temps'], Devol_sans['Ddom'], 'k--')
[<matplotlib.lines.Line2D at 0xb45e1ac>]
plot(Devol_sans['temps'], Devol_sans['Ddom'], 'k--', label=u'Diamètre dominant (cm)')
plot(Devol_sans['temps'], Devol_sans['Dmoy'], 'b--', label=u'Diamètre moyen (cm)')
legend(loc='upper left')
grid(True)
title(u"Évolution des diamètres dominant et moyen\nen l'absence d'éclaircie", fontsize=14)
xlabel(u"Âge du peuplement (années)", fontsize=12)
<matplotlib.text.Text at 0xb45e7ac>
Devol_avec = genfromtxt("evolution_diametre_avec_eclaircies.csv", skiprows=1, names=True, delimiter=";")
plot(Devol_avec['temps'], Devol_avec['Ddom'], 'k--', label=u'Diamètre dominant (cm)')
plot(Devol_avec['temps'], Devol_avec['Dmoy'], 'b--', label=u'Diamètre moyen (cm)')
grid(True)
legend(loc='upper left')
title(u"Évolution des diamètres dominant et moyen\navec des éclaircies", fontsize=14)
xlabel(u"Âge du peuplement (années)", fontsize=12)
<matplotlib.text.Text at 0xb617b8c>
f, (ax1, ax2) = subplots(ncols=2, figsize=(10,6))
ax1.plot(Devol_sans['temps'], Devol_sans['Ddom'], 'k--', label=u'Diamètre dominant (cm)')
ax1.plot(Devol_sans['temps'], Devol_sans['Dmoy'], 'b--', label=u'Diamètre moyen (cm)')
ax1.grid(True)
ax1.legend(loc='upper left') # légende en haut à gauche de l'axe
ax1.set_title(u"Sans éclaircies")
ax2.plot(Devol_avec['temps'], Devol_avec['Ddom'], 'k--', label=u'Diamètre dominant (cm)')
ax2.plot(Devol_avec['temps'], Devol_avec['Dmoy'], 'b--', label=u'Diamètre moyen (cm)')
ax2.grid(True)
ax2.legend(loc='upper left')
ax2.set_title(u"Avec éclaircies")
<matplotlib.text.Text at 0xb8c440c>
On constate qu'avec des éclaircies, les diamètres finals sont plus gros et tous les arbres atteignent à peu près le même diamètre.
Cette fois, on va tracer des histogrammes.
Ddistrib_sans = genfromtxt("classes_diametre_sans_eclaircies.csv", skiprows=1, names=True, dtype=['S5', 'i'], delimiter=";")
nb_classes = Ddistrib_sans.size
fig, ax = subplots(figsize=(7,4))
ax.set_xlabel(u"Diamètre (cm)", fontsize=14)
ax.set_ylabel("Nombre d'arbres", fontsize=14)
ax.set_title(u"Répartition par classes de diamètres\nen l'absence d'éclaircie", fontsize=16)
width=0.5
ax.set_xticks(arange(nb_classes) + width/2)
ax.set_xticklabels(Ddistrib_sans['diametre'], rotation=70, fontsize=11)
barres = ax.bar(arange(nb_classes), Ddistrib_sans['N'], width=width)
Ddistrib_avec = genfromtxt("classes_diametre_avec_eclaircies.csv", skiprows=1, names=True, dtype=['S5', 'i'], delimiter=";")
nb_classes = Ddistrib_avec.size
fig, ax = subplots()
ax.set_xlabel(u"Diamètre (cm)", fontsize=14)
ax.set_ylabel("Nombre d'arbres", fontsize=14)
ax.set_title(u"Répartition par classes de diamètres\navec des éclaircies", fontsize=16)
width=0.5
ax.set_xticks(arange(nb_classes) + width/2)
ax.set_xticklabels(Ddistrib_avec['diametre'], rotation=70, fontsize=11)
barres = ax.bar(arange(nb_classes), Ddistrib_avec['N'], width=width)
La commande magique %loadpy
charge un script depuis le Web :
%loadpy http://matplotlib.org/mpl_examples/pylab_examples/animation_demo.py
Grâce à la commande magique %%script
, on peut exécuter dans IPython du code écrit dans d'autres langages. Pour l'instant, seuls bash, perl, ruby et R sont implémentés.
%%script perl
$_="krJhruaesrltre c a cnp,ohet";$_.=$1,print$2while s/(..)(.)//;
Just another perl hacker,
%%script ruby
puts "Hello from Ruby #{RUBY_VERSION}"
Hello from Ruby 1.9.3
Mais cela ne doit pas nous faire oublier que Python est la seule et unique Voie menant à la Vérité. Nous nous séparerons donc sur les dix-neuf mantras de l'enseignement zen pythonicien :
import this
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!