Débuter avec les notebooks

Introduction

Jupyter est une application Web qui regroupe intimement deux fonctionnalités très différentes :

  • Un outil qui permet de créer des documents multimédia intégrant du texte, des formules mathématiques, des graphiques, des images, voire des animations et des vidéos.
  • Une interface qui permet d'exécuter du code informatique. Pour cela Jupyter s'appuie sur des programmes indépendants capablent d'interpréter le langage dans lequel est écrit ce code. Dans la terminologie de Jupyter ces interpréteurs sont appelés des noyaux (kernel en anglais). La machine jhub.lycee-chateaubriand.fr dispose des noyaux pour les langages Python 3 et OCaml.

Les documents Jupyter sont appelés des notebooks. Un fichier notebook est reconnaissable par son extension .ipynb.

Le document que vous lisez actuellement est un notebook.

Manipulation du notebook

À l'usage, il se révèle plus efficace d'utiliser les raccourcis clavier que de naviguer dans les menus avec la souris. Prenez-en l'habitude.

Le notebook est constitué d'une succession de cellules. Les cellules peuvent être dans le mode commande ou le mode édition. Un liseré de couleur repère la cellule actuellement selectionnée. Le liseré est bleu si la cellule est en mode commande et vert si elle est en mode édition.

Le mode édition

Pour entrer dans le mode édition de la cellule sélectionnée, il suffit de presser la touche [Enter] ou de cliquer à l'intérieur de la cellule. Quand une cellule est en édition vous pouvez saisir du texte comme dans un éditeur classique.

Lorsque le curseur est en début de ligne ou lorsque vous avez sélectionné du texte, l'appui sur la touche [Tab] (respectivement [Shift-TAB]) indente (respectivement désindente) les lignes correspondantes.

Voici d'autres raccourcis clavier :

  • [Ctrl-,] : Commente ou décommente une ligne. Si du texte a été sélectionné, toutes les lignes correspondantes sont commentées ou décommentées.

  • [Ctrl-A] : Sélectionne tout le texte de la cellule.

  • [Ctrl-Z] : Annule les dernières saisies de texte.

  • [Ctrl-Enter] : Exécute la cellule.

  • [Shift-Enter] : Exécute la cellule et sélectionne la cellule suivante. L'appui répété de cette touche permet ainsi d'exécuter pas à pas toutes les cellules du notebook.

  • [Alt-Enter] : Exécute la cellule et insére une nouvelle cellule juste en dessous.

  • [ESC] : Passe dans le mode commande.

Le mode commande

Quand vous êtes dans le mode commande, vous pouvez ajouter ou supprimer des cellules mais vous ne pouvez pas saisir de texte dans une cellule. Notez bien que dans ce mode, certaines touches du clavier sont associées à des actions sur le notebook. Tenter de saisir du texte dans ce mode peut donc produire des effets inattendus.

Voici les raccourcis principaux disponibles en mode commande :

  • A et B : Insèrent une nouvelle cellule, respectivement au-dessus ou au-dessous de la cellule sélectionnée.

  • M : Transforme la cellule en une cellule de type Markdown (Cf. ci-dessous).

  • Y : Transforme la cellule en une cellule de type Code.

  • C et V : Copie et colle des cellules.

  • DD : Supprime la cellule sélectionnée.

  • Z : Annule la dernière suppression de cellule.

  • II : Interrompt l'exécution du code.

  • 00 : Redémarre l'interpréteur. Il se retrouve alors dans son état initial.

  • H : Affiche la liste de tous les raccourcis clavier.

Le langage Markdown

La touche M transforme la cellule sélectionnée en type Markdown. Vous pouvez alors rédiger du texte enrichi (titres, sous-titres, gras, italique, alinéas, tableaux, liens hypertexte, etc). Le texte de la cellule doit être rédigé en langage Markdown qui est un langage de balisage léger. La syntaxe markdown est facile à apprendre. Le plus simple est d'ailleurs de regarder des exemples de documents tels que celui que vous lisez actuellement. Il est également possible d'insérer des morceaux de texte en langage LaTeX pour composer des expressions mathématiques.

L'interpréteur Python

L'état de l'interpréteur

