Les nouvelles technologies pour l’enseignement des mathématiques
Intégration des TICE dans l’enseignement des mathématiques

MathémaTICE, première revue en ligne destinée à promouvoir les TICE à travers l’enseignement des mathématiques.

Python et les graphes de fonctions
Article mis en ligne le 7 février 2018
dernière modification le 3 avril 2021

par François Goichot

Cet article peut être librement diffusé et son contenu réutilisé suivant la licence CC-by-sa

Pour écraser une mouche, le marteau-pilon n’est pas l’outil le plus ergonomique. Pour tracer un graphe de fonction, on ne pense pas forcément à Python. Mais la circulaire aménageant le programme de mathématiques de seconde pour la rentrée 2017, qui préconise l’utilisation d’un « langage de programmation textuel », précise que « même si les logiciels traceurs de courbes permettent d’obtenir rapidement la représentation graphique d’une fonction définie par une formule algébrique, il est intéressant, notamment pour les fonctions définies par morceaux, de faire écrire aux élèves un algorithme de tracé de courbe. » L’idée n’a toutefois pas été reprise dans le document ressource publié en juin dernier. Nous proposons ici d’explorer différentes façons d’utiliser le langage Python pour écrire un tel algorithme, approchant la courbe par des segments de droites.

Voir aussi :

Avec matplotlib

La façon la plus naturelle, pour un utilisateur expérimenté de Python, de tracer un graphe de fonction, c’est d’utiliser la « bibliothèque » ad hoc, matplotlib - en fait son module pyplot suffira largement. Commençons donc par présenter cette méthode.

matplotlib ne fait pas partie de Python standard. Selon l’environnement utilisé (ÉduPython, Pyzo, Thonny, etc) vous serez donc peut-être amené à le télécharger.
Dans la suite de cette partie, nous supposerons que cela a été fait. Il est alors facile d’obtenir un graphe :

  1. import matplotlib.pyplot as plt  
  2.  
  3. def g(x):
  4.     '''la fonction qu'on veut représenter'''
  5.     return(2*x*x-3*x+1)
  6.    
  7. def graphe(f,a,b,N):
  8.     '''trace le graphe de la fonction f entre a et b avec N segments'''
  9.     lx = [a+i*(b-a)/N for i in range(N+1)]
  10.     ly = [f(x) for x in lx]
  11.     plt.plot(lx,ly)
  12.     plt.show()  # affichage
  13.    
  14. # programme principal
  15. graphe(g,-2,3,6)

Télécharger

Pour le lecteur peu familier de Python, quelques commentaires :

  • comme tout module Python, matplotlib.pyplot doit être importé pour être utilisé dans un programme ; c’est ce que fait la première ligne, en adoptant plt comme « alias » (synonyme abrégé).
  • on crée ensuite la fonction (au sens de Python) correspondant à la fonction (mathématique) que l’on veut représenter.
  • la ligne 9 crée la liste des abscisses des N+1 points, régulièrement répartis entre a et b. L’instruction range(N+1) crée la liste des entiers de 0 à N.
  • la ligne 10 crée la liste des images par f des points précédents.
  • la ligne 11 crée le dessin, en reliant les points dont les abscisses sont dans la liste lx et les ordonnées dans la liste ly.
  • plt.show() lance l’affichage.
    Enfin, l’unique ligne du programme principal lance l’exécution de la fonction graphe, avec en premier paramètre la fonction $g$ que l’on veut représenter.

L’« importation » expliquée aux débutants

Notre éventuel lecteur novice en Python s’étonnera sans doute de voir différentes façons d’importer des modules : nous venons d’utiliser import matplotlib.pyplot as plt alors que plus loin ce sera from dessin2d import *. En fait, une troisième version serait aussi possible : import matplotlib.pyplot mais avec celle-ci, dans le programme précédent, au lieu de plt.plot(lx,ly) nous aurions dû écrire matplotlib.pyplot.plot(lx,ly), et matplotlib.pyplot.show() au lieu de plt.show(). On s’en lasse vite, c’est pourquoi on introduit l’« alias » plt.

