Je viens de découvrir ces deux projets très chousettes : tutormagic et nbtutor, tous les deux lias bres, gratuits, en Python, et proposant d'intégrer des visualisations intéractives, venant ou inspirées du merveilleux PythonTutor.com.
Peut-on utiliser l'un des deux en mode offline (sans une iframe vers PythonTutor) ? Je pense que non pour tutormagic, je pense que oui pour nbtutor.
Est-ce que ça marche bien pour tous les types de Python de base : booléens/entiers/flottants, chaînes, objets, nombres complexes, tableau/list
, dictionnaire/ensemble ? Probablement ! Réponse : oui !
Et avec des tableaux numpy ? Probablement !. Réponse : oui pour tutormagic
avec ma nouvelle version qui ajoute le mode "py3anaconda" (à voir si cette PR est acceptée !)
tutormagic
peut-être, tant qu'on ne génère pas de figure... avec nbtutor
apparemment oui.tutormagic
en mode "py3anaconda" supporte plein de modules scientifiques puisque Anaconda 5.2 supporte plein de trucs : scipy, sympa, scikit-learn, et j'en passe !Jupyter a beaucoup changé ces dernières anneés, entre 2018 et 2021..
Mais ave jupyter classique, normalement ça va les "vieilles" extensions fonctionnent encore. Jupyter Lab, c'est une autre histoire !
pip
, et s'installe en une commande !Dans mon installation de Python 3, j'ai installé les deux paquets tutormagic
et nbtutor
Où installer ?
sudo pip install
;pip install
(ou avec conda install
mais pas testé).%%bash
echo "Démonstration (pas exécutée)"
exit 0 # enlevez ça pour refaire ça chez vous
# tapez ça dans votre terminal
pip install tutormagic nbtutor
# si ça marche pas, rajoutez sudo
sudo pip install tutormagic nbtutor
# il faut manuellement installer et activer nbtutor
jupyter nbextension install --overwrite --py nbtutor
jupyter nbextension enable --py nbtutor
Et donc ce notebook peut déclarer ses dépendances :
%load_ext watermark
watermark -a "Lilian Besson (Naereen)" -p tutormagic,nbtutor,numpy,matplotlib
test = "Démo de jupyter notebook"
print(test)
somme = 0
MAX_I = 10
for i in range(1, MAX_I):
somme += i**3
print(f"Somme des {MAX_I} premiers cubes = {somme}")
Je vais emprunter le petit problème arithmético-algorithmique suivant, posé à mes élèves au début de février 2021 :
Mini challenge algorithmique pour les passionnés en manque de petits exercices de code : (optionnel) Vous avez dû observer que ce mois de février est spécial parce que le 1er février est un lundi, et qu'il a exactement 4 lundis, 4 mardis, 4 mercredis, 4 jeudis, 4 vendredis, 4 samedis et 4 dimanches.
Question : Comptez le nombre de mois de février répondant à ce critère (je n'ai pas trouvé de nom précis), depuis l'année de création de l'ENS Rennes (1994, enfin pour Cachan antenne Bretagne) jusqu'à 2077 (1994 et 2077 inclus).
Pour plus d'informations, cf ce notebook) (aussi sur GitHub et nbviewer).
Avec le module calendar on pourrait faire comme en Bash : imprimer les calendriers, et rechercher des chaînes particulières... mais ce n'est pas très propre. Essayons avec ce même module mais en écrivant une solution fonctionnelle !
import calendar
def filter_annee(annee):
return (
set(calendar.Calendar(annee).itermonthdays2(annee, 2))
& {(1,0), (28, 6), (29, 0)}
) == {(1, 0), (28, 6)}
filter_annee(2020), filter_annee(2021), filter_annee(2022)
C'est un bon exemple car il utilise des listes, des ensembles etc. Peut-être restreindre le nombre d'année considérées, pour simplifier ?
Et donc on a juste à compter les années, de 1994 à 2077 inclus, qui ne sont pas des années bissextiles et qui satisfont le filtre :
%%time
len(list(filter(filter_annee, ( annee
for annee in range(1994, 2077 + 1)
# if not calendar.isleap(annee) # en fait c'est inutile
)
)))
tutormagic
¶Je suis le tutoriel présent sur la documentation.
%load_ext tutormagic
%%tutor --lang python3 --height 500
URL = "http://PythonTutor.com"
test = "Démo de tutormagic"
explication = f"avec un iframe vers {URL}"
print(test, explication)
somme = 0
MAX_I = 10
for i in range(1, MAX_I):
somme += i**3
print(f"Somme des {MAX_I} premiers cubes = {somme}")
Ca marche bien pour ce petit exemple !
Essayons plus solide :
%%tutor --lang python3 --run --height 500 --curInstr 11
import calendar
def filter_annee(annee):
return (
set(calendar.Calendar(annee).itermonthdays2(annee, 2))
& {(1,0), (28, 6), (29, 0)}
) == {(1, 0), (28, 6)}
nb_bonnes_annees = len(list(filter(filter_annee, ( annee
for annee in range(1994, 2077 + 1)
# if not calendar.isleap(annee) # en fait c'est inutile
)
)))
Whooo! Ca a pris 763 étapes, on était pas loin de la limite des 1000 étapes sur PythonTutor.com, mais ça marche !
Ca marche vraiment bien !
En repliant le code (avec une extension, Codefolding), on peut montrer juste le début (la "magic" %%tutor
) et l'iframe !
Bonus : ça fonctionne si on ferme le notebook et qu'on le rouvre, sans même avoir à réexécuter la cellule ! Très pratique pour préparer son cours !
nbtutor
¶Je suis aussi la documentation sur la page GitHub de nbtutor.
%reload_ext nbtutor
Ca semble bien chargé, essayons !
%%nbtutor --reset --force --debug
explication = "Démo de nbtutor, normalement sans iframe vers PythonTutor.com"
print(explication)
somme = 0
MAX_I = 10
for i in range(1, MAX_I):
somme += i**3
print(f"Somme des {MAX_I} premiers cubes = {somme}")
Ca marche pas :( Apparemment ça marche pas bien si tutormagic a aussi été exécuté...
Je vois sur la documentation du projet que l'installation de l'extension ajoute une "barre d'outil de cellule", mais je ne l'ai pas... je l'ai désormais, en rechargeant le notebook !
Elle prend la place de la barre d'outil pour les slides RISE, mais ce n'est pas grave, on peut passer de l'un à l'autre.
Je rencontre le même problème que ce ticket.
Essayons de comprendre ce problème de nbtutor !!
!pip show nbtutor
!jupyter-nbextension list 2>&1 | grep -A 2 nbtutor
Bon, le package semble installé, et ajouté comme une extension Jupyter, qui le reconnaît et la valide...
Chargez l'extension avec %load_ext nbtutor
a fonctionné... juste la magic %%nbtutor
ne marche pas.
%%nbtutor?
Sa documentation se charge bien !
Docstring:
::
%nbtutor [-r] [-f] [-i] [-d N] [--digits D] [--max_size S] [--step_all]
[--expand_arrays] [--nolies] [--debug]
Mais rien ne se passe ! Même en mode --debug
...
%%nbtutor --reset --force --debug
x = sum([i**3 for i in range(1, 10)])
print(x)
Aucun message d'erreur, rien dans la console qui a lancé Jupyter...
Je pense que l'extension est cassée avec les nouvelles variantes de Python ou de Jupyter, qui comme je le suspectais a beaucoup changé depuis 2016 (dernier commit de ce projet)...
J'ai demandé au développeur s'il maintenait encore son projet.
Et pour l'exemple plus gros :
%%nbtutor --reset --force
import calendar
def filter_annee(annee):
return (
set(calendar.Calendar(annee).itermonthdays2(annee, 2))
& {(1,0), (28, 6), (29, 0)}
) == {(1, 0), (28, 6)}
nb_bonnes_annees = len(list(filter(filter_annee, ( annee
for annee in range(1994, 2077 + 1)
# if not calendar.isleap(annee) # en fait c'est inutile
)
)))
Ca marche ! Wooo!
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
# avec la police "Humour Sans", téléchargée depuis https://aur.archlinux.org/packages/ttf-humor-sans/
with plt.xkcd():
plt.figure(figsize=(14,8))
plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)
plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)
plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)
plt.legend()
plt.xlabel("Axe $x$")
plt.ylabel("Axe $y$")
plt.title("Trois fonctions de la variable réelle")
plt.show()
tutormagic
?¶%%tutor --lang python3 --height 100
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
with plt.xkcd():
plt.figure(figsize=(14,8))
plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)
plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)
plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)
plt.legend()
plt.xlabel("Axe $x$")
plt.ylabel("Axe $y$")
plt.title("Trois fonctions de la variable réelle")
plt.show()
Ici, le problème vient de la "cell magic" %matplotlib
en ligne 4 !
Et si on essaie sans cette cell magic ?
%%tutor --lang python3 --height 500
import numpy as np
import matplotlib.pyplot as plt
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
print(Y1)
print(Y2)
print(Y3)
Evidemment, ça ne marche pas ! Numpy et matplotlib ne sont PAS supportés dans PythonTutor.com...
Seul les modules suivants sont disponibles avec le mode "Python3" basique :
Only these modules can be imported:
__future__, abc, array, bisect, calendar, cmath,
collections, copy, datetime, decimal, doctest, fractions,
functools, hashlib, heapq, io, itertools, json,
locale, math, operator, pickle, pprint, random,
re, string, time, types, typing, unittest
Ca allait être un avantage de nbtutor... au moins la partie numpy.
Bon, et bien on peut essayer "Python 3.6 with Anaconda (experimental)".
Depuis le site web, j'arrive à utiliser Python 3.6 + Anaconda 5.2 EXPERIMENTAL! pour le code suivant.
%%tutor --lang py3anaconda --height 500
import numpy as np
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
print(Y1)
print(Y2)
print(Y3)
%%tutor --lang py3anaconda --height 500
# https://docs.scipy.org/doc/scipy/reference/tutorial/integrate.html
x = 4
print("Hello world!" * x)
import numpy as np
I = np.sqrt(2/np.pi)*(18.0/27*np.sqrt(2)*np.cos(4.5) - 4.0/27*np.sqrt(2)*np.sin(4.5))
print(I)
import scipy.integrate as integrate
import scipy.special as special
result = integrate.quad(lambda x: special.jv(2.5,x), 0, 4.5)
print(result)
I += np.sqrt(2*np.pi) * special.fresnel(3/np.sqrt(np.pi))[0]
print(I)
print(abs(result[0]-I))
POUF ça marche, merci à ma petite modification (trois lignes changées).
C'est quand même beau les logiciels open-source...
Au fait, PythonTutor n'est plus libre, mais un fork récent existe : c'est peut-être un vol de propriété intellectuelle, mais bon, ça peut dépanner !
TODO: l'installer chez moi, et faire en sorte que tutormagic fonctionne en local aussi ! Regardez ce ticket assez récent, mais pas avancé (mais très détaillé), et celui là sur un clone de PythonTutor.
=> J'ai réussi à avoir PyTutor (v5-unity) mais juste pour Python3... pas C ou Java... et je sais pas comment faire !
nbtutor
?¶%%nbtutor --reset --force --debug
import numpy as np
import matplotlib.pyplot as plt
X = np.linspace(-10, 10, 40)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
with plt.xkcd():
plt.figure(figsize=(14,8))
plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)
#plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)
#plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)
plt.legend()
#plt.xlabel("Axe $x$")
#plt.ylabel("Axe $y$")
plt.title("Trois fonctions de la variable réelle")
plt.show()
Je suspectais que ça n'allait pas marcher avec nbtutor puisque ça ne marchait pas avant !! puisque nbtutor a les mêmes limitations que tutormagic !
Mais ça marche pour du numpy pur !
Et ça marche aussi pour des petits codes utilisant Matplotlib, mais très vite mon CPU prend 100%, ça rame, je ne vais pas chercher davantage.
%%nbtutor --reset --force
import numpy as np
X = np.linspace(-10, 10, 400)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
Y = [Y1, Y2, Y3]
for i, y in enumerate(Y):
for f in [np.min, np.max, np.mean]:
name_f = str(f.__name__)
print(f"{name_f} of Y{i+1}, result = {f(y)}")
Cet exemple fonctionne !
Je vais écrire un programme C qui fait un petit truc intéressant, et essayer les choses suivantes :
tutormagic
! Je pense que ça devrait fonctionner, car c'est une iframe vers PythonTutor.com, qui supporte le C (version C11 ou C17) avec gcc ;nbtutor
! Si ça marche, c'est que ça utilise le serveur PythonTutor sans le montrer, sinon ça ne devrait pas marcher./* Cette cellule est écrite en langage C, il ne faut pas l'exécuter avec le kernel Python */
/* Installez le kernel https://github.com/brendan-rius/jupyter-c-kernel */
#include <stdio.h>
#define MIN_I 1
#define MAX_I 10
#define MIN_POWER 1
#define MAX_POWER 5
unsigned long power(int n, unsigned long acc, unsigned long x) {
if (n == 0) { return acc; }
if (n % 2 == 0) { return power(n/2, acc, x*x); }
else { return power((n-1)/2, acc * x, x*x); }
}
int main(void) {
for (int i = MIN_I; i <= MAX_I; i++) {
printf("\n- Pour i = %i :", i);
for (int n = MIN_POWER; n <= MAX_POWER; ++n) {
printf("\n\ti^%i = %li", n, power(n, 1, i));
}
}
}
tutormagic
¶Alors évidemment, la "cell magic" %%tutor
ne marche pas depuis le kernel C, mais en repassant au kernel Python 3, on peut faire :
%reload_ext tutormagic
%%tutor --lang c --height 600
/* Cette cellule est écrite en langage C, il ne faut pas l'exécuter avec le kernel Python */
/* Installez le kernel https://github.com/brendan-rius/jupyter-c-kernel */
#include <stdio.h>
#define MIN_I 1
#define MAX_I 10
#define MIN_POWER 1
#define MAX_POWER 5
unsigned long power(int n, unsigned long acc, unsigned long x) {
if (n == 0) { return acc; }
if (n % 2 == 0) { return power(n/2, acc, x*x); }
else { return power((n-1)/2, acc * x, x*x); }
}
int main(void) {
for (int i = MIN_I; i <= MAX_I; i++) {
printf("\n- Pour i = %i :", i);
for (int n = MIN_POWER; n <= MAX_POWER; ++n) {
printf("\n\ti^%i = %li", n, power(n, 1, i));
}
}
}
Conclusion ?
tutormagic marche TROP bien pour du C !
Il faudrait ajouter le OCaml... Cf discussion initiée ici !
nbtutor
¶Il n'y a pas pour l'instant la possibilité d'utiliser nbtutor pour d'autres langages, mais ce ticket en parle.
tutormagic
¶IPython.display(...)
, donc pas moyen de les garder... et même en "Save notebook widget state" ça ne fonctionne pas, je m'en doutais ;nbtutor
¶tutormagic
!Est-ce que ça marche bien depuis un slide live RISE ? Je pense que oui ! Oui !
tutormagic
, ça marche sans aucun problème ! Pour Python et C !nbtutor
, ça marche mais les contrôles ne s'affichent pas... j'ai ouvert ce ticket, à voir si le développeur du projet répondra à mes questions !Qu'est-ce que ça donnerait une fois le notebook transformé en page web HTML statique ?
Et si je transforme en PDF, ça donne quoi ?
Plus de notebooks ? Merci d'avoir lu ! Allez voir d'autres notebooks intéressants dans ma collection qui grandit depuis 2016. Plein de notebooks en Python niveau L2/L3 mais aussi en OCaml et Python niveau agrégation, et bien plus ! En Java, Rust, Julia, Bash, GNUPlot et GNU Octave et des démonstrations d'extensions peu connues mais super intéressantes !