Lorsqu'on ouvre un terminal, un programme interpréteur de commandes système est lancé et attend les commandes pour les exécuter. De la même façon, en arrière-plan d'un notebook, un interpréteur de code a été lancé et attend lui aussi qu'on lui donne du code. Dans le coin supérieur droit vous pouvez voir qu'il s'agit ici de l'interpréteur Python 3.

Un notebook est constitué de cellules successives. Les cellules pour écrire du code sont repérables par le prompt In[ ]. Essayons :

In [ ]:
2 / 3
Out[ ]:
0.6666666666666666

Le texte 2 / 3 a été transmis à l'interpréteur Python 3. Il s'agit d'une expression Python 3 valide. L'interpréteur a donc pu l'évaluer. La valeur de l'expression (dont le type est ici un nombre flottant) est alors récupérée et représentée sous la forme du texte affiché à droite du prompt de sortie Out [ ]:

N'oubliez pas que vous êtes dans un notebook. Vous pouvez donc modifier l'expression ci-dessus et la ré-exécuter en appuyant sur [Shift-ENTER]. Faites-le ! Le numéro entre crochet est un compteur. Il est incrémenté à chaque sollicitation de l'interpréteur.

In [ ]:
hauteur = 2 + 2

L'exécution de l'instruction ci-dessus n'a produit aucun résultat en sortie. Cette instruction a cependant eu un effet. Elle a modifié l'état de l'interpréteur. En interne, l'interpréteur a associé la valeur de l'expression 2 + 2 (c'est-à-dire le type entier 4) au nom hauteur.

On peut alors exécuter :

In [ ]:
5 * hauteur
Out[ ]:
20

L'état de l'interpréteur est constitué de toutes les associations valeurs/noms qu'il mémorise en interne. Les séquences de code qui contiennent des affectations (opérateur =) modifient l'état de l'interpréteur. Un même code peut produire des effets différents selon l'état dans lequel se trouve l'interpréteur.

Observez les deux cellules de code ci-dessous. Selon que l'on exécute d'abord la premiere cellule ou d'abord la seconde cellule, l'état de l'interpréteur ne sera pas le même à l'issue. Ce n'est pas la même chose d'ajouter 1 à un nombre puis de doubler le résultat ou bien de doubler un nombre et d'ajouter le 1 ensuite.

In [ ]:
hauteur = hauteur + 1
In [ ]:
hauteur = 2 * hauteur

Lorsque vous ouvrez un notebook vous le visualisez tel qu'il a été sauvegardé. Vous voyez en particulier les résultats des séquences de code qui ont été exécutées. Cependant, à l'ouverture du notebook, un nouvel interpréteur tout neuf est lancé. Vous devez donc exécuter à nouveau les cellules. Mais dans quel ordre ? La réponse naturelle est : dans l'ordre où apparaissent les cellules. Mais cela suppose que la personne qui a réalisé le notebook a fait correctement les choses.

Les exceptions

Dans certaines situations, l'interpréteur peut s'interrompre en levant une exception. Les exceptions n'ont rien d'exceptionnelles. On en rencontre souvent, en particulier lorsque l'on commet des erreurs de programmation.

In [ ]:
T = [18, 5, 3]
T[3]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-6-f2846d826172> in <module>()
      1 T = [18, 5, 3]
----> 2 T[3]

IndexError: list index out of range

Pour comprendre ce qui a produit l'exception il faut :

  • identifier le nom de l'exception, ici IndexError,
  • lire le message associé, ici 'list index of range',
  • identifier l'instruction qui l'a provoqué, ici l'évaluation de T[3] à la ligne 2.

Il se peut aussi que le code que l'on exécute ne se termine pas :

In [ ]:
k = 1
while k > 0:
    k = k + 1
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-7-7218bf9e3b57> in <module>()
      1 k = 1
----> 2 while k > 0:
      3     k = k + 1

KeyboardInterrupt: 

Lorqu'une cellule de code s'exécute le prompt comporte une étoile In[ *]. Pour interrompre l'interpréteur, il suffit d'appuyer deux fois sur la touche I. Si cela s'avère nécessaire, il est également possible de redémarrer l'interpréteur. Pour cela il faut appuyer deux fois sur la touche 0. L'interpréteur se retrouve alors dans son état initial.

