python fichier csv

Identification

Infoforall

30 - Introduction à l'Opendata avec Python


Opendata : en français, on dit données ouvertes. Il s'agit d'un concept : le fait que les données et l'information sont un bien commun à l'humanité et qu'il est important de pouvoir la diffuser et la partager. On trouve désormais des fichiers regroupant des données traitant de tout et de rien très facilement sur le Web.

Quelques critères permettant de qualifier une information OpenData :

  • Complète
  • Primaire
  • Opportune
  • Accessible
  • Exploitable
  • Non-discriminatoire
  • Non-propriétaire
  • Libre de droits
  • Permanente
  • Gratuite

Nous allons en parler un peu ici. Si vous voulez plus de renseignements après cette activité : article Wikipedia.

Prérequis : Dictionnaires et tableaux.

Attention : on rappelle que les tableaux qui nous utilisons dans Python sont en réalité des objets de type list. Or, justement, les listes sont une structure de données assez différentes des tableaux.

Nous utiliserons donc les lists de python comme des tableaux (arrays en anglais).

Evaluation ✎ : questions 04-06-08-09-10-11-12-18-19-21-22-23-24 (13 questions).

Logiciel nécessaire pour l'activité : Python 3 : Thonny, IDLE ...

Documents de cours : open document ou pdf

1 - OpenData

OpenData : à la fois mouvement philosophique et politique qui vise à diffuser l'information au plus grand nombre. Grossièrement, la notion possède les mêmes fondements que la liberté de la presse : avec une information libre d'accès, on peut lutter facilement contre :

  • les manipulations,
  • les mensonges et
  • les contre-vérités.

Un autre lien évident avec l'informatique ? Le monde du logiciel libre : si on a accès au code-source, on peut clairement savoir ce que fait un programme plutôt que devoir faire confiance à l'éditeur. C'est donc exactement la même chose avec l'OpenData : si on a accès aux informations brutes, on peut analyser les chiffres plutôt que de faire confiance à l'article.

Il s'agit normalement de publier de façon légale des informations en permettant aux autres de les lire, de les publier et de les exploiter. Il existe de multiples licences liées à ces publications bien entendu.

Il existe une autre thématique proche de l'OpenData : la fuite de données. Celle des lanceurs d'alerte qui publient des données non libres. Ils savent qu'ils seront poursuivis et qu'ils risquent gros mais décident d'informer le monde d'une malversation ou plus généralement d'un problème. Ainsi le site Wikileaks a publié des données liées aux renseignements américains. Il ne s'agissait pas d'OpenData ici mais la diffusion de ces informations a clairement modifié le monde et la vision qu'ont les gens de la collecte d'informations.

Avec le Web, la diffusion d'informations est facile. Reste le problème de la fiabilité : publier des données ne veut pas dire que l'information soit réelle ! Le travail des journalistes n'est donc pas fini. Au contraire, il ne fait que commencer et il leur faudra désormais des outils informatiques puissants pour analyser différentes sources et fournir une probabilité de véracité.

Nous retrouvons ici Tim Berners-Lee. Le créateur du Web a défini une échelle dans l'OpenData.