Mais, entre les deux premières versions, quelles différences ? La première est dans l’usage qu’on en fera : avec from matplotlib.pyplot import * , on pourrait utiliser chaque fonction du module avec son nom seul, par exemple plot(lx,ly). Alors qu’avec import matplotlib.pyplot as plt on est obligé de les « préfixer » avec plt. : donc plt.plot(lx,ly) dans notre exemple. Cela peut paraître fastidieux, mais c’est le seul moyen d’éviter les problèmes d’homonymie : des fonctions portant le même nom dans des modules distincts. Par exemple, les modules math et numpy proposent tous deux une fonction log. Si on a importé ces deux modules avec la syntaxe from ... import * et qu’on tape x = log(u), laquelle des deux fonctions log sera-t-elle utilisée ? Tant que les deux coïncident, ce n’est pas gênant. Mais ce n’est pas toujours le cas. Pour un module qu’on ne connaît pas bien, utiliser la syntaxe import ... as ... ou import ... est plus prudent.

Une autre différence est moins visible, sauf dans un environnement comme Thonny, qui permet à l’utilisateur de voir toutes les fonctions importées : la syntaxe from ... import * a l’inconvénient d’importer toutes les fonctions du module, ce qui, avec un « gros » module, peut finir par être encombrant. Le module math ne contient [1] que 53 fonctions, mais le sous-module pyplot de matplotlib, à lui seul, en contient 977 !

Avec des élèves de lycée, il est certainement prématuré d’évoquer les explications qui précèdent. Pour justifier l’utilisation de cette syntaxe import matplotlib.pyplot as plt pour l’importation du module pyplot de la bibliothèque matplotlib, , on peut leur dire plus simplement : faisons comme tout le monde. Car cette syntaxe est très fréquemment utilisée, dans la vaste documentation Python, pour les raisons expliquées ci-dessus.

Pour être complet sur cette question, signalons une dernière façon d’importer, non pas un module cette fois, mais une seule fonction d’un module : si par exemple on veut utiliser la fonction sqrt (racine carrée) du module math et seulement celle-là, il suffit de taper from math import sqrt, et on peut alors l’utiliser, sous la forme simple sqrt().

Revenons à notre problème initial.

On obtient le graphe cherché, auquel matplotlib a ajouté des axes gradués mais non centrés :

Si on les préfère centrés à l’origine, on peut les ajouter, en couleur noire, avec les commandes plt.axhline(color = 'k') ;  plt.axvline(color='k'). De même pour diverses décorations : des étiquettes sur les axes latéraux avec [2]plt.xlabel('$x$') ; plt.ylabel('$f(x)$'), et un titre avec plt.title("Tracé approché d'un graphe"). Le résultat est bien propre :

Le programme correspondant graphe_avec_matplotlib.py est ici

Programme grapheur
Graphe avec le module python Matplotlib.py

Mais c’est assez loin de l’algorithmique telle qu’on peut l’imaginer en seconde : on n’a utilisé aucune des structures élémentaires (boucle, condition, etc). Et on a besoin des listes, dont l’introduction en seconde peut sembler prématurée.

Nous allons voir une première façon d’y remédier, sans changer le résultat - et sans que l’élève ait besoin de manipuler des listes. L’idée est de le faire travailler, non pas avec matplotlib directement, mais avec un module (au sens de Python toujours : un ensemble de fonctions prédéfinies) que nous appellerons dessin2d : créé par le professeur et mis à disposition de l’élève. Attention, comme il ne s’agit pas d’un module de Python standard, il faudra que le fichier contenant dessin2d soit dans le dossier de travail de l’élève (celui où il enregistre ses propres programmes), pour que Python le trouve sans difficulté. L’élève pourra alors l’utiliser avec la syntaxe standard : from dessin2d import *.

Voici ce que nous proposons comme contenu pour ce fichier - mais bien sûr chacun pourra l’adapter à son usage :

  1. import matplotlib.pyplot as plt  
  2.  
  3. def point(x,y):
  4.     '''crée le point de coordonnées (x,y)'''
  5.     plt.plot(x,y,'o')
  6.    
  7. def segment(x0,y0,x1,y1):
  8.     '''crée le segment reliant (x0,y0) à (x1,y1)'''
  9.     lx, ly = [x0,x1], [y0,y1]
  10.     plt.plot(lx,ly,'b')
  11.    
  12. def affiche():
  13.     '''affiche le dessin'''
  14.     plt.show()