L'entrée et la sortie standards

Les programmes, quels qu'ils soient, lisent des données en provenance de sources variées (fichiers, réseaux, ports usb, etc). Ils envoient eux-mêmes des données vers diverses destinations. Dans tous les cas, cela se traduit par la lecture ou l'écriture de flux d'octets.

Tout programme peut lire un flux d'octets particulier que l'on appelle l'entrée standard. Lorsqu'il s'agit d'un programme lancé par un utilisateur, l'entrée standard est généralement le flux d'octets généré par le clavier. Tout programme peut aussi écrire sur un flux particulier que l'on appelle la sortie standard.

Dans un programme Python la fonction print permet d'écrire sur la sortie standard.

In [ ]:
print("Accueil s'écrit :")
for x in "accueil":
    print(x)
Accueil s'écrit :
a
c
c
u
e
i
l

Remarquez que l'exécution du code ci-dessus n'a retourné aucune valeur. Il n'y a pas de prompt Out[ ]. Remarquez aussi que chaque fonction print a également envoyé un caractère saut de ligne sur la sortie standard.

Dans un programme Python la lecture de l'entrée standard se fait au moyen la fonction input :

In [ ]:
texte = input("Veuillez entrer un texte : ")
texte.upper()
Veuillez entrer un texte : bonjour
Out[ ]:
'BONJOUR'

L'interpréteur a écrit le texte Veuillez entrer un texte : sur la sortie standard. Les caractères générés par l'utilisateur sur l'entrée standard ont également été réémis en écho sur la sortie standard.

L'expression texte.upper() a été évaluée et sa valeur retournée comme résultat pour Out[ ].

De façon générale, si la dernière ligne d'une séquence de code est une expression non affectée à une variable, alors la valeur de cette expression est retournée comme résultat pour Out[ ].

L'aide en ligne IPython

Lorsque vous sélectionnez Python 3 comme kernel, le programme lancé par Jupyter n'est pas directement l'interpréteur Python mais le programme IPython.

Comme expliqué à la fin du document Python dans le terminal, le texte soumis par l'utilisateur est d'abord analysé par IPython qui utilise l'interpréteur Python chaque fois qu'il est nécessaire d'évaluer du code python. Ce principe permet d'ajouter des fonctionnalités très pratiques mais qui ne font pas partie du langage Python.

IPython offre par ce biais plusieurs mécanismes d'assistance dont il faut user sans modération.

L'auto-complétion

Lorsque vous commencez à saisir un nom connu de l'interpréteur, l'appui sur la touche [TAB] complète le nom automatiquement. Si plusieurs noms sont possibles un menu contextuel vous propose de choisir. Ceci économise de la frappe tout en évitant les erreurs d'orthographe dans les noms des variables.

Les infobulles

Lorsque le curseur de saisie est sur un nom connu de l'interpréteur (ou immédiatement à droite), l'appui sur [Shift-TAB] affiche une infobulle qui donne un descriptif succinct de l'objet désigné par ce nom. C'est un moyen rapide de vérifier par exemple quels sont les arguments qui sont attendus par une fonction.

La documentation d'un objet

Pour lire la documentation en ligne concernant un objet python (module, fonction, classe, etc), il suffit d'ajouter un ? juste après le nom et d'appuyer sur la touche [Enter]. Un pager s'ouvre alors avec la dite documentation. En doublant le ? le pager donne une documentation plus complète.

Les commandes systèmes via IPython

Lorsque le texte de la cellule débute par un ! alors IPython en déduit que le reste du texte n'est pas du code python mais une commande système qui doit être exécutée par le Shell. Autrement dit IPython peut se substituer au terminal. Par exemple, la commande ci-dessous exécutée dans un terminal permet de visualiser les premiers octets du fichier debuter-avec-les-notebooks-master.ipynb.

xxd debuter-avec-les-notebooks-master.ipynb | head

Le programme xxd affiche les octets sur la sortie standard. Mais ici le flux de sortie est redirigé vers l'entrée du programme head. Celui-ci envoie sur la sortie standard uniquement les 10 premières lignes du texte qu'il lit en entrée.

Pour exécuter cette commande système depuis le notebook, il suffit de placer un point d'exclamation au tout début :

