Dans l'activité précédente de Python pour la Physique-Chimie, nous sommes parvenus à réaliser une représentation en nuage de points. Voyons maintenant comment rajouter l'allure d'une fonction linéaire permettant de modéliser cela.
Remarque : si vous travaillez via un site vous permettant d'utiliser mapplotlib, il faudrait rajouter la ligne permettant de sauvegarder les courbes dans un fichier-image :
plt.savefig("essai.png")
Pour l'instant, nous travaillons uniquement sur des données d'expérience stockées dans des listes.
Mais comment faire pour tracer U = R.I par exemple ? Nous n'allons pas devoir taper toutes les valeurs voulues dans une liste ?
Non. Il existe un moyen plus rapide de faire cela. Nous allons utiliser un bouclage qui va nous permettre de réaliser plusieurs fois la même chose.
Voyons comment cela fonctionne.
Imaginons qu'on désire afficher 5 fois le message ATTENTION (pour indiquer que c'est vraiment très important) et attendre que l'utilisateur tape sur ENTREE avant d'arrêter le programme :
print("Attention")
print("Attention")
print("Attention")
print("Attention")
print("Attention")
input("Tapez ENTREE")
01° Tester pour vérifier que le programme fonctionne.
Néanmoins, c'est un peu répétitif : on doit noter 5 fois le même mot. Nous allons faire mieux : vous pouvez mettre le terme voulu en mémoire et demander ensuite à Python d'aller lire le contenu que vous avez mis en mémoire.
On place un contenu en mémoire en utilisant un mot-clé qui permettra d'aller retrouver ce contenu en mémoire. Cela s'appelle une variable.
Pour placer quelque chose en mémoire, on donne le nom de la variable (ici mis en valeur en gras et en bleu), on place un signe =
et on place le contenu voulu de l'autre côté :
monTexte = "Attention"
Pour afficher le contenu de la variable, il suffit de taper le nom de celle-ci :
print(monTexte)
02° Lire le programme ci-dessous sans le lancer. Que va-t-il afficher sur la console ?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
monTexte = "Heureusement que les variables existent"
print(monTexte)
print(monTexte)
input("Tapez ENTREE")
Voici le contenu de la variable en fonction de l'enchainement des instructions :
CLIQUEZ ICI POUR VOIR LE CONTENU DE LA VARIABLE :
monTexte :
03° Modifier le programme pour qu'il affiche 5 fois "Ne passez pas encore à la question suivante" en utilisant une variable.
Et si nous voulions afficher le message 200 fois. Il faudrait taper 200 lignes ? C'est un peu gros.
Et justement, il existe une instruction qui permet de faire cela : la boucle FOR (POUR en français). Cette instruction va permettre de faire les mêmes instructions plusieurs fois.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
monTexte = "Heureusement que la boucle FOR existe"
for compteur in range(5) :
print(compteur)
print(monTexte)
input("Tapez sur ENTREE")
CLIQUEZ ICI POUR VOIR LE CONTENU DE LA VARIABLE :
monTexte :
compteur :
Pour décaler le texte, on fait utiliser la tabulation (touche TAB à gauche de A) ou utiliser 4 espaces. Par contre, utilisez une des méthodes et gardez la pour tout votre code. Ne mélangez pas les deux façons de faire.
04° Utiliser le programme. Combien de fois effectue-t-on la boucle ? Enumérer les valeurs prises par la variable compteur de la boucle FOR. Combien de valeurs y a-t-il ?
Vous devriez obtenir ceci :
0
Heureusement que la boucle FOR existe
1
Heureusement que la boucle FOR existe
2
Heureusement que la boucle FOR existe
3
Heureusement que la boucle FOR existe
4
Heureusement que la boucle FOR existe
Tapez sur ENTREE
...CORRECTION...
Les instructions (compteur) et print(monTexte) sont réalisées 5 fois. La boucle est donc réalisée 5 fois.
La variable compteur prend les valeurs 0, 1, 2, 3 et 4.
La variable compteur prend donc bien 5 valeurs mais comme on commence à 0, on ne va que jusqu'à 4.
Comme nous pouvons le voir, le programme réalise en boucle uniquement les instructions qui sont tabulées par rapport à la ligne du for. Pour tabuler, il faut utiliser soit la touche TAB de votre clavier (souvent à gauche de la touche A), soit placer 4 espaces à la suite.
Si on analyse le code ci-dessous et qu'on le décompose en bloc, on voit que cela ressemble à ceci :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
monTexte = "Heureusement que la boucle FOR existe"
for compteur in range(5) :
print(compteur)
print(monTexte)
input("Tapez sur ENTREE")
Par contre, on voit bien que l'instruction affichant "Tapez sur ENTREE" ne s'effectue qu'une fois : elle ne fait pas partie de la boucle puisqu'elle n'est pas tabulée.
Vous pouvez tenter de la tabuler : vous verrez qu'on doit taper sur ENTREE à chaque fois avant de recommencer la boucle.
05° Créer un programme utilisant une variable contenant un texte et une boucle FOR pour afficher 500 fois le message Un peu de patience !
...CORRECTION...
monTexte = "Un peu de patience !"
for compteur in range(500) :
print(compteur)
print(monTexte)
input("Tapez sur ENTREE")
NOTE : on peut afficher des nombres et du texte dans un même print en utilisant la virgule. Exemple :print(x," et ",y)
.
Vous pouvez tester avec cette version du programme précédent qui affiche le message et le numéro sur la même ligne :
monTexte = "Un peu de patience !"
for compteur in range(500) :
print(compteur, " ", monTexte)
input("Tapez sur ENTREE")
Retournons à notre problème de modélisation de la loi d'Ohm :
06° Créer un programme qui demande à l'utilisateur la valeur d'une résistance. On stockera le résultat dans une variable R. Calculer et afficher les valeurs de U et I pour I variant de 0 à 10A.
NOTE : on peut afficher des nombres et du texte dans un même print en utilisant la virgule. Exemple :print(x," et ",y)
.
...CORRECTION...
R = int(input("Valeur de R en Ohm ? "))
print("U(V)\tI(A)")
for I in range(10) :
U = R*I
print(U,"\t",I)
input("Tapez sur ENTREE")
Nous savons maintenant réaliser des boucles variant de 0 à 19 avec un for I in range(20) :
par exemple.
07° Utiliser le code suivant pour répondre à la question suivante : comment faire varier l'intensité de -10 A à 10 A ?
for I in range(-5,5) :
print(I)
input("Tapez sur ENTREE")
...CORRECTION...
On constate que la boucle avec I in range(-5,5) permet d'obtenir les valeurs -5,-4,-3,-2,-1,0,1,2,3 et 4.
Pour obtenir des valeurs comprises de -10 à +10, il faut donc utiliser cela :
for I in range(-10,11) :
print(I)
input("Tapez sur ENTREE")
08° Utiliser le code suivant pour répondre à la question suivante : comment faire varier l'intensité de -100 A à 100 A de 20 A en 20 A ?
for I in range(-5,5,2) :
print(I)
input("Tapez sur ENTREE")
...CORRECTION...
On constate que la boucle avec I in range(-5,5,2) permet d'obtenir les valeurs -5,-3,-1,1,3.
On part donc de -5 et on augmente donc de 2 à chaque fois. On augmente tant que le résultat est strictement inférieur à 5.
Pour obtenir des valeurs comprises de -100 A à +100 A, il faut donc utiliser cela :
for I in range(-100,101,20) :
print(I)
input("Tapez sur ENTREE")
On peut aussi faire décroitre le compteur I : il suffit de placer en troisième place un argument négatif :
for I in range(100,-101,-50) :
print(I)
input("Tapez sur ENTREE")
On obtient alors ceci :
100
50
0
-50
-100
Tapez sur ENTREE
Voici maintenant la façon la plus courte qui soit de réaliser une liste d'intensité :
I = [ x for x in range(10) ]
print(I)
On voit alors ceci dans la console :
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Nous avons donc réussi à créer une liste dont les éléments sont des nombres entiers.
Nous avions la liste expérimentale suivante pour les intensités :
I = [ 0, 10.2, 19.8, 38.9 ]
09° Utiliser le code précédent pour répondre à la question suivante : comment créer une liste d'intensités variant -300 A à 300 mA de 50 mA en 50 mA ?
...CORRECTION...
I = [ x for x in range(-300,301,50) ]
print(I)
Nous pouvons également réaliser des calculs sur les valeurs produites par le range.
Imaginons qu'on désire créer une liste d'intensités variant de 0 mA à 300 mA par palier de 20 mA. Nous savons faire. Mais comment faire si nous voulons obtenir une liste en A et pas en mA ?
Voici une solution :
I = [ x/1000 for x in range(0,301,20) ]
print(I)
Ou même :
I = [ x*1E-3 for x in range(0,301,20) ]
print(I)
[0.0, 0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.22, 0.24, 0.26, 0.28, 0.3]
10° Compléter le code suivant pour obtenir une liste contenant les tensions théoriques obtenues à partir de la valeur de la résistance stockée dans R et correspondant à la liste des intensités stockées dans I.
R = float(input("Résistance (Ω) ? "))
I = [ x/1000 for x in range(0,301,20) ]
# Ceci est juste un commentaire - il faut modifier la ligne suivante
U = [ 12 for x in range(0,301,20) ]
print(I)
print(U)
Pour l'instant, voici un exemple d'exécution avec le 'mauvais' code actuel :
Résistance (Ω) ? 200
[0.0, 0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.22, 0.24, 0.26, 0.28, 0.3]
[12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12]
Après correction du code, vous devriez obtenir ceci :
Résistance (Ω) ? 200
[0.0, 0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.22, 0.24, 0.26, 0.28, 0.3]
[0.0, 4.0, 8.0, 12.0, 16.0, 20.0, 24.0, 28.0, 32.0, 36.0, 40.0, 44.0, 48.0, 52.0, 56.0, 60.0]
...CORRECTION...
R = float(input("Résistance (Ω) ? "))
I = [ x/1000 for x in range(0,301,20) ]
# Ceci est juste un commentaire - il faut modifier la ligne suivante
U = [ R*x/1000 for x in range(0,301,20) ]
print(I)
print(U)
Pour tracer un nuage de points, nous avons utilisé la fonction scatter. Pour tracer des segments reliant les points dont on fournit les coordonnées, il faut utiliser la fonction plot.
11° Utiliser le code suivant pour voir comment cela fonctionne. Il permet d'obtenir ceci :
import matplotlib.pyplot as plt
x = [ valeur for valeur in range(-3,4) ]
y = [ 10,12,8,3,-8,10,12 ]
plt.plot(x, y)
plt.grid()
plt.savefig("essai.png")
plt.show()
12° Tracer la droite représentative de U = RI en utilisant les listes suivantes :
R = float(input("Résistance (Ω) ? "))
I = [ x/1000 for x in range(0,51,50) ]
U = [ R*x/1000 for x in range(0,51,50) ]
Pour une résistance de 100 Ω, on obtient ceci :
...CORRECTION...
import matplotlib.pyplot as plt
R = float(input("Résistance (Ω) ? "))
I = [ x/1000 for x in range(0,51,50) ]
U = [ R*x/1000 for x in range(0,51,50) ]
plt.plot(I, U)
plt.grid()
plt.savefig("essai.png")
plt.show()
Il nous reste à tracer cela dans notre graphique. Pour cela, nous avions déjà réussi à tracer un nuage de points. Voici le code pour rappel (voir l'activité précédente si vous voulez une description ligne par ligne).
import matplotlib.pyplot as plt
I = [ 0, 10.2, 19.8, 38.9 ]
U = [ 0, 1.0, 2.0, 3.9 ]
plt.scatter(x=I, y=U)
plt.grid()
plt.savefig("loiOhm.png")
plt.show()
13° Tracer (sur le même graphique), le nuage de points expérimentaux et la droite modélisant la loi d'Ohm. Nous allons retrouver ce qui pose souvent problème aux élèves : la bonne concordance des unités.
...CORRECTION...
import matplotlib.pyplot as plt
# Création du nuage de points expérimentaux - Ie en mA - Ue en V
Ie = [ 0, 10.2, 19.8, 38.9 ]
Ue = [ 0, 1.0, 2.0, 3.9 ]
plt.scatter(x=Ie, y=Ue)
# Création de la droite théorique - R en ω - It en mA - Ut en V
R = float(input("Résistance (Ω) ? "))
It = [ x for x in range(0,51,50) ]
Ut = [ R*x/1000 for x in range(0,51,50) ]
plt.plot(It, Ut)
# Création de la grille
plt.grid()
# Création du fichier-image pour la sauvegarde éventuelle
plt.savefig("loiOhm.png")
# Affichage de l'interface
plt.show()
Choix des noms dans la correction :
J'ai choisi d'utiliser l'indice e pour les résultats expérimentaux et t pour théoriques.
Le code fonctionne encore en les enlevant partout. Mais dans ce cas, la deuxième version de U et I va écraser la première. Il ne restera donc en mémoire que les valeurs théoriques.
14° La résistance étant connue à +/- 5%, tracer les deux droites théoriques :
...CORRECTION...
import matplotlib.pyplot as plt
# Création du nuage de points expérimentaux - Ie en mA - Ue en V
Ie = [ 0, 10.2, 19.8, 38.9 ]
Ue = [ 0, 1.0, 2.0, 3.9 ]
plt.scatter(x=Ie, y=Ue, label="Mesures expérimentales")
# Création des 2 droites théoriques - R en ω - It en mA - Ut en V
R = float(input("Résistance (Ω) ? "))
It1 = [ x for x in range(0,51,50) ]
Ut1 = [ 0.95*R*x/1000 for x in range(0,51,50) ]
plt.plot(It1, Ut1, label="Courbe théorique avec Rmin")
Ut2 = [ 1.05*R*x/1000 for x in range(0,51,50) ]
plt.plot(It1, Ut2, label="Courbe théorique avec Rmax")
# Réglages axes et grille
plt.xlabel("Intensité I(mA)")
plt.ylabel("Tension U(V)")
plt.title("Caractéristique électrique U=f(I) d'un résistor")
plt.legend(loc='lower right')
plt.axis([0,40,0,4])
plt.grid()
# Création du fichier-image pour la sauvegarde éventuelle
plt.savefig("loiOhm.png")
# Affichage de l'interface
plt.show()
Nous avons bien avancé. Nous avons vu :
Comment accéder au contenu d'une liste ? Encore une fois, nous allons devoir utiliser une boucle for.
La solution la plus directe consiste à lire le contenu de la liste en faisant varier l'index :
Ie = [ 0, 10.2, 19.8, 38.9 ]
for index in range(4) :
print(Ie[index])
0
10.2
19.8
38.9
Et voilà. Par contre, il existe une source d'erreur énorme : on pourrait dépasser l'index maximum ou, au contraire, ne pas lire toute la liste.
Pour connaitre la longueur d'une liste, il existe une fonction native len. Ici len(Ie)
renvoie donc 4 . Les index disponibles sont donc 0-1-2-3.
15° Modifier le code précédent pour qu'il utilise la fonction len et soit donc plus solide.
...CORRECTION...
Ie = [ 0, 10.2, 19.8, 38.9 ]
for index in range(len(Ie)) :
print(Ie[index])
La méthode précédente est efficace mais un peu lourde à écrire. Python peut faire mieux. On peut lire les éléments contenus dans une liste en tapant un simple code du type :
Ie = [ 0, 10.2, 19.8, 38.9 ]
for element in Ie :
print(element)
Cette fois, on n'utilise plus l'index : on obtient directement la valeur suivante :
0
10.2
19.8
38.9
Avantage : c'est plus court à écrire.
Désavantage : on ne connait pas l'index de l'élément.
En fonction de ce qu'on veut faire, on prendra donc l'un ou l'autre.
16° Utiliser un for nominatif pour lire le contenu de la listeDesCourses.
listeDesCourses = [ "Carottes", "Lait", "Chips" ]
...CORRECTION...
listeDesCourses = [ "Carottes", "Lait", "Chips" ]
for element in listeDesCourses :
print(element)
Pour l'instant, nous n'avons fait que tracer nous même les droites représentatives. Mais nous pouvons faire comme sous n'importe quel logiciel de gestion de données : nous pouvons demander à Python de tenter de tracer la 'droite moyenne'.
Il existe plusieurs bibliothèques permettant de faire cela. Nous utiliserons ici la bibliothèque scipy qui contient un module nommé stats. Comme il s'agit de tracer une droite, nous pourrions assez facilement nous débrouiller pour le faire à la main mais quelqu'un a déjà fait le travail. Autant l'utiliser.
Pour importer ce module, nous allons devoir taper ceci en début de code :
from scipy import stats
Simple ? Oui. Sauf que la bibliothèque scipy n'est normalement pas installée sur votre ordinateur. Nous allons tenté de l'installer. La réussite va dépendre de la configuration et la sécurisation de la session. Si vous devez utiliser une bilbiothèque avec vos élèves, le mieux est de l'installer préalablement.
17° Ouvrir la console/shell/bash/invite de commandes de votre système d'exploitation. Taper l'instruction suivante pour voir si Python est correctement installé et si son dossier a été ajouté au Path, la variable d'environnement qui contient les répertoires à surveiller pour démarrer un programme.
> python
>>> exit()
Si ça n'a pas marché, ça commence mal. Cela veut dire que les répertoires de Python n'ont pas été rajoutés à Path. Pour télécharger les modules, il faudra y aller à la main. On sort du cadre de cette introduction. Mais c'est faisable.
Supposons que ca fonctionne. Il ne reste plus qu'à télécharger la bibliothèque, en espérant que le réseau laisse tout passer et que les droits de modification sur le PC soit suffisant :
18° Utiliser la commande suivante pour télécharger et installer la bibliothèque scipy sur votre poste.
> pip install scipy
Et voilà. C'est fini après quelques secondes de téléchargement. Simple non ?
Voyons comment parvenir à faire cette régression linéaire avec Python.
Après avoir fait une recherche sur Internet, un élève vous propose le programme suivant :
import matplotlib.pyplot as plt
from scipy import stats
# Création du nuage de points expérimentaux - Ie en mA - Ue en V
Ie = [ 0, 10.2, 19.8, 38.9 ]
Ue = [ 0, 1.0, 2.0, 3.9 ]
plt.scatter(x=Ie, y=Ue, label="Mesures expérimentales")
# On récupère le coefficient directeur a
# On récupère l'ordonnée à l'origine b
a, b, r_value, p_value, std_err = stats.linregress(Ie, Ue)
print("Le coefficient directeur a = ", a)
print("L'ordonnée à l'origine b = ", b)
# Réglages axes et grille
plt.xlabel("Intensité I(mA)")
plt.ylabel("Tension U(V)")
plt.title("Caractéristique électrique U=f(I) d'un résistor")
plt.legend(loc='lower right')
plt.axis([0,40,0,4])
plt.grid()
# Affichage de l'interface
plt.show()
19° Utiliser le programme pour répondre aux questions suivantes :
...CORRECTION...
On utilise la fonction linregress du module stats.
On doit lui fournir la liste des coordonnées X des points et la liste des coordonnées Y des points.
Le coefficient directeur est renvoyée par la fonction et on le stocke dans a.
Les unités de l'intensité n'étant pas les bonnes, on ne peut pas obtenir notre 100...
Pour connaitre tout cela, il faut simplement aller fouiller dans la documentation : scipy
Réglons cette histoire d'unité. Nous avons vu comment créer une liste à partir d'un range. Voyons maintenant comme faire avec un for nominatif :
import matplotlib.pyplot as plt
from scipy import stats
# Création du nuage de points expérimentaux - Ie en mA - Ue en V
Ie = [ 0, 10.2, 19.8, 38.9 ]
IeA = [ x/1000 for x in Ie]
Ue = [ 0, 1.0, 2.0, 3.9 ]
plt.scatter(x=Ie, y=Ue, label="Mesures expérimentales")
# On récupère le coefficient directeur a
# On récupère l'ordonnée à l'origine b
a, b, r_value, p_value, std_err = stats.linregress(IeA, Ue)
print("Le coefficient directeur a = ", a)
print("L'ordonnée à l'origine b = ", b)
# Réglages axes et grille
plt.xlabel("Intensité I(mA)")
plt.ylabel("Tension U(V)")
plt.title("Caractéristique électrique U=f(I) d'un résistor")
plt.legend(loc='lower right')
plt.axis([0,40,0,4])
plt.grid()
# Affichage de l'interface
plt.show()
Que fait cette ligne ?
IeA = [ x/1000 for x in Ie]
On prend chaque élément de Ie qu'on divise par 1000 pour créer une nouvelle liste qu'on nomme IeA.
Cette fois, les valeurs de a et b sont :
100.496968039
-0.00606027447226
20° Rajouter quelques lignes au programme de façon à tracer en plus la droite théorique obtenue en utilisant les valeurs de a et b.
Il faudra créer une liste d'ordonnée nommée par exemple y et lui appliquer un code du type (le code de l'exemple n'est pas le bon bien entendu) :
y = [ 10*x+3 for x in IeA]
...CORRECTION...
import matplotlib.pyplot as plt
from scipy import stats
# Création du nuage de points expérimentaux - Ie en mA - Ue en V
Ie = [ 0, 10.2, 19.8, 38.9 ]
IeA = [ x/1000 for x in Ie]
Ue = [ 0, 1.0, 2.0, 3.9 ]
plt.scatter(x=Ie, y=Ue, label="Mesures expérimentales")
# On récupère le coefficient directeur a
# On récupère l'ordonnée à l'origine b
a, b, r_value, p_value, std_err = stats.linregress(IeA, Ue)
print("Le coefficient directeur a = ", a)
print("L'ordonnée à l'origine b = ", b)
# RAJOUT QUESTION 20
y = [(a*x+b) for x in IeA]
plt.plot(Ie, y, label="droite moyenne")
# Réglages axes et grille
plt.xlabel("Intensité I(mA)")
plt.ylabel("Tension U(V)")
plt.title("Caractéristique électrique U=f(I) d'un résistor")
plt.legend(loc='lower right')
plt.axis([0,40,0,4])
plt.grid()
# Affichage de l'interface
plt.show()
Nous verrons dans l'activité suivante comment répondre différemment en fonction de conditions précises. Il s'agit des conditions exprimées avec un IF (SI en français). Nous utiliserons cela pour traiter la partie sur le tableau d'avancement.
Question : comment sortir d'une boucle for avant la fin ?
On utilise l'instruction break. Exemple : on lance une boucle avec un compteur évoluant de 0 à 20 mais on sort définitivement de la boucle lorsque le compteur vaut 5 :
for compteur in range(21) :
print(compteur)
if (compteur == 5) :
break
input("pause")
CLIQUEZ SUR UN BOUTON-REPONSE :
compteur :
Sur la console, on voit bien qu'on ne revient pas dans la boucle pour le cas 6 et plus :
0
1
2
3
4
5
Remarque : même s'il reste des instructions sous le break, on ne les exécute pas : on quitte immédiatement la boucle.
Question : Je voudrais compter de 0 à 6 mais sans passer par 5, c'est possible ?
On utilise l'instruction continue. On lance une boucle avec un compteur évoluant de 0 à 6 mais sans agir pour 5 en forçant un retour au début de la boucle :
for compteur in range(7) :
if (compteur == 5) :
continue
print(compteur)
input("pause")
CLIQUEZ SUR UN BOUTON-REPONSE :
compteur :
Comme vous le voyez, je n'ai même pas besoin de mettre un else après le if. L'exécution du programme va répartir au début de la boucle dès qu'elle rencontrera continue.
Sur la console, on voit bien qu'on a sauté l'exécution du print pour le 5 :0
1
2
3
4
6
Remarque : même s'il reste des instructions sous le continue, on ne les exécute pas : on remonte immédiatement la boucle pour faire l'itération suivante.