Télécharger

Les seuls outils ainsi mis à disposition de l’élève sont le tracé d’un point et d’un segment. On lui cache le fait que Python adapte automatiquement le repère aux objets géométriques qu’il doit représenter. Pour que l’élève s’approprie ce petit outil, on pourra lui fournir le programme suivant :

  1. from dessin2d import *
  2.  
  3. segment(0,0,0,2)
  4. segment(0,2,1,3)
  5. segment(1,3,2,2)
  6. segment(0,2,2,2)
  7. segment(2,2,2,0)
  8. segment(0,0,2,0)
  9. point(1,2.45)
  10.  
  11. affiche()

Télécharger

et lui demander d’ajouter une porte à la maison, par exemple. On devrait alors pouvoir l’amener à représenter, avec ce même outil, un graphe de fonction en l’approchant par des segments. Chaque professeur saura mieux que nous l’adapter à ses élèves. Nous nous contenterons de montrer ce qui pourrait être la production d’un élève :

  1. from dessin2d import *
  2.  
  3. def g(x):
  4.     '''la fonction qu'on veut représenter'''
  5.     return(2*x*x-3*x+1)
  6.    
  7. def graphe(f,a,b,n):
  8.     '''représente la fonction f entre a et b avec n points'''
  9.     h = (b-a)/n # longueur de chaque segment
  10.     x = a
  11.     for i in range(n):
  12.         segment(x,f(x),x+h,f(x+h))
  13.         x = x+h
  14.     affiche()
  15.    
  16. graphe(g,-2,3,6)

Télécharger

qui redonne le premier dessin ci-dessus.

Si l’on veut permettre à l’élève d’obtenir un graphe plus conforme aux usages (axes centrés, légende, etc), il suffit d’enrichir dessin2d avec des traductions des commandes Python décrites au début de ce texte. Mais ce ne serait plus vraiment une question d’algorithmique.

Avec la tortue

Voyons une autre solution, avec un autre outil de dessin proposé par Python, beaucoup plus rudimentaire : la « tortue » du module turtle. Ce module est en principe présent dans toute installation standard de Python - sans téléchargement supplémentaire donc. Il contient les commandes permettant de piloter une tortue virtuelle ; si le « stylo » est baissé, la tortue laissera une trace de son passage ; c’est ainsi que l’on dessine. Comme avec les « lutins » de Scratch donc, en plus rustique et... en anglais.

Comment se repère la tortue ? Par défaut, l’unité de longueur est le pixel, l’unité d’angle est le degré. C’est l’utilisateur qui doit adapter ce repère à son usage. Dans le cas qui nous occupe, cela posera une vraie difficulté pour la plupart des élèves : l’intervalle $[a,b]$ étant donné, il faut déjà appliquer correctement la proportionnalité pour lui faire correspondre la dimension horizontale de la fenêtre de la tortue. Mais pour la dimension verticale, il faut d’abord savoir quel sera l’intervalle à représenter, donc déterminer le minimum et le maximum de la fonction à représenter sur $[a,b]$.
Pour des élèves déjà habitués à manipuler la tortue, cela peut être le thème d’une activité riche ; mais il vaudra mieux fixer la fonction à représenter et l’intervalle. Même ainsi, cela demandera du temps.

Nous proposons une autre solution, sur la base de la commande setworldcoordinates de la tortue : elle permet de définir un repère choisi par l’utilisateur. L’aide du module signale que cette commande a le défaut de déformer les angles ; mais cela n’aura pas d’inconvénient pour ce que nous voulons faire.

Pour faciliter la tâche de l’élève - et même du professeur - nous travaillerons avec le module tortue, à télécharger ici

Module tortue.py
Ce module propose une traduction en français des principales fonctions du module turtle.py

Ce n’est qu’une version francisée des principales commandes du module turtle, le fichier d’aide est disponible ici

Fichier d’aide sur le module tortue.py

Nous l’utiliserons dans la suite. Mais on peut bien sûr faire tout ce qui suit avec le module originel turtle, et ainsi faire travailler l’anglais aux élèves.