In [ ]:
!xxd debuter-avec-les-notebooks-master.ipynb | head
00000000: 7b0a 2022 6365 6c6c 7322 3a20 5b0a 2020  {. "cells": [.  
00000010: 7b0a 2020 2022 6365 6c6c 5f74 7970 6522  {.   "cell_type"
00000020: 3a20 2263 6f64 6522 2c0a 2020 2022 6578  : "code",.   "ex
00000030: 6563 7574 696f 6e5f 636f 756e 7422 3a20  ecution_count": 
00000040: 312c 0a20 2020 226d 6574 6164 6174 6122  1,.   "metadata"
00000050: 3a20 7b0a 2020 2020 2263 6f6c 6c61 7073  : {.    "collaps
00000060: 6564 223a 2074 7275 650a 2020 207d 2c0a  ed": true.   },.
00000070: 2020 2022 6f75 7470 7574 7322 3a20 5b5d     "outputs": []
00000080: 2c0a 2020 2022 736f 7572 6365 223a 205b  ,.   "source": [
00000090: 0a20 2020 2022 5f74 6974 6c65 203d 205c  .    "_title = \
xxd: Broken pipe

Voici d'autres exemples :

In [ ]:
# Liste le contenu du dossier courant
!ls -l
total 60
-rw-rw-r-- 1 denis denis   237 août  29 14:13 arithmetique2.py
-rw-r--r-- 1 denis denis   439 août  13  2015 arithmetique.py
-rw-r--r-- 1 denis denis 47475 août  29 16:24 debuter-avec-les-notebooks-master.ipynb
-rw-rw-r-- 1 denis denis   109 août  29 14:14 square.py
In [ ]:
# Envoie le texte du fichier sur la sortie standard
!cat arithmetique.py
""" Module de fonctions arithmétiques
"""

def euclide(a, b):
    """ Algorithme d'Euclide étendu
        Retourne le pgcd de `a` et `b`, ainsi que les coefficients de
        Bézout `u` et ` v` tels que au + bv = pgcd(a, b).
    """
    r0, u0, v0, r1, u1, v1 = a, 1, 0, b, 0, 1
    while r1 != 0:
        q = r0 // r1
        r0, u0, v0, r1, u1, v1 = (
        r1, u1, v1, r0 - q * r1, u0 - q * u1, v0 - q * v1)
    return r0, u0, v0
In [ ]:
# Affiche les 8 premières lignes du fichier
!head -n8 arithmetique.py
""" Module de fonctions arithmétiques
"""

def euclide(a, b):
    """ Algorithme d'Euclide étendu
        Retourne le pgcd de `a` et `b`, ainsi que les coefficients de
        Bézout `u` et ` v` tels que au + bv = pgcd(a, b).
    """
In [ ]:
# Copie les 8 premières lignes du fichier arithmetique.py 
# dans le fichier arithmetique2.py
!head -n8 arithmetique.py > arithmetique2.py
In [ ]:
!cat arithmetique2.py
""" Module de fonctions arithmétiques
"""

def euclide(a, b):
    """ Algorithme d'Euclide étendu
        Retourne le pgcd de `a` et `b`, ainsi que les coefficients de
        Bézout `u` et ` v` tels que au + bv = pgcd(a, b).
    """

Les commandes magiques de IPython

%pylab inline

Importe les modules numpy et matplotlib. L'option inline indique que les figures Matplotlib seront insérées dans le notebook lui-même plutôt que dans une fenêtre graphique à part. C'est la seule option possible car votre système ne dispose pas d'interface graphique.

L'instruction %pylab inline exécute en particulier les deux instructions

import numpy as np
import matplotlib.pyplot as plt
In [ ]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib
In [ ]:
X = np.linspace(-1, 1, 100)
plt.plot(X, X ** 2)
Out[ ]:
[<matplotlib.lines.Line2D at 0x7ff2d287a3c8>]

%%writefile

Crée un fichier de texte. L'exemple donné ci-dessous est un script python mais il peut s'agir de n'importe quel fichier de texte.

In [ ]:
%%writefile square.py
""" Exemple de scripts
"""

k = 1
s = 1
while k <= 10:
    print(k ** 2, s)
    s = s + 2*k + 1
    k = k + 1
Overwriting square.py

%less

Cette commande permet de visualiser le contenu d'un fichier de texte.

In [ ]:
%less square.py

%run

La commande %run foo exécute le script python foo.py comme si vous l'aviez exécuté directement depuis le terminal :

psiet-turing@Chato~$ python3 foo.py

La différence est qu'à la fin de l'exécution du script tous les objets définis par le script sont insérés dans l'espace de nom global de l'interpréteur.

In [ ]:
%run square
1 1
4 4
9 9
16 16
25 25
36 36
49 49
64 64
81 81
100 100
In [ ]:
k, s
Out[ ]:
(11, 121)

%timeit

Mesure le temps d'exécution d'une instruction ou d'une expression. Le code est exécuté plusieurs fois et le temps moyen d'exécution est affiché :

In [ ]:
!cat arithmetique.py
""" Module de fonctions arithmétiques
"""

def euclide(a, b):
    """ Algorithme d'Euclide étendu
        Retourne le pgcd de `a` et `b`, ainsi que les coefficients de
        Bézout `u` et ` v` tels que au + bv = pgcd(a, b).
    """
    r0, u0, v0, r1, u1, v1 = a, 1, 0, b, 0, 1
    while r1 != 0:
        q = r0 // r1
        r0, u0, v0, r1, u1, v1 = (
        r1, u1, v1, r0 - q * r1, u0 - q * u1, v0 - q * v1)
    return r0, u0, v0
In [ ]:
%run arithmetique
In [ ]:
euclide
Out[ ]:
<function __main__.euclide>
In [ ]:
euclide(123456789, 12345)
Out[ ]:
(3, 1642, -16420903)
In [ ]:
%timeit euclide(123456789, 12345)
100000 loops, best of 3: 3.79 µs per loop

%pastebin

Envoie un texte vers le serveur Github Gist. Tout le monde peut alors lire le texte à partir de l'url retournée par la commande.

On peut envoyer le contenu d'un fichier de texte :

In [ ]:
%pastebin arithmetique.py
Out[ ]:
'https://gist.github.com/0409dc7e9be57c539ffbf481a67c1c61'

On peut aussi envoyer le contenu d'une cellule, par exemple In[9] :

In [ ]:
%pastebin 9
Out[ ]:
'https://gist.github.com/838f1820703d0006b4ebdc41dfbc3da1'

%who et %whos

Affiche les objets qui sont actuellement définis dans l'interpréteur Python :

In [ ]:
%who
T	 X	 euclide	 hauteur	 k	 s	 texte	 x	 
In [ ]:
%whos
Variable   Type        Data/Info
--------------------------------
T          list        n=3
X          ndarray     100: 100 elems, type `float64`, 800 bytes
euclide    function    <function euclide at 0x7ff2d28c5950>
hauteur    int         10
k          int         11
s          int         121
texte      str         bonjour
x          str         l

Exporter un notebook

Lorsque vous avez terminé la rédaction de votre document, vous pouvez le télécharger sur l'ordinateur local dans le format qui vous convient. Choisir pour cela File/Download as...

Vous pouvez aussi publier votre document dans la partie publique de votre dossier personnel (~/pub/). Supposons par exemple qu'il existe un notebook ~/doc/phys/meca/oscillateur.ipynb dans votre dossier personnel. Ouvrez alors un terminal et exécutez les commandes suivantes :

jupyter nbconvert ~/doc/phys/meca/oscillateur
cp ~/doc/phys/meca/oscillateur.html ~/pub

La première commande a créé un fichier oscillateur.html dans le dossier où se trouve le fichier le notebook. La seconde commande a copié ce fichier HTML dans le dossier /pub/.

Quiconque peut désormais consulter le document à partir de l'adresse : https://jhub.lycee-chateaubriand.fr/~psiet-turing/oscillateur.html. (En supposant que votre compte est psiet-turing).

Remarque importante : Vous êtes responsable de ce que vous publiez sur l'internet.

Pour produire un fichier PDF plutôt qu'un fichier HTML vous devez exécuter la commande

jupyter nbconvert --to pdf ~/doc/phys/meca/oscillateur