4. TP manipulation de liste et de chaîne#
Marc BUFFAT, dpt mécanique, Université Lyon 1
Attention il faut exécuter la cellule vide suivante !!
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from validation.validation import info_etudiant
# test si numero étudiant spécifier
try: NUMERO_ETUDIANT
except NameError: NUMERO_ETUDIANT = None
if type(NUMERO_ETUDIANT) is not int :
NOM, PRENOM, NUMERO_ETUDIANT = info_etudiant()
from validation.validation import check_function,liste_functions,info_etudiant
from validation.valide_markdown import test_markdown, test_code
from IPython.display import display, Markdown, clear_output
def printmd(string):
display(Markdown(string))
# parametres spécifiques
_uid_ = NUMERO_ETUDIANT
np.random.seed(_uid_)
printmd("## Etudiant {} {} id={}".format(NOM,PRENOM,NUMERO_ETUDIANT))
# parametres
_a_ = sum([int(c) for c in str(_uid_)])
_ID=['MGC1020M','MGC1049M','MGC1021M','MGC1054M','MGC1023M','MGC1024M','MGC1056M',
'MGC1029M','MGC1061M','MGCLG1AM','MGC1017M','MGC1058M']
_I1 = np.random.randint(len(_ID))
_I2 = _I1
while _I2 == _I1 : _I2 = np.random.randint(len(_ID))
_I3 = _I1
while (_I3 == _I1) or (_I3 == _I2): _I3 = np.random.randint(len(_ID))
print("codes des UE à étudier: {} {} {}".format(_ID[_I1],_ID[_I2],_ID[_I3]))
try:
printmd("INITIALISATION OK!")
except:
print("Erreur vous n'avez pas executée la cellule vide précédente !")
print("Votre Notebook n'est pas initialisé correctement !")
4.1. Rappels de cours sur les listes#
création, indexation de liste
test appartenance à une liste (in)
test conditionnel (if)
itération sur les élements (boucle for)
on considère la liste suivante:
malist = [1, 2, 3, '4', [5, 'six'], [7]]
dans la cellule suivante affichée le second et l’avant dernier elément de la liste, ainsi que leur type en utilisant la fonction type
en utilisant la methode append
ajouter l’élément 8 à malist
malist = [1, 2, 3, '4', [5, 'six'], [7]]
### BEGIN SOLUTION
### END SOLUTION
4.1.1. test d’appartenance#
pour tester l’appartenance à une liste, on utilise l’opérateur in
en utilisant l’instruction suivante
elt in malist
test si elt
est un élément de la liste malist
.
dans la cellule suivante, tester si 3 , 4 et 5 sont des éléments de la liste en utilisant l’opérateur in
et commenter le résultat.
A quel élément appartient 5 ?
# test si 3,4,5 sont dans malist
### BEGIN SOLUTION
### END SOLUTION
4.2. test logique#
ré-écrire les tests précédents en affichant un message si l’élément elt
est dans la liste malist
en utilisant le test logique if
if condition :
instruction si vrai (True)
else:
instruction sinon (False)
elt = 4
elt = 3
### BEGIN SOLUTION
### END SOLUTION
4.2.1. itération for sur les éléments d’un liste#
En Python, l’instruction for permet d’itérer sur les éléments d’une séquence (ou liste). Pour passer en revu tous les éléments de malist on écrit:
for elt in malist:
faire quelque chose avec la variable elt
Ici, pour la première fois, nous rencontrerons une particularité du langage Python: l’indentation. Pour délimiter ce que Python doit faire avec chaque fruit dans la liste des fruits, nous plaçons la ou les déclarations suivantes en retrait à partir de la gauche.
Commment indenter? C’est une question de style, et chacun a une préférence: deux espaces, quatre espaces, une tabulation… sont tous des styles valables: mais choisissez un style et soyez cohérent!
un conseil utiliser plutôt des espaces qu’une tabulation. Nous choisirons dans la suite une indentation de 4 espaces.
Passer en revu tous les éléments de malist et afficher leur type
# afficher le type des elts de malist
### BEGIN SOLUTION
### END SOLUTION
4.2.2. itération avec un compteur#
on peut aussi faire des itérations avec un compteur en accédant ensuite aux éléments en utilisant l’indexation
N = len(malist)
for i in range(N):
elt = malist[i]
faire quelque chose avec la variable elt
ré-écrire la boucle précédente en affichant en plus l’indice de l’élément
# afficher le type des elts de malist
### BEGIN SOLUTION
### END SOLUTION
4.3. Rappel sur les fonctions#
fonction: suite d’instructions à partir de données (argument) renvoie un résultat (valeur de retour)
4.3.1. définition d’une fonction#
def MaFonction(donnee1,donnee2,...)
'''documentation'''
...
resultat = calculer a partir des donnees
...
return resultat
Attention: dans une fonction les arguments (donnee1,donnee2,..) et les variables dans la fonction sont des variables locales.
4.3.2. utilisation d’une fonction#
mon_res = MaFonction(ma_donnee1, ma_donnee2,)
Lors de l’appel d’une fonction, les arguments (ma_donnee1,ma_donnee2,..) peuvent être des expressions, des valeurs ou des variables (si elles sont initialisées) dont le nom n’a pas être identique à celui de la définition. Par contre l’ordre et le type doivent être respectés.
4.4. Exercise sur les fonctions#
On vous donne tout d’abord la définition de la fonction à écrire avec ces paramêtres et le résultat attendu, et on vous demande d’écrire la fonction python correspondante, de la vérifier et enfin de passer le test de validation.
S=['exo010','S010','S010','S010']
Exos=liste_functions(S,_a_)
printmd("## Exercices a faire")
print("nbre d'exercices = ",len(Exos))
Note_exos = [0]*len(Exos)
print(Exos)
4.4.1. premier exercice#
printmd("### Exercice test: écrire une fonction func t.q.")
nom = Exos[0]
!test_exo -e $nom
4.4.1.1. Analyse#
comprendre la question
pour la chaine « bonjour » renvoie « bour »
pour la chaine « w » renvoie « «
Algorithme func(chaine) ''' renvoie une chaîne contenant les 2 premiers et 2 derniers caractères, ou une chaine vide si la longueur est < a 2 ''' si longueur(chaine) < 2 alors retour "" sinon retour chaine[0]+chaine[1]+chaine[-2]+chaine[-1]
# fonction a écrire
def func(chaine):
''' renvoie une chaîne contenant les 2 premiers et 2 derniers caractères,
ou une chaine vide si la longueur est < a 2 '''
4.4.1.2. Vérification#
on choisit des valeurs ou on connait la solution pour tester
printmd("## Verification: appel de la fonction")
printmd("**spécifier les arguments de la fonction func pour la tester t.q.**")
!test_exo -e $nom
# specifier les valeurs des arguments pour tester
### BEGIN SOLUTION
### END SOLUTION
4.4.1.3. Validation#
une fois la fonction testée on peut passer à l’étape de validation, qui sert aussi à noter le travail
assert(check_function(func,Exos[0]))
Note_exos[0] = 1
print("Tests de validation OK! note =",sum(Note_exos)," sur",len(Note_exos))
4.4.2. Exercises#
les exercises suivants de 1 a 4 sont optionnels et donnés en fin de notebook (partie optionnelle)
4.5. Objectif du TP: manipulation de données textuelles#
Nous allons traiter des données textuelles contenues dans un fichier tiré de l’offre de formation de l’université Lyon 1, et qui contient les cours de la première année de master mécanique MAM. Nous créerons différentes listes pour nous permettre de localiser le titre, les crédits et la description d’un cours en fonction du code du cours.
Le fichier de données de cet exemple se trouve dans un sous-répertoire data
du répertoire de travail et se nomme master_M1MAM.txt
Nous allons commencer par lire le fichier de données dans le notebook Jupyter, puis nettoyer un peu les données et enfin trouver des moyens de manipuler ces données.
4.6. Lecture des données dans un fichier#
Nous avons un fichier de données et nous voulons lire son contenu dans notebook Jupyter. En général, c’est une bonne idée de jeter un coup d’œil dans le fichier pour voir à quoi ressemble son contenu. Cela nous donne également une chance d’apprendre une petite astuce.
Rappelez-vous que les cellules de code d’un notebook Jupyter peuvent gérer n’importe quelle instruction IPython
valide. Eh bien, IPython
est capable de faire un peu plus que d’executer des instructions Python: il peut également exécuter n’importe quelle commande système. Si vous connaissez Unix/linux (le système d’exploitation du serveur Jupyter), cela peut être très utile: par exemple, vous pouvez facilement lister tous les fichiers dans le répertoire de travail (votre emplacement dans le système de fichiers de l’ordinateur).
Pour exécuter une commande système (c.a.d, une commande du shell), il suffit d’ajoutez un point d’exclamation !
devant la commande. La commande dont nous avons besoin est head mon_fichier.txt
qui affiche les premières lignes du fichier de nom mon_fichier.txt
.
Notre dossier de données se trouve dans le sous-répertoire data, donc nous devons écrire ./data/
avant le nom du fichier,master_M1MAM.txt
pour préciser le chemin d’accès (le point .
indique le répertoire courant et le /
est utilisé sous Unix pour séparer les noms des dossiers (répertoires).
La commande s’écrit donc
!head ./data/master_M1MAM.txt
!head ./data/master_M1MAM.txt
De même, sous Unix/Linux, la commande wc-l mon_fichier
renvoie le nombre de lignes du fichier mon_fichier
.
Ansi pour notre fichier, le nombre de lignes du fichier vaut:
!wc -l ./data/master_M1MAM.txt
L’étape suivante consiste à ouvrir le fichier, lire son contenu et l’enregistrer dans une variable Python pour pouvoir l’utiliser par la suite.
La fonction open()
avec le nom de fichier entre " "
comme argument renvoie un objet fichier (file) Python.
Il faut ensuite lire le contenu du fichier et on plusieurs façons de le faire.
Enfin, une fois les données lues, il faut fermer le fichier en utilisant la méthode .close()
de l’objet fichier.
Pour lire le fichier, on peut utilisez la méthode de fichier read ()
, mais on obtient une (très grande) chaîne avec tout le contenu du fichier. A la place, on utilisera la méthode readlines()
, qui renvoie une liste de chaînes, où chaque chaîne contient une ligne dans le fichier.
fichier_M1MAM = open('./data/master_M1MAM.txt')
M1MAM_text = fichier_M1MAM.readlines()
fichier_M1MAM.close()
Vérifier la bonne lecture en affichant le type de M1MAN_text et sa taille.
On pourra comparer avec le résultat donnée par la commande Unix wc -l mon_fichier
qui renvoie le nombre de ligne du fichier mon_fichier
fichier_M1MAM = ''
M1MAM_text = ''
### BEGIN SOLUTION
### END SOLUTION
print("nbre de lignes =",len(M1MAM_text))
assert (len(M1MAM_text) == 48)
4.7. Nettoyage et organisation des données textuelles#
Lors de la manipulation de données textuelles, l’une des actions typiques consiste à supprimer les espaces et les lignes vides. Ici, nous allons supprimer les espaces au début et à la fin de chaque ligne.
Notez qu’il y a aussi quelques lignes vides: nous les ignorerons. L’objectif est d’obtenir deux nouvelles listes: une avec la ligne d’identification du cours et une autre avec les descriptions des cours.
Étudiez l’algorithme suivant:
Pour chaque ligne dans la liste des lignes
enlever les blancs à la fin de la ligne
si la ligne est vide continuer
sinon si la ligne commence par MGC la rajouter à la liste des cours
sinon rajouter la ligne a la liste de description du cours
sa traduction en code Python s’écrit:
Cours = []
Descriptions = []
for ligne in M1MAM_text:
ligne = ligne.strip() #enleve les espaces
if ligne == '': #saute les lignes vides
continue
elif ligne.startswith('MGC'):
Cours.append(ligne) #nom du cours commence par MGC
else:
Descriptions.append(ligne) #description du cours
RéEcrire dans la cellule suivante ce code Python pour calculer la liste des cours et la description de ces cours.
Nous avons utilisé la méthode strip()
pour se débarrasser des espaces en début et de fin de chaîne, et nous avons également utilisé startswith()
pour identifier les lignes contenant le nom de cours.
Vérifiez en affichant quelques éléments des 2 listes et testez que les 2 listes ont bien le même nombre d’éléments
Cours = []
Descriptions = []
### BEGIN SOLUTION
### END SOLUTION
print("cours:",Cours[0:2])
print("description:",Descriptions[0:2])
assert(len(Cours) == len(Descriptions))
4.7.1. Extraction de l’information#
Pour chaque cours, la ligne avec le nom du cours, contient son code APOGE, son titre, le nombre de crédit ects et le semestre séparé par :
L’algorithme suivant permet de créer ces listes à partir de la liste des cours
Pour chaque cours dans la liste des cours
découpez chaque chaîne cours en sous chaîne en utilisant le séparateur ':'
ajouter la première sous-chaîne à la liste des codes APOGEE: cours_id
ajouter la seconde sous-chaîne à la liste des titres: cours_titre
ajouter la troisième sous-chaîne à la liste des ECTS cours_ects
ajouter la quatrième sous-chaîne à la liste des semestres: cours_semestre
Le code suivant implémente cet algorithme et permet d’extraire avec une boucle ces formations de la liste Cours
en créant 4 nouvelles listes: cours_id
, cours_titre
, cours_ects
, cours_semestre
cours_id = []
cours_titre = []
cours_ects = []
cours_semestre = []
for cours in Cours:
cours_info = cours.split(':')
cours_id.append(cours_info[0].strip())
cours_titre.append(cours_info[1].strip())
cours_ects.append(cours_info[2].strip())
cours_semestre.append(cours_info[3].strip())
RéEcrire dans la cellule suivante ce code Python et vérifiez le résultat en affichant les listes ainsi crées.
A quoi sert la méthode strip()
?
cours_id = []
cours_titre = []
cours_ects = []
cours_semestre = []
### BEGIN SOLUTION
### END SOLUTION
assert(len(cours_id)==len(Cours))
assert(len(cours_titre)==len(Cours))
assert(len(cours_ects)==len(Cours))
assert(len(cours_semestre)==len(Cours))
4.7.2. exercices#
modifier le code précédent pour avoir dans cours_ects
la valeur entière du nombre de crédits du cours au lieu de la chaine “6 ECTS” ou “3 ECTS”, et de même dans cours_semestre
le numéro entier du semestre (1 ou 2) au lieu de semestre 1
ou semestre 2
.
On pourra noter que pour convertir un chiffre caractère en entier on utilise la fonction int(car)
i = int(`1`)
cours_id = []
cours_titre = []
cours_ects = []
cours_semestre = []
### BEGIN SOLUTION
### END SOLUTION
print("ECTS",cours_ects)
assert(type(cours_ects[0])==int)
print("Semestre",cours_semestre)
assert(type(cours_semestre[0])==int)
solution
Pour afficher la solution, cliquez ici (mais chercher la solution avant!):
cours_id = []
cours_titre = []
cours_ects = []
cours_semestre = []
for cours in Cours:
cours_info = cours.split(':')
cours_id.append(cours_info[0].strip())
cours_titre.append(cours_info[1].strip())
cours_ects.append(int(cours_info[2].strip()[0]))
cours_semestre.append(int(cours_info[3].strip()[-1]))
# solution
cours_id = []
cours_titre = []
cours_ects = []
cours_semestre = []
### BEGIN SOLUTION
### END SOLUTION
4.8. Utilisation des données#
4.8.1. accès à l’information d’un cours#
Les listes que nous avons créées sont alignées: c’est-à-dire que chaque élément du même emplacement d’index correspond au même cours. Ainsi, en trouvant l’emplacement d’un identifiant de cours, nous pouvons accéder à toutes les autres informations.
Nous utilisons la méthode index()
pour trouver l’index de l’identifiant du cours et afficher le reste des informations. On mettra l’index de ces cours dans les 3 variable i1
, i2
et i3
Calculer et afficher l’information pour les 3 cours suivants (afficher après exécution de la cellule suivante)
print("codes des UE à afficher: {} {} {}".format(_ID[_I1],_ID[_I2],_ID[_I3]))
i1 = 0
i2 = 0
i3 = 0
### BEGIN SOLUTION
### END SOLUTION
assert(cours_id[i1] == _ID[_I1])
assert(cours_id[i2] == _ID[_I2])
assert(cours_id[i3] == _ID[_I3])
4.8.2. exercise: calcul sur les données#
A partir des listes créés, déterminer le nombre total de crédits sur l’année puis sur chacun des 2 semestres, et le mettre dans les variables:
ects_total
ects_s1
ects_s2
Créez la liste des codes APOGEE des cours du premier semestre et des cours du second semestre dans
cours_s1
cours_s2
Déterminez le nombre de cours du premier et du second semestre dans
nbre_cours_s1
nbre_cours_s2
On pourra utiliser la forme de boucle suivante:
for i,val in enumerate(liste):
pour itérer sur l’index et les valeurs d’une liste
ects_total = 0
ects_s1 = 0
ects_s2 = 0
cours_s1 = []
cours_s2 = []
nbre_cours_s1 = 0
nbre_cours_s2 = 0
### BEGIN SOLUTION
### END SOLUTION
print("ects_total=",ects_total,ects_s1,ects_s2)
print("cours semestre 1:",cours_s1)
print("cours semestre 2:",cours_s2)
assert(ects_total == 60)
assert(ects_s1 == 30 and ects_s2 == 30)
assert((nbre_cours_s1 == 6) and (nbre_cours_s1 + nbre_cours_s2) == len(Cours))
4.9. Conclusion#
Ecrire votre analyse et votre conclusion en utilisation la cellule de texte suivante au format markdown
4.9.1. Analyse et conclusion#
4.9.1.1. Principe de la méthode d’analyse#
4.9.1.2. Résultat de l’analyse#
4.9.1.3. Conclusion#
# test 2
assert(test_markdown('TP_chaine_liste.ipynb','cell-comment1',minm=40,maxe=0.3))
4.10. Exercises optionels#
4.10.1. Exercise 1 à faire#
Exercice dans les cellules suivantes avec :
une cellule avec la définition de la fonction avec ces arguments.
une cellule de code pour définir la fonction en spécifiant les bons arguments mais sans changer le nom de la fonction,
une cellule pour tester la fonction
une cellule pour valider la fonction (et noter)
printmd("### Exercise 1: écrire une fonction func1 t.q.")
nom = Exos[1]
!test_exo -e $nom
# spécifier les arguments
def func1( ):
#rentrer votre code
# definir la valeur de retour
return
print("Verification: appel de la fonction")
# specifier les valeurs des arguments pour tester
### BEGIN SOLUTION
### END SOLUTION
assert(test_code('TP_chaine_liste.ipynb','cell-verif1','func1('))
assert(check_function(func1,Exos[1]))
4.10.2. Exercice 2 à faire#
printmd("### Exercise 2: écrire une fonction func2 t.q.")
nom = Exos[2]
!test_exo -e $nom
# spécifier les arguments
def func2( ):
#rentrer votre code
# definir la valeur de retour
return
print("Verification: appel de la fonction")
# specifier les valeurs des arguments pour tester
### BEGIN SOLUTION
### END SOLUTION
assert(test_code('TP_chaine_liste.ipynb','cell-verif2','func2('))
assert(check_function(func2,Exos[2]))
4.10.3. Exercice 3 à faire#
printmd("### Exercise 3: écrire une fonction func3 t.q.")
nom = Exos[3]
!test_exo -e $nom
# spécifier les arguments
def func3( ):
#rentrer votre code
# definir la valeur de retour
return
print("Verification: appel de la fonction")
# specifier les valeurs des arguments pour tester
### BEGIN SOLUTION
### END SOLUTION
assert(test_code('TP_chaine_liste.ipynb','cell-verif3','func3('))
assert(check_function(func3,Exos[3]))
4.11. FIN du TP#
from platform import python_version,uname,platform
print("Systeme :",uname())
print("OS :",platform())
print("version python:",python_version())