Comme pour le module dessin2d utilisé sous l’onglet « Avec matplotlib », pour que l’élève puisse utiliser tortue, il faut l’avoir déposé dans son dossier de travail ; il l’importe alors, au début de son propre programme, avec from tortue import * .

À ce stade, il vaut mieux commencer par se familiariser avec les commandes de la tortue, avec un petit programme réalisant une figure classique, par exemple un « escargot de Pythagore. »

Cela fait, pour amener les élèves à écrire un algorithme permettant de représenter par approximation le graphe d’une fonction $f$ donnée et sur l’intervalle $[a,b]$ donné, pour un pas $n$ donné on peut suggérer le scénario suivant :

  • on fixe par exemple $f(x) = 2x^2-3x+1, a=-2, b=3$ et $n=10$ et on impose la commande change_repere(-5,-5,5,5). Et on demande aux élèves d’écrire (avec tortue) le programme dessinant les axes, et les segments approchant le graphe ; Cela devrait donner un programme semblable à celui-ci :
  1. from tortue import *
  2.  
  3. def g(x):
  4.     '''la fonction qu'on veut représenter'''
  5.     return(2*x*x-3*x+1)
  6.  
  7. def dessin(f):
  8.     '''dessine l'approximation du graphe de f  
  9.    obtenue en reliant les points (xi,f(xi))'''
  10.     change_repere(-5,-5,5,5)
  11.     a, b = -2, 3
  12.     maz() ; vitesse(1)
  13.     montre()
  14.     # dessin des axes
  15.     couleur('n')
  16.     leve() ; va_en(-5,0) ; baisse() ; va_en(5,0)          
  17.     leve() ; va_en(0,-5) ; baisse() ; va_en(0,5)
  18.     # dessin du graphe
  19.     couleur('b')
  20.     leve() ; va_en(a,f(a)) ; baisse()
  21.     for i in range(1,10):
  22.         xi = a + i * (b-a)/9
  23.         va_en(xi,f(xi))
  24.     cache()
  25.    
  26. dessin(g)

Télécharger

  • les élèves vont s’apercevoir que les paramètres de change_repere sont inadaptés, pour les ordonnées. Ils peuvent trouver des valeurs correctes par essais successifs. On peut espérer que certains pensent à chercher le maximum de la fonction sur l’intervalle donné.
  • on peut alors leur poser la question : comment modifier le programme pour qu’il fonctionne avec n’importe quel intervalle (sous entendu, borné, bien sûr) ? avec n’importe quelle fonction (sous-entendu, définie sur l’intervalle) ? Ils doivent alors se rendre compte qu’il faut d’abord déterminer les valeurs extrêmes prises par la fonction. Il leur faudra sans doute de l’aide pour faire la fin :
  • construire avec Python le tableau de valeurs de $f$. Cela suppose que l’élève sache un peu manier les listes avec Python. Il faudra en fait construire d’abord la liste des $n$ nombres $x_i$, régulièrement répartis entre $a$ et $b$. Puis la liste de leurs images, en relevant au passage la plus grande et la plus petite.
  • modifier leur programme précédent pour utiliser ces nouvelles données.

On trouvera ici

graphe_avec_tortue2.py

le programme souhaité, avec quelques compléments esthétiques.

Un perfectionnement intéressant serait de faire varier le pas d’approximation, de façon à améliorer la précision du graphe sur les intervalles où la fonction varie plus vite. Pour une fonction donnée sur un intervalle donné, on peut le proposer aux élèves les plus avancés. Le cas général (une fonction quelconque sur un intervalle borné quelconque) supposerait que le programme commence par détecter ces intervalles de forte variation. C’est nettement plus ambitieux.

N.B. N’ayant pas d’élèves de lycée sous la main, je n’ai pas pu tester les idées exposées ici. J’accueillerai avec intérêt les commentaires, en particulier après essai de mise en œuvre même partielle : fgoichot@univ-valenciennes.fr

Je remercie Gérard Kuntz, Alain Busser, Aymeric Picaud et les autres membres du comité de rédaction, pour leur lecture attentive et leurs suggestions, qui ont permis d’améliorer cet article.

F. G.