Photo de Tim Berners-Lee
Tim Berners-Lee (2010) - CC BY 2.0 - Paul Charliee — originally posted to Flickr
Rang Signification
Données non filtrées ou non structurées (éventuellement dégradées), mises en ligne avec un format non standard ou sans format du tout
★★ Données structurées (données au format CSV, TSV, XML, ou issues d'un format dont la spécification est connue : PDF, fichier tableur...)
★★★ Données librement exploitables. Juridiquement (Cf. licences) et techniquement (les données ne sont pas lisibles uniquement avec un logiciel propriétaire (EXCEL ou autres).
★★★★ Données datées, identifiées par des URL (avec date de mise à jour) afin que l'on puisse « pointer » un lien vers elles (et les retrouver éventuellement mises à jour).
★★★★★ Données liées à d'autres données graçe à des données communes, pour les contextualiser et les enrichir

Nous allons voir ce que signifient tous ces termes.

Données non structurées (rappel SNT)

Une donnée non structurée est une donnée présente mais qu'un système automatisé aura bien du mal à retrouver.

Exemple

C'est une voiture de sport de couleur rouge et conduite par un moustachu qui porte des chemises à fleurs.

L'un de ses amis possède un bel hélicoptère. Celui-ci est plutôt marron mais possède également quelques lignes colorées dans des tons rouge, bleu et jaune.

01° Quel est le type et la couleur du premier véhicule ? Quel est le type et la couleur du second véhicule ?

...CORRECTION...

Véhicule : Voiture de sport --- Couleur : rouge

Véhicule : Hélicoptère --- Couleur : marron

Vous venez d'utiliser un centre de traitement de l'information super performant : votre cerveau.

Pas possible pour un ordinateur. Il existe une branche de la recherche informatique qui traite de cette capacité de donner du sens à un texte brut mais c'est loin d'être facile et rapide.

La meilleur façon pour qu'un ordinateur puisse traiter cela est de lui mâcher le travail : nous allons structurer les données.

Vocabulaire sur les Données structurées (rappel SNT)

Une donnée structurée est une donnée présentée selon un format connu et fixe.

Il peut s'agir par exemple d'un tableau à deux entrées.

Exemple

Identifiant Type Utilisation Couleur
1 Voiture Sport Rouge
2 Hélicoptère Tourisme Marron
3 Voiture Sport Rouge

Les colonnes sont nommées descripteurs, attributs ou caractères selon les spécialités scientifiques.
Ici, j'utiliserai le mot attribut.

Toutes les données présentes dans ce tableau partagent les mêmes attributs et les fournissent dans le même ordre :
ID - Type - Catégorie - Couleur.

Chaque ligne se nomme enregistrement, objet, ou individu selon la spécialité scientifique.
Ici, nous parlerons d'enregistrement.

La seule ligne qui ne soit pas un enregistrement est la toute première, en bleu foncé. Il s'agit de l'en-tête (header en anglais). Elle permet de connaître le nom des attributs justement.

Une case (intersection d'un enregistrement et d'un attribut) peut porter le nom de champ.

Le contenu d'une case se nomme une valeur.

L'ensemble des données se nomme la collection en informatique (ou population en mathématique).

Collection Attributs
Identifiant Type Utilisation Couleur
Enregistrements 0 Voiture Sport Rouge
1 Hélicoptère Tourisme Marron
2 Voiture Sport Rouge

En résumé :

  • L'ensemble des données ➡ Collection
  • Une colonne ➡ Attribut
  • Une ligne ➡ Enregistrement
  • Une case ➡ Champ
  • Valeur d'une case ➡ Valeur
  • La toute première ligne ➡ En-tête

02° Quels sont les attributs de la collection précédente ? Combien y-a-t-il d'enregistrements dans cette collection ? Quelle est la valeur qui correspond à l'attribut d'index 2 et à l'enregistrement d'index 0 ?

...CORRECTION...

Il y a 4 attributs :

  • Index 0 : Identifiant
  • Index 1 : Type
  • Index 2 : Utilisation
  • Index 3 : Couleur

Il y a 3 enregistrements d'index 0, 1 et 2.

La valeur d'index 2 en attribut (colonne Utilisation) et d'index 0 en enregistrement correspond à sport

03° Quel est le seul attribut qui permette de distinguer le premier et le dernier enregistrement ?

...CORRECTION...

Il s'agit de l'identifiant. On rajoute TOUJOURS un identifiant qu'on définit soi-même. De cette façon, nous sommes certains de pouvoir distinguer deur enregistrements même si les contenus sont similaires.

✎ 04° Peut-on séparer deux individus uniquement à l'aide du nom et du prénom ? A quoi sert le numéro de sécurité sociale ?

2 - Tableau de tableaux

Voyons comment gérer cela avec Python.

Nous allons créer des tableaux de tableaux ou des tableaux de dictionnaires.

Vous allez voir que c'est finalement assez intuitif.

Création d'enregistrements

Comme les données sont structurées, nous pourrions stocker les valeurs d'un enregistrement dans un tableau.

1 2 3
enr0 = ['0', 'voiture', 'sport', 'rouge'] enr1 = ['1', 'hélicoptère', 'tourisme', 'marron'] enr2 = ['2', 'voiture', 'sport', 'rouge']

05° Que va-t-on récupérer si on lance ceci dans la console Python après avoir placé les 3 variables précédentes en mémoire ?

>>> enr0[1] >>> enr1[1] >>> enr2[1]

...CORRECTION...

>>> enr0[1] 'voiture' >>> enr1[1] 'helicoptère' >>> enr2[1] 'voiture'

On récupère donc bien les valeurs stockées dans l'attribut TYPE qui correspond à l'index 1.

✎ 06° Sur le même principe que la question précédente, que doit-on taper pour récupérer la couleur de l'enregistrement 2 ?

Pour l'instant, nous avons encore 3 variables : une pour chaque enregistrement. C'est pénible.

Création de la collection

Nous allons stocker nos enregistrements-tableau dans un tableau. On obtient ainsi un tableau de tableaux.

1 2 3 4 5
enr0 = ['0', 'voiture', 'sport', 'rouge'] enr1 = ['1', 'hélicoptère', 'tourisme', 'marron'] enr2 = ['2', 'voiture', 'sport', 'rouge'] maCollec = [enr0, enr1, enr2]

07° Que va-t-on récupérer si on lance ceci dans la console Python après avoir placé les 4 variables précédentes en mémoire ?

>>> maCollec[0] >>> maCollec[1] >>> maCollec[2]

...CORRECTION...

>>> maCollec[0] ['0', 'voiture', 'sport', 'rouge'] >>> maCollec[1] ['1', 'helicoptère', 'tourisme', 'marron'] >>> maCollec[2] ['2', 'voiture', 'sport', 'rouge']

On récupère donc bien les valeurs stockées dans l'attribut TYPE qui correspond à l'index 1.

Un élément maCollec[i] du tableau maCollec est donc un tableau.
Et si on veut lire uniquement un attribut ? Il suffit de rajouter un crochet supplémentaire derrière [i].

Exemple

>>> maCollec[0] ['0', 'voiture', 'sport', 'rouge'] >>> maCollec[0][0] '0' >>> maCollec[0][1] 'voiture' >>> maCollec[0][2] 'sport'

Traduction en Français : maCollec[0][1] veut dire "va chercher l'élément d'indice 0 du tableau maCollec. Dans le tableau que tu récupères, va maintenant chercher l'élement d'indice 1.

On obtient ainsi cela :

maCollec[0][1] ['0', 'voiture', 'sport', 'rouge'][1] 'voiture'

✎ 08° Sur le même principe que l'exemple précédente, que va répondre l'interpréteur Python à l'instruction suivante ?

>>> maCollec[1][3]

Pour se passer des variables intermédiaires, nous allons taper ceci :

1 2 3 4 5
maCollec = [ # [ début d'un tableau ['0', 'voiture', 'sport', 'rouge'], # virgule comme séparateur ['1', 'helicoptère', 'tourisme', 'marron'], # virgule comme séparateur ['2', 'voiture', 'sport', 'rouge'] ] # ] fin de tableau

On ouvre un tableau (crochet rouges) et qu'on y place des tableaux (crochets violets), séparés par des virgules.

On pourrait créer ce tableau d'une nouvelle façon : par extension et ajouts successifs, en rajoutant au fur et à mesure les nouveaux enregistrements avec la méthode append() (to append se traduit par le verbe rajouter).

Il ne s'agit donc plus d'un tableau statique de taille fixe : les vrais tableaux statiques ne disposent pas de cette fonctionnalité. Le type list de Python est plutôt ce qu'on nomme un tableau dynamique. Mais si on n'utilise pas append() pour rajouter des cases et pop() pour supprimer des cases et qu'on met des éléments de même type dans chaque case, on obtient bien un tableau statique.

1 2 3 4
maCollec = [] maCollec.append( ['0', 'voiture', 'sport', 'rouge'] ) maCollec.append( ['1', 'helicoptère', 'tourisme', 'marron'] ) maCollec.append( ['2', 'voiture', 'sport', 'rouge'] )

✎ 09° Utiliser ce programme. Demander dans la console le contenu de maCollec. Donner l'instruction permettant de rajouter un enregistrement de votre choix. Fournir le code à rajouter dans le programme et le résultat de la variable maCollec après modification.

Dernière question de cette sous-partie : voyons comment visualiser le contenu de notre collection proprement en utilisant les fStrings et leurs capacités à forcer l'affichage sur un nombre précis de caractères (20 ici).

✎ 10° Utiliser ce programme qui mémorise une collection et une fonction-procédure visualiser(). Taper ensuite visualiser(maCollec) dans la console pour voir le contenu de la collection d'enregistrements.

1 2 3 4 5 6 7 8 9
def visualiser(c): """affiche le contenu des enregistements e de la collection c""" for e in c: print(f"{e[1]:20}{e[2]:20}{e[3]:20}") maCollec = [] maCollec.append( ['0', 'voiture', 'sport', 'rouge'] ) maCollec.append( ['1', 'helicoptère', 'tourisme', 'marron'] ) maCollec.append( ['2', 'voiture', 'sport', 'rouge'] )

Questions

  1. Justifier qu'on puisse ici se passer de l'utilisation d'une boucle avec indice sur la ligne 3 (for e in c plutôt que for i in range(len(c))).
  2. Fournir le code de la même fonction mais en utilisant plutôt les indices.

...CORRECTION PARTIELLE...

>>> visualiser(maCollec) voiture sport rouge helicoptère tourisme marron voiture sport rouge

Vous vous doutez bien que nous n'allons pas taper directement le contenu du tableau. Dans la dernière partie, nous verrons comment créer ces tableaux à l'aide de fichiers-texte au format CSV.

3 - Tableau de tuples

Le problème du tableau de tableaux statiques, c'est que toutes les "cases" doivent contenir le même type d'éléments (que des integers, que des strings ...)

En Python, nous n'utilisons pas réellement des tableaux mais des objets de type list qui permettent d'avoir des types différents dans les cases. Mais, sinon, comment faire ?

Et bien, on pourrait utiliser un tableau de tuples.

Lors de la déclaration, la seule différence vient de la présence de parenthèses.

1 2 3 4 5
enr0 = ('0', 'voiture', 'sport', 'rouge') enr1 = ('1', 'hélicoptère', 'tourisme', 'marron') enr2 = ('2', 'voiture', 'sport', 'rouge') maCollec = [enr0, enr1, enr2]

Deux intérêts à cette façon de faire :

  • On peut maintenant mettre ce qu'on veut dans les éléments, ils n'ont plus à être de même type.
  • Les n-uplets/tuples de Python sont immuables : on ne pourra pas modifier involontairement les enregistrements.

4 - Tableau de dictionnaires

Le problème du tableau de tableaux ou du tableau de tuples, c'est qu'il faut connaitre le numéro d'indice de l'attribut pour le trouver. C'est un peu pénible.

On peut faire mieux avec les dictionnaires : il suffit de créer un tableau de dictionnaires dont la clé est ... le nom du descripteur (ou attribut) !

1 2 3 4 5
enr = {} enr['id'] = 0 enr['type'] = 'voiture' enr['utilisation'] = 'sport' enr['couleur'] = 'rouge'

Une fois enr en mémoire, on peut alors lancer ceci dans la console :

>>> enr['couleur'] 'rouge'

Avantage : on voit le nom de l'attribut. Pas de risque de se tromper de numéro de colonne.

Désavantage : la création est longue !

Nous allons donc automatiser les choses en créant une fonction creer_enregistrement() qui attend un tableau en paramètes et qui renvoie le dictionnaire correspondant.

✎ 11° Utiliser ce programme qui utilise la fonction creer_enregistrement() pour créer et renvoyer un dictionnaire créé à partir du tableau val des 4 valeurs transmises lors de l'appel.

Voici son prototype :

def creer_enregistrement(val:list) -> dict

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
def creer_enregistrement(val): """Renvoie un dictionnaire ayant les clés id, type, utilisation et couleur :: param val(list) :: un tableau de 4 éléments correspondant aux clés ci-dessus :: return (dict) :: dictionnaire-enregistrement avec les 4 clés id, type, utilisation et couleur .. exemple .. >>> creer([0, 'voiture', 'sport', 'rouge']) {'id': 0, 'type': 'voiture', 'utilisation': 'sport', 'couleur': 'rouge'} """ d = {} d['id'] = val[0] d['type'] = val[1] d['utilisation'] = val[2] return d enr = creer_enregistrement([0,'voiture','sport','rouge']) print(enr)

Question

Expliquer clairement pourquoi la fonction renvoie ce qu'on observe ci-dessous. Il faudra expliquer ce qu'elle fait du tableau des valeurs [0, 'voiture', 'sport', 'rouge'] reçu via le paramètre val :

>>> creer_enregistrement([0, 'voiture', 'sport', 'rouge']) {'id': 0, 'type': 'voiture', 'utilisation': 'sport'}

✎ 12° Zut ! Il manque la couleur lors de la création du dictionnaire. Rajouter la clé 'couleur' dans la fonction creer_enregistrement(). On notera que la couleur est stockée sur l'indice 3 du tableau val reçu en paramètre.

Maintenant que nous savons créer nos enregistrements, nous n'avons plus qu'à les rajouter progressivement par extension et ajouts successifs (avec la méthode append()) dans notre collection. Nous allons donc créer un tableau de dictionnaires.

13° Rajouter ces instructions sous votre fonction et afficher ensuite le contenu de la collection maCollec dans la console.

18 19 20 21 22
# Programme maCollec = [] maCollec.append( creer_enregistrement([0,'voiture','sport','rouge']) ) maCollec.append( creer_enregistrement([1,'hélicoptère','tourisme','marron']) ) maCollec.append( creer_enregistrement([2,'voiture','sport','rouge']) )

On rappelle que la structure d'un programme est la suivante

  • Les importations
  • Les déclarations de CONSTANTES
  • Les déclarations de fonctions
  • Les instructions du programme principal

Vous obtenez donc un tableau de dictionnaires ayant tous les mêmes clés.

Nous aurions obtenu le même résultat si nous avions tapé ceci à la main :

1 2 3 4 5
maCollect = [ {'id': 0, 'type': 'voiture', 'utilisation': 'sport', 'couleur': 'rouge'}, {'id': 1, 'type': 'hélicoptère', 'utilisation': 'tourisme', 'couleur': 'marron'}, {'id': 2, 'type': 'voiture', 'utilisation': 'sport', 'couleur': 'rouge'} ]

Les clés des dictionnaires sont les attributs de notre collection.

Chacun des dictionnaires est l'un des enregistrements.

Comme les dictionnaires ont tous les mêmes clés, on dira que ce sont des p-uplets nommés (4-uplets nommés ici) : des séquences de 4 éléments ayant chacun un nom bien particulier.

14°Que va-t-on obtenir en tapant ceci dans la console :

>>> maCollec[1]

...CORRECTION...

On obtient l'indice 1 de notre tableau-collection, à savoir un dictionnaire.

{'id': 1, 'type': 'hélicoptère', 'utilisation': 'tourisme', 'couleur': 'marron'}

15° Que va-t-on obtenir en tapant ceci dans la console :

>>> maCollec[1]['couleur']

...CORRECTION...

Avec maCollec[1], on obtient l'indice 1 de notre tableau, à savoir un dictionnaire.

{'id': 1, 'type': 'hélicoptère', 'utilisation': 'tourisme', 'couleur': 'marron'}

Si on cherche la clé 'couleur' dans ce dictionnaire, il va donc renvoyer la valeur associée à cette clé, et c'est 'marron'.

Il est temps maintenant de voir comment on peut parvenir à récupérer des données libres pour en faire quelque chose.

5 - CSV

Vous avez vu en SNT qu'il existe des formats courants de données. Cela permet de créer des données et de les distribuer dans un format qui sera facilement utilisable par quelqu'un d'autre.

On pourra croire qu'on peut simplement créer des fichiers Python avec des tableaux de tableaux ou des tableaux de dictionnaires, mais comment faire si quelqu'un veut récupérer vos données avec un autre langage que Python ?

L'un des formats les plus répandus est le format CSV.

CSV et TSV

Description générale

C'est un moyen simple de transmettre des données : on écrit un simple fichier-texte.

Ce fichier est compréhensible par n'importe quel langage de programmation et n'importe quel tableur.

CSV veut dire Comma Separated Values : littéralement, des valeurs séparées par des virgules.

Structuration des données

  1. La première ligne du fichier-texte contient les intitulés des attributs .
  2. Chaque enregistement est constitué d'une ligne.
  3. La fin de ligne (Line Feed) correspond à un octet valant 10 (norme ASCII). Elle est représentée par dans l'exemple ci-dessous, et encodé par \n en Python.
  4. Dans un enregistrement, chaque valeur est séparée des autres par une virgule.

Exemple

1 2 3 4
#,Name,Type 1,Type 2,Total,HP,Attack,Defense,Sp. Atk,Sp. Def,Speed,Generation,Legendary 1,Bulbasaur,Grass,Poison,318,45,49,49,65,65,45,1,False 2,Ivysaur,Grass,Poison,405,60,62,63,80,80,60,1,False 3,Venusaur,Grass,Poison,525,80,82,83,100,100,80,1,False

Si on rajoute les éléments importants en rouge (dont le passage à la ligne invisible à l'affichage ):

1 2 3 4
#,Name,Type 1,Type 2,Total,HP,Attack,Defense,Sp. Atk,Sp. Def,Speed,Generation,Legendary 1,Bulbasaur,Grass,Poison,318,45,49,49,65,65,45,1,False 2,Ivysaur,Grass,Poison,405,60,62,63,80,80,60,1,False 3,Venusaur,Grass,Poison,525,80,82,83,100,100,80,1,False

La première colonne est donc celle de l'attribut #, pour dire qu'il s'agit d'un simple nombre d'identification.

La deuxième colonne correspond au nom du pokemon.

Les deux suivantes correspondantes à ces deux types ...

On trouve des variables du CSV.

L'élément séparateur virgule est très courant. En ASCII, l'octet symbolisant une virgule est 44.

L'élément séparateur peut être parfois le point-virgule (;), un octet valant 59 en ASCII.

L'élement séparateur peut être la tabulation, un octet à 9. On parle alors de TSV, Tabulated Separated Values.

Mais ça ne change rien au principe : il faut juste connaître l'élément séparateur utilisé pour parvenir à bien séparer les éléments.

16° Aller à l'adresse Github suivante (ou faire une simple recherche dans un moteur de recherche sur CSV + Pokemon): CSV POKEMON GITHUB.

Vous devriez aboutir sur une page qui vous présente visuellement le fichier sous la forme d'un tableau, comme un tableur.

Une définition : celle d'encodage

Appuyer sur le bouton RAW ("cru" ou "brut" en Français) : vous devriez voir le vrai contenu de ce fichier.

Et voilà, un beau fichier CSV, à savoir un fichier-texte qui possède juste un nom qui finit en .csv.

17° Enregistrer le fichier (Enregister sous dans votre navigateur). Ouvrir ce fichier avec votre tableur habituel. Si vous ne parvenez pas à télécharger le fichier, voici un lien avec une copie depuis infoforall : LE FICHIER CSV.

Avec OpenCalc de LibreOffice, on obtient la page d'accueil suivante :

Tableur

Il faut lui indiquer l'encodage utilisé (à priori UTF-8 si le fichier est récent) ainsi que le caractère séparateur. Ici la virgule.

Ensuite, on lance et on obtient ceci :

CSV dans un tableur

Comment parvenir à créer un tableau de dictionnaires avec cela ?

C'est le but de la partie suivante.

6 - Fichier CSV et Python

Voici ce que nous allons faire :

  1. On crée un tableau dynamique vide nommé c comme collection.
  2. On ouvre avec Python le fichier-texte csv en lecture
  3. On lit l'en-tête (la première ligne) pour créer un tableau att contenant les noms des attributs
  4. On lit chaque ligne suivante et
    • on crée un tableau val contenant les valeurs lues sur la ligne puis
    • on utilise val et att pour créer le dictionnaire enr comme enregistrement.
    • on rajoute alors enr à notre collection c en utilisant la méthode append()
  5. On ferme le fichier

Passons à l'implémentation de cette technique.

Vous allez simplement la mettre en mémoire : le programme n'est constitué que de trois fonctions qu'on place en mémoire. Nous allons progressivement apprendre à les manipuler et les comprendre.

✎ 18° Placer le programme ci-dessous en mémoire.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
def decomposer(chaine, separateur): """Fonction qui décompose la chaine et créant un tableau contenant les valeurs :: chaine (str) :: la chaîne de caractères à décomposer :: separateur (str) :: le caractère de séparation :: return (list) :: un tableau contenant les différents éléments .. exemple .. >>> decomposer('1,voiture,sport,rouge', ',') ['1', 'voiture', 'sport', 'rouge'] """ chaine = chaine.replace('\n', '') # On supprime le passage à la ligne chaine = chaine.replace('\r', '') # On supprime le retour chariot t = chaine.split(separateur) # On crée un tableau à partir de la chaine return t def creer_enregistrement(val, att): """Renvoie un dictionnaire créé à partir des tableaux val et att :: param val(list) :: un tableau des X valeurs :: param att(list) :: un tableau de X attributs correspondants :: return (dict) :: le dictionnaire voulu .. exemple .. >>> creer([0, 'voiture', 'sport', 'rouge'], ['id', 'type', 'utilisation', 'couleur']) {'id': 0, 'type': 'voiture', 'utilisation': 'sport', 'couleur': 'rouge'} """ d = {} if len(val) == len(att): for i in range(len(val)): d[att[i]] = val[i] return d def creer_collection(nom_fichier): """Fonction qui renvoie un tableau de dictionnaire à partir d'un fichier CSV :: param nom_fichier(str) :: le nom du fichier CSV correct à traiter :: return (list) :: le tableau de dictionnaires """ # 1 - On crée une collection-tableau vide c = [] # 2 - On ouvre le fichier en lecture ('r' pour reading) f = open(nom_fichier, 'r', encoding='utf-8') # 3 - On lit la première ligne pour créer le tableau des attributs entete = f.readline() # entete est un simple string qui finit par \n att = decomposer(entete, ',') # création d'un tableau contenant les attributs # 4 - On lit la suite du fichier ligne par ligne for ligne in f: val = decomposer(ligne, ',') # tableau contenant les valeurs enr = creer_enregistrement(val, att) # dictionnaire clé-valeur c.append(enr) # rajout à la collection # 5 - On ferme le fichier f.close() # On ferme le fichier # 6 - On renvoie la collection return c

Utiliser ensuite les instructions dans la console pour visualiser ce que la fonction decomposer() renvoie.

>>> attributs = decomposer('#,Name,Type 1,Type 2,Total\n', ',') >>> attributs ['#', 'Name', 'Type 1', 'Type 2', 'Total']

Répondre ensuite aux question suivantes liées à la fonction decomposer() :

  • A quoi sert la ligne 12 ?
  • Que fait la ligne 14 contenant la méthode split() ? Si vous ne voyez pas, allez voir la partie FAQ.
  • Que renvoie la fonction ? Un tableau, un dictionnaire ou un string ?

La fonction creer_enregistrement() fonctionne un peu comme celle de la partie précédente : elle crée un dictionnaire à partir d'un tableau. La différence ici est qu'elle attend deux tableaux :

  • le tableau val contient les valeurs dans l'ordre voulu aux indices 0, 1 et plus...
  • le tableau att contient les noms des clés correspondantes aux indices 0, 1, 2...

Exemple :

  • on envoie dans val le tableau suivant ['voiture', 'rouge']
  • on envoie dans att le tableau suivant ['type', 'couleur']
  • la fonction renvoie alors le dictionnaire {'type': 'voiture', 'couleur': 'rouge'}.

L'avantage est que notre programme fonctionne sur tous les fichiers CSV. Si on donne les noms des attributs à la main, cela implique de changer de programme dès qu'on change de fichier.

✎ 19° Utiliser le code suivant et expliquer la réponse que vous allez obtenir. Il faudra notamment clairement réaliser l'étape des lignes 30 et 31 sur les 2 premières étapes (index valant 0 et 1).

>>> att = decomposer('#,Name,Type 1,Type 2,Total\n', ',') >>> val = decomposer("1,Bob,Feu,Terre,58\n", ',') >>> enr = creer_enregistrement(val, att) >>> enr

Il reste à voir la lecture du fichier ligne par ligne de façon à rajouter les enregistrements un par un. Allons voir la fonction creer_collection().

  1. on commence par créer un tableau vide c qui contiendra notre collection d'enregistrements.
  2. on ouvre le fichier en lecture en créant un objet-fichier f qui permettra à Python d'interagir sur ce fichier-texte.
  3. on lit la première ligne uniquement à l'aide de la méthode readline() (sans s, une seule ligne donc). Une fois lue, le pointeur de lecture va se positionner automatiquement sur la ligne suivante, prêt à la lire si on lui demande à l'aide d'un autre readline().
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
def creer_collection(nom_fichier): """Fonction qui renvoie un tableau de dictionnaire à partir d'un fichier CSV :: param nom_fichier(str) :: le nom du fichier CSV correct à traiter :: return (list) :: le tableau de dictionnaires """ # 1 - On crée une tableau vide c = [] # 2 - On ouvre le fichier en lecture f = open(nom_fichier, 'r', encoding='utf-8') # 3 - On lit la première ligne pour créer le tableau des attributs entete = f.readline() # entete est un simple string qui finit par \n att = decomposer(entete, ',') # création d'un tableau contenant les attributs

Le début du fichier CSV est :

1 2 3 4
#,Name,Type 1,Type 2,Total,HP,Attack,Defense,Sp. Atk,Sp. Def,Speed,Generation,Legendary 1,Bulbasaur,Grass,Poison,318,45,49,49,65,65,45,1,False 2,Ivysaur,Grass,Poison,405,60,62,63,80,80,60,1,False 3,Venusaur,Grass,Poison,525,80,82,83,100,100,80,1,False

Après exécution de la ligne 47, c'est donc comme si vous aviez tapé ceci :

entete =
"#,Name,Type 1,Type 2,Total,HP,Attack,Defense,Sp.Atk,Sp. Def,Speed,Generation,Legendary\n"

Lorsqu'on appliquera la ligne 48 qui décompose entete à l'aide de la fonction decomposer(), on va donc obtenir le tableau att suivant 

att =
['#', 'Name', 'Type 1', 'Type 2', 'Total', 'HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed', 'Generation', 'Legendary']

Ensuite, on lit la totalité du fichier CSV, une ligne à la fois (L51) :

  • L52 : on récupére un tableau de valeurs val en fournissant la ligne du fichier CSV (composée d'un string décrivant les attributs du Pokemon) à la fonction decomposer()
  • L53 : on utilise le tableau de valeurs val et le tableau d'attributs att pour créer un enreigstrement att sous forme d'un dictionnaire en faisant appel à creer_enregistrement()
  • L54 : on rajoute l'enregistrement dans le tableau de la collection avec la méthode append().
50 51 52 53 54
# 4 - On lit la suite du fichier ligne par ligne for ligne in f: val = decomposer(ligne, ',') # tableau contenant les valeurs enr = creer_enregistrement(val, att) # dictionnaire clé-valeur c.append(enr) # rajout à la collection

C'est fini. On a lu toutes les lignes. On ferme le fichier et on renvoie le tableau qui contient l'ensemble des enregistrements-dictionnaires.

56 57 58 59 60
# 5 - On ferme le fichier f.close() # On ferme le fichier # 6 - On renvoie la collection return c

20° Mettre les fonctions de la question 18 en mémoire et utiliser ceci pour vérifier que pks (pour pokemons) contient bien la collection des pokemons :

>>> pks = creer_collection('pokemon.csv') >>> pks

Vous devriez visualiser l'ensemble des enregistrements, mais sans espacement ou passage à la ligne.

Si on cherche à le présenter de façon plus correct, ça donne ceci :

[ {'#': '1', 'Name': 'Bulbasaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '318', 'HP': '45', 'Attack': '49', 'Defense': '49', 'Sp. Atk': '65', 'Sp. Def': '65', 'Speed': '45', 'Generation': '1', 'Legendary': 'False'}, {'#': '2', 'Name': 'Ivysaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '405', 'HP': '60', 'Attack': '62', 'Defense': '63', 'Sp. Atk': '80', 'Sp. Def': '80', 'Speed': '60', 'Generation': '1', 'Legendary': 'False'}, {'#': '3', 'Name': 'Venusaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '525', 'HP': '80', 'Attack': '82', 'Defense': '83', 'Sp. Atk': '100', 'Sp. Def': '100', 'Speed': '80', 'Generation': '1', 'Legendary': 'False'}, {'#': '3', 'Name': 'VenusaurMega Venusaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '625', 'HP': '80', 'Attack': '100', 'Defense': '123', 'Sp. Atk': '122', 'Sp. Def': '120', 'Speed': '80', 'Generation': '1', 'Legendary': 'False'},

✎ 21° Rajouter, sous les déclarations de fonctions, les instructions permettant de faire ceci :

  1. créer et stocker la collection dans une variable pks (à l'aide de creer_collection()).
  2. afficher tous les enregistrements un par un en utilisant une boucle FOR.

Remarque : ne notez que les dernieres lignes de votre programme dans votre copie numérique, pas les trois fonctions que nous venons d'analyser (creer_collection(), creer_enregistrement() et decomposer()).

✎ 22° Quel est le coût de votre programme en tenant compte de la création de la collection et de l'affichage ? Le nombre de données n correspondra aux nombres de Pokemons.

  1. Coût constant
  2. Coût logarithmique
  3. Coût linéaire
  4. Coût quadratique

Il faudra vous demander qu'il est le coût de la création du dictionnaire puis de l'affichage.

✎ 23° Modifier le programme pour qu'il n'affiche que les enregistrements pour lequels les points de vie (HP) des Pokemons sont strictement supérieurs à 99.

✎ 24° Modifier le programme pour qu'il trouve le pokemon qui a le plus de PV. On affichera son enregistrement.

✎ MAISON° Chercher sur le Web un autre fichier de votre choix en tapant une recherche du type "CSV opendata un truc".

Télécharger le fichier sur votre ordinateur. L'ouvrir avec le programme Python pour vérifier que cela fonctionne. Me fournir l'URL du fichier CSV et les 6 premiers enregistrements.

Quelques exemples possibles :

  • Les naufragés du Titanic : TITANIC (attention, c'est un fichier TSV : c'est la tabulation \t qui sépare les élements ; pensez à modifier la virgule en \t lors des appels à décomposer aux lignes 54 et 58)
  • La liste des cas de COVID-19 dans le monde : COVID
  • Les villes françaises : base-donnees-villes-francaises

7 - FAQ

A quoi servent les méthodes replace et split ?

La méthode des strings replace sert à créer une copie d'un string en remplaçant certains caractères par d'autres caractères.

>>> "Ali Baba".replace("a","u" ) 'Ali Bubu'

La méthode des strings split sert à créer un tableau contenant des bouts du string d'entrée. On doit préciser l'élément qui sert à faire la séparation. Par défaut (si on ne donne rien), il s'agit de l'espace : on obtient ainsi (presque) les mots.

>>> "A B C D".split() ['A', 'B', 'C', 'D'] >>> "A B C D".split(" ") ['A', 'B', 'C', 'D'] >>> "Bon, alors, ça fonctionne bien ?".split() ['Bon,', 'alors,', 'ça', 'fonctionne', 'bien', '?'] >>> "Bon, alors, ça fonctionne bien ?".split(',') ['Bon', ' alors', ' ça fonctionne bien ?']

Et on fait comment pour récupérer les caractères un par un dans un tableau ?

Voici trois façons de faire pour récupérer ce tableau :

  1. Utiliser la fonction native list (simple et rapide)
  2. >>> list("A B CD") ['A', ' ', 'B', ' ', 'C', 'D']
  3. Utiliser une création par compréhension (simple et rapide)
  4. >>> [c for c in "A B CD"] ['A', ' ', 'B', ' ', 'C', 'D']
  5. Utiliser une création par extension avec append (hors programme en 1er NSI)
  6. >>> rep = [] >>> for c in "A B CD": rep.append(c) >>> rep ['A', ' ', 'B', ' ', 'C', 'D']

Pour l'instant on doit télécharger le fichier CSV à la main. On ne peut pas les récupérer automatiquement ?

Oui, sans problème. Je vous montre ici deux façons de réaliser cela.

La façon la plus basique et la plus facile, utilisant l'un des modules de base présent dans la distribution Python. Et une façon plus complexe, basée sur un module extérieur utilisant le module précédent.

Module urllib

Ce module est présent dans votre distribution Python normalement.

Voici comment parvenir à télécharger un fichier et à l'enregistrer dans le même dossier que celui où est stocké le fichier Python.

1 2
import urllib.request urllib.request.urlretrieve('https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv', 'leFichier.csv')

On doit donc fournir à la fonction urlretrieve deux arguments : le premier est un string contenu l'URL de la ressource et le deuxième est un string contenant le nom d'enregistrement sur votre disque dur.

Facile, rapide et efficace.

Le tout est de trouver une URL valide.

Module requests

Deuxième façon de faire avec un module Python plus complet et basé sur celui que nous venons de voir : requests.

Dans Thonny, aller dans le menu TOOLS - MANAGE PACKAGES. Lancer une recherche sur et installer le module requests.

1 2 3 4
import requests reponse = requests.request(url='https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv', method='GET') chaine_reponse = reponse.text print(text)

Il nous reste donc simplement à enregister cela dans un fichier csv, avec l'option w pour indiquer qu'on veut l'écrire donc.

1 2 3 4 5
import requests reponse = requests.request(url='https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv', method='GET') fichier = open('mesdonnees.csv', 'w', encoding='utf-8') fichier.write(reponse.text) fichier.close()

Et voilà. Vous pouvez aller voir, vous venez de récupérer le fichier sur votre ordinateur.

Il suffit de rajouter ceci au début du programme général et ça marche maintenant sans avoir besoin de faire des copier-coller et autres choses automatisables.

Et ça fonctionne avec toutes pages accessibles. Le module permet même de s'identifier pourvu que vous connaissiez l'identifiant et le mot de passe. Vous commencez à vous rendre compte qu'on peut récupérer TOUT ce que vous postez sur le Web ? Ca fait peur, non ?

Maintenant vous savez chercher des fichiers CSV, les charger en mémoire et les transformer en collection.

Votre collection peut être :

  • un tableau de tableaux (si tous les éléments des enregistrements sont de même type)
  • un tableau de tuples (si les éléments ne sont pas de même type)
  • un tableau de dictionnaires (si vous voulez accéder aux descripteurs par leurs noms et pas avec un numéro d'index)

Dans la partie Données, nous allons voir les algorithmes nous permettant de les parcourir, et d'y réaliser du traitement d'informations.

C'est bien beau d'avoir des informations en mémoire. Le problème c'est de les exploiter.

Activité publiée le 08 03 2020
Dernière modification : 13 12 2020
Auteur : ows. h.