Tutoriel python : la bibliothèque Ipywidgets
Table des matières
Introduction
Chaque fois que vous devez modifier la sortie de votre cellule Jupyter notebook, vous devez changer le script (code) et relancer la cellule concernée. Cela peut être lourd, inefficace et une source d'erreurs. C'est dans ce niveau que la bibliothèque ipywidgets entre en jeu. Elle fournit une interface interactive qui permet de générer les données/résultats, sans avoir à modifier le script.
Dans ce tutoriel, nous allons vous présenter deux modules interact et widgets. Nous allons passer en revue quelques informations essentielles et traiter des exemples en utilisant les DataFrames pandas.
1. Prérequis
La toute première exigence à prendre en considération est l’environnement fonctionnel de Jupyter. La mise en place et l’installation n'entre pas dans le cadre du ce tutoriel. Il faut aussi savoir comment manipuler les DataFrames pandas pour pouvoir suivre l’exemple d’utilisation. Il est conseillé d’aller voir le tutoriel sur la bibliothèque pandas : Tutoriel python : Comment installer et utiliser pandas ?
La première étape, comme d'habitude, consiste à installer la bibliothèque ipywidgets :
Syntaxe:
pip install ipywidgets
Une fois l’installation terminée, vous pouvez l’importer avec la commande import. Nous importons les modules nécessaires au fur à mesure de ce tutoriel.
Passons maintenant à la pratique ! Nous allons vous présenter deux modules qui vont rendre vos Jupyter notebook très interactifs : Interact et Widgets.
2. Le module INTERACT
Un widget est un élément d'interface interactive, tel qu'un bouton, une liste déroulante ou une zone de texte. Les widgets Ipython peuvent être créés soit directement, soit par une fonction d'interaction qui permet de générer une sortie interactive. Nous explorons d'abord le module interact, car il est pratique pour une utilisation rapide. La fonction d’interaction (interact) prend une fonction comme premier argument, suivi par les arguments de la fonction avec leurs valeurs possibles. Cela génère une interface graphique avec laquelle vous pouvez interagir. Cela peut sembler assez abstrait au premier abord, mais un exemple devrait le rendre plus clair. Commençons d’abord par importer le module :
Syntaxe:
from ipywidgets import interact
Ensuite, créons un exemple assez simple :
Syntaxe:
def square(x):
return x**2
interact(square, x = (0,20))
Résultat d’exécution:
La fonction square ci-dessus permet de retourner le carré d’un nombre. Nous passons square dans interact, avec un intervalle de nombres entiers allant de 0 à 20 permettant ainsi de créer un curseur interactif. Chaque fois que nous choisissons un nombre, la fonction square est appelée et le message imprimé est mis à jour.
Vous vous demandez peut-être comment interact décide de créer un curseur interactif. Il s'avère que ce choix est basé sur les options de saisie. Dans l'exemple précédant, celles-ci ont été données sous la forme(0,20) (début, fin, pas).
Nous pourrons également créer une liste déroulante en passant une liste de valeurs ou une case à cocher à l’aide d’un booléen. L’exemple suivant montre cela en utilisant une autre fonction product (qui retourne le produit de deux nombres) avec différentes options de saisie pour créer plusieurs types de sortie interactive (widgets).
Syntaxe:
from ipywidgets import fixed
def product(x , y , z):
if z:
return x * y
else:
return (x, y)
# x curseur interactif : entier entre 0 et 10 avec un pas de 2
# y curseur interactif : réel entre 0 et 10 avec un pas de 0.1
# z booléen
interact(product, x = (0, 10, 2), y = (0.0, 10.0, 0.1), z = True)
# x liste déroulante : trois valeurs 10, 20 et 30
# y fixe : valeur 10
# z booléen
interact(product, x = [10, 20, 30], y = fixed(10), z = True)
Résultat d’exécution
Comme vous l’avez déjà remarqué, interact n'est pas un module limité aux fonctions d'un seul argument. Nous pouvons utiliser plusieurs arguments pour créer plusieurs widgets, en suivant les mêmes règles ci-dessous. Dans cet exemple, nous avons utilisé une fonction à trois arguments pour créer trois widgets différents et nous avons également utilisé la fonction fixed pour fixer l'argument y en 10 (on obtient alors des sorties interactives seulement pour x et z). Essayer par vous-même, c’est assez simple mais en même temps très utile.
3. Le module WIDGETS
Souvent, nous souhaitons créer des widgets manuellement, nous allons laisser de côté la fonction interact et définir explicitement chacun des contrôles que nous désirons. Il existe un large choix dans le modulewidgets. Le script suivant crée une liste déroulante avec interact contenant toutes les classes définies dans le module et affiche également une description :
Syntaxe:
from ipywidgets import widgets
def help_me(function):
command = 'help(widgets.'+function+')'
return exec(command)
interact(help_me, function = dir(widgets))
Résultat d’exécution:
Nous allons générer plusieurs widgets manuellement en utilisant le module widgets. Commençons par créer un curseur interactif, nous utiliserons soit la fonction IntSlider() pour créer un curseur de valeurs entières ou FlaotSlider() pour créer un curseur de valeurs réelles. Nous pouvons ajouter comme paramètre : les valeurs minimales et maximales, le pas, une description et une valeur initiale:
Syntaxe:
curseur1 = widgets.IntSlider(
min=0,
max=10,
step=1,
description='curseur1:',
value = 3
)
curseur2 = widgets.FloatSlider(
min=0,
max=10,
step=0.5,
description='curseur2:',
value = 5.5
)
Nous pouvons utiliser la fonction display() pour afficher le widget :
Syntaxe:
from IPython.display import display
display(curseur1)
display(curseur2)
Résultat d’exécution:
Nous pouvons aussi utiliser les deux fonctions HBox() et VBox() pour afficher les widgets horizontalement ou verticalement:
Syntaxe:
widgets.HBox([curseur1, curseur2])
Résultat d’exécution:
Pour récupérer la valeur d'un widget, nous allons utiliser l’attribut value :
Syntaxe:
print(curseur1.value)
print(curseur2.value)
Dans le script suivant, nous allons créer plusieurs widgets simultanément :
- IntRangeSlider() qui permet de sélectionner une limite inférieure et supérieure d'une plage.
- Dropdow() permet de créer une liste déroulante.
- RadioButtons() permet de sélectionner une valeur unique dans une liste d'options.
- combobox() affiche une liste de possibilités de correspondances lorsqu’on commence à taper.
- Button() permet de créer un bouton.
- Label() affiche un texte non éditable.
- Text() affiche un texte éditable.
Syntaxe:
### widget 1
int_range_slider = widgets.IntRangeSlider(
value = (20, 40),
min = 0,
max = 100,
step = 2,
description = 'Intervalle:'
)
### widget 2
dropdown = widgets.Dropdown(
value = 'lundi',
options = ['lundi', 'mardi', 'jeudi', 'vendredi'],
description = 'Liste:'
)
### widget 3
buttons = widgets.RadioButtons(
value = 'lundi',
options = ['lundi', 'mardi', 'jeudi', 'vendredi'],
description = 'Choix multiple'
)
### widget 4
combobox = widgets.Combobox(
placeholder = 'Nom de la ville ...',
options = ['Rabat', 'Rome', 'Lisbon', 'London', 'Paris'],
description = 'combo box'
)
### widget 5
button = widgets.Button(
description = 'Nom du bouton',
button_style = 'success', # '', success', 'info', 'warning' et 'danger'
)
### widget 6
Label = widgets.Label('Titre de la variable')
### widget 7
zone_text = widgets.IntText()
# Affichage
widgets.VBox(
[
int_range_slider,
dropdown,
buttons,
combobox,
button,
Label,
zone_text,
]
)
Résultat d’exécution:
Les deux fonctions HBox() et VBox() peuvent également être utilisées pour créer des mises en page plus complexes. Ci-dessous, nous créons deux VBox. Ensuite, nous plaçons les VBox elles-mêmes dans une HBox pour les disposer l'une à côté de l'autre :
Syntaxe:
###
dropdown = widgets.Dropdown(
value = 'lundi',
options = ['lundi', 'mardi', 'jeudi', 'vendredi'],
description = 'Liste:'
)
###
buttons = widgets.RadioButtons(
value = 'lundi',
options = ['lundi', 'mardi', 'jeudi', 'vendredi'],
description = 'Choix:'
)
###
b1 = widgets.Button(
description = 'Gauche',
button_style = 'success', # '', success', 'info', 'warning' et 'danger'
)
b2 = widgets.Button(
description = 'Droit',
button_style = 'success', # '', success', 'info', 'warning' et 'danger'
)
###
vbox1 = widgets.VBox([widgets.Label('Gauche'), buttons, b1])
vbox2 = widgets.VBox([widgets.Label('Droit'), dropdown, b2])
###
widgets.HBox([vbox1, vbox2])
Résultat d’exécution:
Les widgets peuvent répondre à des événements, qui sont soulevés lorsqu'un utilisateur interagit avec eux. Un exemple simple est le fait de cliquer sur un bouton pour qu'une action se produise. Nous allons ici créer un simple bouton appelé button. Pour le lier à l'événement déclenché à partir de la fonction jour, nous assignons la fonction à la méthode on_click du bouton. Lorsque le bouton est cliqué, il affiche le jour :
Syntaxe:
Jours = widgets.Dropdown(
value = 'Lundi',
options = ['Lundi', 'Mardi', 'Jeudi']
)
button = widgets.Button(
description = 'affiche le jour',
button_style = 'success'
)
def jour(obj):
print(Jours.value)
button.on_click(jour)
widgets.VBox([Jours, button])
Résultat d’exécution:
4. Exemple d’utilisation : Pandas DataFrames
Il faut commencer d’abord par importer une table de données pour pouvoir la manipuler. Dans ce tutoriel, nous utiliserons la bibliothèque pydataset. Installez-la avec la commande suivante :
Syntaxe:
!pip install pydataset
Importez le module data qui contient plusieurs tables de données. Dans ce tutoriel, nous utiliserons la table de données nommée movies. Vérifions qu’il s’agit bien d’une DataFrame pandas, créons une copie et affichons les 5 premières lignes :
Syntaxe:
from pydataset import data
df_movies = data('movies')
print( type(df_movies) )
df_copy = df_movies.copy()
df_copy.head()
Résultat d’exécution:
Supposant maintenant, que nous voulons filtrer cette table de données par année (obtenir que les films de l’année 1971 par exemple), le script est le suivant :
Syntaxe:
df_copy.loc[df_copy['year'] == 1971].head()
Résultat d’exécution:
Ensuite, nous souhaitons refaire la même chose mais cette fois-ci pour toutes les années. Affichons d’abord les années présentes dans la DataFrame en utilisant la fonction unique() :
Syntaxe:
df_copy.year.unique()
Résultat d’exécution:
Comme vous l’avez remarqué, il y a plusieurs années différentes. Alors, pour ne pas répéter le script précédant pour chaque année, il vaut mieux générer une interface interactive en utilisant interact :
Syntaxe:
List_year = sorted( df_copy.year.unique().tolist() )
def select_year(year):
return df_copy[['title', 'year', 'rating']].loc[df_copy['year'] == year].reset_index(drop = True)
interact(select_year, year = List_year)
Résultat d’exécution:
Nous n’avons affiché que le nom du film, l’année et le rating. Vous pouvez l’essayer par vous-même pour remarquer que les années sont triées dans l’ordre croissant.
Supposant que vous voulez aussi filtrer les films en utilisant le rating, le script serait le suivant :
Syntaxe:
def select_year(year, rating):
return df_copy[['title', 'year', 'rating']].loc[(df_copy['year'] == year) & (df_copy['rating'] >= rating)].reset_index(drop = True)
interact(select_year, year = List_year, rating = (0.0,10.0))
Résultat d’exécution:
Créons un autre exemple, mais cette fois-ci en utilisant widgets. Nous allons utiliser les fonctions suivantes : describe() qui génère une description statistique de la table de données et isnull().value_counts() qui compte les valeurs manquantes. Le script suivant, crée une interface interactive qui permet d’utiliser les deux fonctions sur n’importe quelle colonne de la table de données :
Syntaxe:
### liste des colonnes
List_columns = df_copy.columns.tolist()
###
Titre = widgets.Label('Nom de la colonne:')
###
columns = widgets.Dropdown(
value = 'title',
options = List_columns
)
###
button1 = widgets.Button(
description = 'Describe',
button_style = 'success'
)
button2 = widgets.Button(
description = 'Valeurs manquantes',
button_style = 'success'
)
###
def describe(column):
display(df_copy[columns.value].describe())
button.on_click(describe)
def NAN(column):
display(df_copy[columns.value].isnull().value_counts())
button1.on_click(describe)
button2.on_click(NAN)
###
box = widgets.HBox([button1 , button2])
widgets.VBox([Titre, columns, box])
Résultat d’exécution:
Jusqu'à présent, tout va bien, mais les résultats de toutes les requêtes s'accumulent dans la même cellule (si nous sélectionnons une nouvelle colonne dans la liste déroulante, une nouvelle trame de données sera affichée sous la première). Ainsi, le comportement souhaité est de rafraîchir le contenu de la trame de données à chaque fois. La solution consiste alors à capturer la sortie de la cellule dans un type spécial de widget, à savoir Output().
Nous allons légèrement modifier le script précédent :
Syntaxe:
List_columns = df_copy.columns.tolist()
Titre = widgets.Label('Nom de la colonne:')
columns = widgets.Dropdown(
value = 'title',
options = List_columns
)
button1 = widgets.Button(
description = 'Describe',
button_style = 'success'
)
button2 = widgets.Button(
description = 'Valeurs manquantes',
button_style = 'success'
)
output = widgets.Output()
def describe(column):
output.clear_output()
with output:
display(df_copy[columns.value].describe())
button1.on_click(describe)
def NAN(column):
output.clear_output()
with output:
display(df_copy[columns.value].isnull().value_counts())
button2.on_click(NAN)
box = widgets.HBox([button1 , button2])
widgets.VBox([Titre, columns, box, output])
Résultat d’exécution:
5. Exercices
5.1. Exercice 1
Question : Créez une interface graphique, en utilisant interact, qui trace la fonction de densité de la loi beta pour différents paramètres de lois et différentes couleurs de courbe (rouge, bleu, noir). Pour plus d’informations sur les distributions de données, regardez le tutoriel : La distribution des données en Python.
5.2. Exercice 2 :
Question : Générez l’interface interactive suivante : (Le bouton produit génère le produit des deux nombres et le bouton somme génère la somme des deux nombres)
6. Solution des exercices
6.1. Exercice 1
Syntaxe:
from scipy.stats import beta
import numpy as np
import matplotlib.pyplot as plt
# a > 0 et b > 0
def pdf_beta(a, b, color):
domain = np.linspace(0, 1)
arg1 = (a, b)
pdf_beta1 = beta.pdf(domain, *arg1, loc = 0, scale = 1)
plt.plot(domain, pdf_beta1, color = color)
plt.show()
interact(pdf_beta, a = (0.1, 10, 0.1), b = (0.1, 10, 0.1), color = ['black', 'red', 'blue'])
Résultat d’exécution:
6.2. Exercice 2
Syntaxe:
# Premier nombre
titre1 = widgets.Label('Nombre 1')
nombre1 = widgets.FloatText()
# Second nombre
titre2 = widgets.Label('Nombre 2')
nombre2 = widgets.FloatText()
# Bouton
button1 = widgets.Button(description = 'Produit', button_style = 'success')
button2 = widgets.Button(description = 'Somme', button_style = 'success')
# Résultat
titre3 = widgets.Label('Résultat:')
resultat1 = widgets.Label()
resultat2 = widgets.Label()
###
def product(produit):
x = nombre1.value
y = nombre2.value
S = x * y
resultat1.value = str(S)
###
def sum(somme):
x = nombre1.value
y = nombre2.value
S = x + y
resultat2.value = str(S)
###
button1.on_click(product)
button2.on_click(sum)
###
vbox1 = widgets.VBox([titre1, nombre1, button1, titre3, resultat1])
vbox2 = widgets.VBox([titre2, nombre2, button2, titre3, resultat2])
###
widgets.HBox([vbox1, vbox2])
Conclusion
Nous sommes arrivés à la fin de ce tutoriel. Ce dernier vous permettra de maîtriser les basiques de la bibliothèque ipywidgets. Elle fournie des fonctionnalités intéressantes et très utiles qui permettent de rendre vos Jupyter notebook très interactif. Merci d’avoir lu et bon courage pour la suite.
Article publié le 30 Novembre 2020 par Sami Nadif