Open In Colab

2.3.1. Tutoriel 3: Structure de données#

Dans cette section, nous examinerons les concepts suivants:

  1. Entrée/Sortie de fichiers (File I/O) : Comment lire et écrire des données à partir de fichiers.

  2. Listes (Lists) : Une collection ordonnée et modifiable d’éléments.

  3. Tuples : Une collection ordonnée mais immuable d’éléments.

  4. Ensembles (Sets) : Une collection non ordonnée d’éléments uniques, sans doublons.

  5. Dictionnaires (Dictionaries) : Une structure qui associe des clés uniques à des valeurs.

Références:

2.3.1.1. File I/O#

Dans cette section, nous présenterons les fonctions de base que nous pouvons utiliser pour stocker et récupérer des données à partir de fichiers de différents formats.

Pour les projets en sciences de l’environnement, les données de recherche sont le plus souvent stockées dans les formats suivants :

  1. Fichiers texte (TXT) : Utilisés pour stocker des données simples sous forme de texte brut, souvent utilisés pour la documentation ou des logs.

  2. Fichiers tabulaires (par exemple, CSV, XLS) : Permettent de structurer des données en tableaux, largement utilisés pour des géodonnées quantitatives.

  3. Données structurées / dictionnaires Python, etc. (par exemple, Pickle, dill, JSON) : Utilisés pour sérialiser et stocker des objets Python ou des données complexes dans un format structuré, facilitant la réutilisation et l’échange de données.

  4. Données maillées (par exemple, HDF5, NetCDF) : Formats optimisés pour stocker et gérer de grandes quantités de données scientifiques multidimensionnelles, souvent utilisées en climatologie ou en imagerie.

Nous allons maintenant voir comment nous pouvons utiliser Python et différents paquets Python pour récupérer les données stockées dans ces formats, et comment sauvegarder vos données dans différents formats pour une utilisation ultérieure.

Référence:

Commençons par importer quelques paquets…

import csv
import pickle
import pandas as pd
import xarray as xr
import numpy as np
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 3
      1 import csv
      2 import pickle
----> 3 import pandas as pd
      4 import xarray as xr
      5 import numpy as np

ModuleNotFoundError: No module named 'pandas'

2.3.1.1.1. Fichiers TXT#

Nous allons maintenant apprendre à écrire des informations dans un fichier .TXT et à les relire à l’aide de fonctions Python intégrées. Les données utilisées dans cette partie du tutoriel seront très simples. Dans les prochains exercices, nous présenterons également les commandes des paquets communautaires qui nous permettent de lire et de stocker des données plus complexes.

2.3.1.1.1.1. Ouverture de fichiers :#

Les fichiers peuvent être ouverts en utilisant la fonction intégrée de Python open(). Cette fonction crée un objet fichier pour les opérations suivantes. Utilisez la syntaxe suivante pour lire un fichier TXT : \ fhandler = open(file_name, access mode, encoding)

  • nom_du_fichier : Le nom du fichier sur lequel vous souhaitez effectuer vos opérations d’E/S.
    Notez qu’il s’agit du chemin d’accès complet au fichier (par exemple, \(\text{\\home\\Documents\\file.txt}\) )

  • encodage : Schéma d’encodage à utiliser pour convertir le flux d’octets en texte. (Standard=utf-8)

  • access_mode : La façon dont un fichier est ouvert, les choix disponibles pour cette option incluent :

access_mode

Its Function

r

Ouvre un fichier en lecture seule

rb

Ouvre un fichier en lecture seule au format binaire

r+

Ouvre un fichier pour la lecture et l’écriture

rb+

Ouvre un fichier pour la lecture et l’écriture au format binaire

w

Ouvre un fichier en écriture uniquement

wb

Ouvre un fichier en écriture uniquement au format binaire

w+

Ouvre un fichier en lecture et en écriture

wb+

Ouvre un fichier pour l’écriture et la lecture au format binaire

a

Ouvre un fichier pour l’ajouter

ab

Ouvre un fichier pour l’ajouter en binaire

a+

Ouvre un fichier pour l’ajout et la lecture

ab+

Ouvre un fichier pour l’ajout et la lecture au format binaire

Dans l’exemple ci-dessous, nous allons essayer de stocker plusieurs phrases dans un nouveau fichier TXT, et utiliser la fonction open() pour voir si le code fonctionne comme prévu.

fhandler = open('test.txt', 'w', encoding="utf-8")
fhandler.write('Hello World!\n')
fhandler.write('I am a UNIL Master Student.\n')
fhandler.write('I am learning how to code!\n')
fhandler.close()

Note

In the code above, we use the open() command to create a write-only (access_mode='w') file test.txt. The open command creates a file object (fhandler) on which we can perform extra operations.

We then try to add three sentences to the TXT file using the .write() operation on the file object.

Remember to close the file with .close() command so that the changes can be finalized!

If the code is writing, we should see a test.txt file created in the same path as this notebook. Let’s see if that’s the case!

💡 Rappel: ! vous permet d’accéder aux commandes “shell” 💡

! ls .
! cat test.txt

Hourra ! Ça marche ! 😀

Mais n’avons-nous pas dit que nous voulions le relire ? 🤨

Essayons de lire le fichier alors ! Pouvez-vous penser à des façons de le faire ?

Voici quelques-unes des fonctions que vous pourriez utiliser.

  1. .close() : Ferme le fichier actuellement ouvert.

  2. .readline([size]) : Lit les chaînes de caractères d’un fichier jusqu’à ce qu’il atteigne le caractère de nouvelle ligne \n si le paramètre size est vide. Sinon, il lira la chaîne de caractères de la taille donnée.

  3. .readlines([size]) : Appelle répétitivement .readline() jusqu’à la fin du fichier.

  4. .write(str) : Écrit la chaîne de caractères str dans le fichier.

  5. .writelines([list]) : Ecrit une séquence de chaînes de caractères dans un fichier. Aucune nouvelle ligne n’est ajoutée automatiquement.

fhandler = open('test.txt','r',encoding='utf-8')
fhandler.readlines()

Et si nous voulions ajouter du texte au fichier ?

with open('test.txt', 'r+') as fhandler:
  print(fhandler.readlines())
  fhandler.writelines(['Now,\n', 'I am trying to', ' add some stuff.'])
  # Go to the starting of file
  fhandler.seek(0)
  # Print the content of file
  print(fhandler.readlines())

Ici, nous utilisons une autre méthode pour ouvrir et écrire le fichier de données. En utilisant l’instruction with pour ouvrir le fichier TXT, nous nous assurons que les données sont automatiquement fermées après l’opération finale. Nous n’avons plus besoin d’écrire l’instruction fhandler.close().

2.3.1.1.2. Fichiers tabulaires#

Que feriez-vous si vous aviez des données joliment organisées dans le format ci-dessous ?

Données1, Données2, Données3
Exemple01, Exemple02, Exemple03
Exemple11, Exemple12, Exemple13

Lorsque vous ouvrez un fichier de ce type dans Excel, voici à quoi il ressemble :

Donnée1

Donnée2

Donnée3

Exemple1

Exemple2

Exemple3

Il s’agit d’un fichier tabulaire séparé par des virgules. Les fichiers de ce type sont généralement enregistrés avec l’extension .csv. Les fichiers .csv peuvent ensuite être ouverts et visualisés à l’aide d’un tableur, tel que Google Sheets, Numbers ou Microsoft Excel.

Mais qu’en est-il si nous voulons utiliser les données dans Python ?

2.3.1.1.2.1. Ouverture des fichiers :#

Heureusement, il existe des paquets communautaires qui peuvent vous aider à importer et à récupérer vos données tabulaires avec un minimum d’effort. Nous présentons ici deux de ces packages : CSV et Pandas.

2.3.1.1.2.1.1. Lire des fichiers CSV avec le paquetage CSV#

reader() peut être utilisé pour créer un objet qui est utilisé pour lire les données d’un fichier CSV. Le lecteur peut être utilisé comme un itérateur pour traiter les lignes du fichier dans l’ordre. Voyons un exemple :

import pooch
import urllib.request
datafile = pooch.retrieve('https://unils-my.sharepoint.com/:x:/g/personal/tom_beucler_unil_ch/ETDZdgCkWbZLiv_LP6HKCOAB2NP7H0tUTLlP_stknqQHGw?download=1',
                          known_hash=None)
row = []
# https://unils-my.sharepoint.com/:x:/g/personal/tom_beucler_unil_ch/ETDZdgCkWbZLiv_LP6HKCOAB2NP7H0tUTLlP_stknqQHGw?e=N541Yq
with open(datafile, 'r') as fh:
  reader = csv.reader(fh)
  for info in reader:
    row.append(info)
print(row[0])
print(row[1])

``{tip} Dans le code ci-dessus, nous utilisons la méthode csv.reader() pour lire itérativement chaque ligne du fichier CSV.

Nous ajoutons une nouvelle ligne à une liste vide à chaque itération.

Nous utilisons la fonction print() pour voir ce qui a été écrit dans la liste. Nous avons constaté que la première ligne contient des informations sur les noms de variables, tandis que la deuxième ligne contient des données à un pas de temps donné.


#### Extraire les données et les écrire dans un nouveau fichier CSV :
Le fichier CSV que nous venons d'importer contient en fait les données des stations météorologiques de janvier 2022 à août 2022. Que se passe-t-il si nous ne voulons que les données des cinq premières lignes ? Pouvons-nous extraire les données et les enregistrer dans un nouveau fichier CSV ?
with open('testsmall.csv', 'w') as fh:
  writer = csv.writer(fh)
  for num in range(5):
    writer.writerow(row[num])

Note

En fait, il existe un meilleur paquetage pour les données tabulaires. Cette bibliothèque s’appelle Pandas. Nous présenterons ce paquetage plus en détail la semaine prochaine. Pour l’instant, nous allons simplement démontrer que nous pouvons utiliser pandas pour faire la même procédure FileI/O que nous avons faite plus tôt avec CSV.

Ici, nous lisons la grande feuille de données de la station météorologique datafile avec la fonction pandas .read_csv().

# importer fichier CSV avec pandas
ALOdatasheet = pd.read_csv(datafile)
# Exporter les cinq premières lignes du cadre de données Pandas vers un fichier CSV
ALOdatasheet[0:5].to_csv('./testsmall_pd.csv')

2.3.1.1.3. Sérialisation et désérialisation avec Pickle#

(Réécrit à partir du GSFC Python Bootcamp)

Pickle est un format interne de Python qui permet d’écrire des données arbitraires dans un fichier de manière à pouvoir les relire, intactes.

  • pickle “sérialise” d’abord l’objet avant de l’écrire dans un fichier.

  • Le décapage (sérialisation) est un moyen de convertir un objet Python (liste, dict, etc.) en un flux de caractères qui contient toutes les informations nécessaires pour reconstruire l’objet dans un autre script Python.

Les types suivants peuvent être sérialisés et désérialisés en utilisant le module pickle :

  • Tous les types de données natifs supportés par Python (booléens, None, entiers, flottants, nombres complexes, chaînes de caractères, octets, tableaux d’octets).

  • Dictionnaires, ensembles, listes et tuples - tant qu’ils contiennent des objets sélectionnables.

  • Les fonctions (décryptées par leur nom de référence, et non par leur valeur) et les classes qui sont définies au niveau supérieur d’un module.

Les fonctions principales de pickle sont :

  • dump() : récupère des données en acceptant des données et un objet fichier.

  • load() : prend un objet fichier, reconstruit les objets à partir de la représentation décapée, et les renvoie.

  • dumps() : renvoie les données décryptées sous forme de chaîne de caractères.

  • loads() : lit les données extraites d’une chaîne.

dump()/load() sérialise/désérialise les objets à travers des fichiers mais dumps()/loads() sérialise/désérialise les objets à travers une représentation sous forme de chaîne de caractères.

# Exemple de dictionnaire Python
data_org = { 'mydata1':np.linspace(0,800,801), 'mydata2':np.linspace(0,60,61)}
# Enregistrer un dictionnaire Python dans un fichier pickle
with open('pickledict_sample.pkl', 'wb') as fid:
     pickle.dump(data_org, fid)
# Deserialize saved pickle file
with open('pickledict_sample.pkl', 'rb') as fid:
     data3 = pickle.load(fid)
for strg in data_org.keys():
  print(f"Variable {strg} is the same in data_org and data3: {(data_org[strg]==data3[strg]).all()}")

2.3.1.2. Lists#

Une liste est une structure de données qui stocke plusieurs éléments dans un ordre spécifique. Les listes sont mutables, ce qui signifie que leur contenu peut être modifié après leur création. Elles sont très utilisées pour :

  • Stocker plusieurs valeurs dans une séquence.

  • Accéder à des éléments via leur index.

  • Modifier des éléments (ajout, suppression, changement).

  • Itérer sur une collection d’éléments.

La création de listes est très facile en Python. Nous pouvons créer des listes en séparant les différents éléments par des virgules entre crochets:[Item1,Item2,Item3]. [Élément1,Élément2,Élément3]

Il existe de nombreuses façons d’interagir avec les listes. Les explorer fait partie du plaisir de Python.

list.append(x) Ajoute un élément à la fin de la liste. Equivalent à a[len(a) :] = [x].

list.extend(L) Étendre la liste en y ajoutant tous les éléments de la liste donnée. Equivalent à a[len(a) :] = L.

list.insert(i, x) Insère un élément à une position donnée. Le premier argument est l’indice de l’élément avant lequel insérer, donc a.insert(0, x) insère en début de liste et a.insert(len(a), x) insère en fin de liste et est équivalent à a.append(x).

list.remove(x) Supprime le premier élément de la liste dont la valeur est x. C’est une erreur s’il n’y a pas d’élément de ce type.

list.pop([i]) Enlève l’élément à la position donnée dans la liste et le renvoie. Si aucun index n’est spécifié, a.pop() supprime et renvoie le dernier élément de la liste. (Les crochets autour du i dans la signature de la méthode indiquent que le paramètre est optionnel, et non pas que vous devez le retourner). est optionnel, et non pas que vous devez taper des crochets à cette position. à cette position. Vous verrez souvent cette notation dans la référence de la bibliothèque Python).

list.clear() Supprime tous les éléments de la liste. Equivalent à del a[ :].

list.index(x) Retourne l’index dans la liste du premier élément dont la valeur est x. C’est une erreur s’il n’y a pas d’élément de ce type.

list.count(x) Retourne le nombre de fois que x apparaît dans la liste.

list.sort() Trie les éléments de la liste à la place.

list.reverse() Inverse les éléments de la liste en place.

list.copy() Retourne une copie superficielle de la liste. Equivalent à a[ :].

# Expérimentons quelques-unes de ces méthodes !
# 1. Créer d'abord une liste
l = ['chien', 'chat', 'poisson', 'poulet', 'oeufs', 'canard']
type(l)
# Découpage de la liste
print(l[0],l[-1],l[0:3],l[-3:-1])

Pensez à mémoriser cette syntaxe ! Elle est au cœur de Python et s’avère souvent déroutante pour les utilisateurs qui viennent d’autres langages.

2.3.1.2.1. Mise à jour de la liste#

Contrairement aux chaînes et aux tuples, les listes sont mutables. Vous pouvez mettre à jour la liste et modifier les éléments quand vous le souhaitez.

my_list = ['chien', 'chat', 'poisson', 'poulet', 'œufs', 'canard']
my_list[2] = 'Arbre'
print('Contenu original : \n', my_list)
print('Longueur originale du tableau : \n', len(my_list))

# Enlever quelques éléments/changer la taille
my_list[2:4] = []
print('Contenu modifié : \n', my_list)
print('Longueur modifiée du tableau : \n', len(my_list))

Modifions la liste à l’aide des méthodes spécifiques aux listes dont nous avons parlé précédemment.

#@title #### **Ajouter de nouveaux éléments à la liste**
my_list = ['chien', 'chat', 'poisson', 'poulet', 'oeufs', 'canard']
my_list.append('Python') # Cela ajoutera l'élément à la fin de la liste
print(my_list)
my_list.insert(0, 'Julia')
print(my_list)
#@title #### **Supprimez des éléments de la liste**
my_list.pop(0)
print(my_list)
del(my_list[-1])
print(my_list)
#@title #### **Compte, Index**
print(my_list.count('poisson'))
print(my_list.index('canard'))
#@title #### **Tri, Inversion**
my_list = ['chien', 'chat', 'poisson', 'poulet', 'oeufs', 'canard']
my_list.reverse()
print(my_list)
my_list.sort()
print(my_list)
my_list.sort(reverse=True)
print(my_list)
#@title #### **Concaténation de listes**
my_list = ['chien', 'chat', 'poisson', 'poulet', 'oeufs', 'canard']
my_list2 = ['Python', 'Julia', 'C++']
print(my_list+my_list2)
my_list.extend(my_list2)
print(my_list)

2.3.1.2.2. Utiliser des listes en boucle#

for index in range(len(my_list)) : # commence à 0 et va jusqu'à la longueur de la liste.
    print("my_list[{}] : {}".format(index, my_list[index]))
# Nous pouvons en fait faire la même chose avec enumerate()
for index,items in enumerate(my_list) :
  print("my_list[{}] : {}".format(index,items))

Compréhension de liste

La compréhension de liste est un moyen syntaxique de créer une liste basée sur la liste existante, comme nous l’avons fait en copiant les listes ci-dessus. La structure de base de la syntaxe comprend une boucle for qui parcourt la liste et évalue une condition à l’aide de la condition if… else. Elle stocke ensuite la sortie de la condition dans une nouvelle liste. Voyons un exemple rapide :

my_list1 = [elem for index,elem in enumerate(my_list) if index % 2 == 0]
print(my_list1)

carrés = [n**2 for n in range(5)]
print(carrés)

Traverser deux listes ensemble avec zip()

# iterate over two lists together uzing zip
for item1, item2 in zip(['cochon','canard','papillon'],[0,1,2]):
    print('first:', item1, 'second:', item2)

2.3.1.3. Tuples#

Les tuples sont similaires aux listes, mais ils sont immuables, c’est-à-dire qu’ils ne peuvent pas être étendus ou modifiés. Quel en est l’intérêt ? D’une manière générale, il s’agit de regrouper des données non homogènes. Les tuples peuvent ensuite être décompressés et distribués par d’autres parties de votre code.

Les tuples peuvent sembler déroutants au début, mais avec le temps, vous finirez par les apprécier.

# les tuples sont créés avec des parenthèses, ou simplement des virgules
a = ('Ryan', 33, True)
b = 'Takaya', 25, False
type(b)
# peut être indexé comme un tableau
print(a[1]) # pas le premier élément !
# et ils peuvent être décompressés
nom, age, statut = a
print(nom,age,statut)
print(a.index('Ryan'))

2.3.1.4. Sets#

Un ensemble (set) est une structure de données non ordonnée qui ne contient que des éléments uniques, ce qui signifie qu’il ne peut pas y avoir de doublons. Contrairement aux listes et aux tuples, un ensemble ne conserve pas l’ordre des éléments et ne permet pas d’accéder directement à un élément via un index.

2.3.1.4.1. Pourquoi utiliser un ensemble ?#

Les ensembles sont particulièrement utiles lorsque :

  • Vous souhaitez éliminer les doublons dans une collection.

  • Vous avez besoin de faire des opérations d’ensemble comme l’union, l’intersection ou la différence entre plusieurs collections.

Création d’un ensemble:

# Création d'un ensemble vide
ensemble_vide = set()

# Création d'un ensemble avec des éléments
mon_ensemble = {1, 2, 3, 4, 5}

Ajout d’éléments:

# Ajout d'un élément à un ensemble
mon_ensemble.add(6)

# Ajout de plusieurs éléments à un ensemble
mon_ensemble.update({7, 8, 9})

Suppression d’éléments:

# Suppression d'un élément spécifique
mon_ensemble.remove(3)

# Suppression d'un élément sans générer d'erreur si l'élément n'est pas présent
mon_ensemble.discard(10)

Opérations ensemblistes:

# Union de deux ensembles
ensemble1 = {1, 2, 3}
ensemble2 = {3, 4, 5}
union_resultat = ensemble1.union(ensemble2)

# Intersection de deux ensembles
intersection_resultat = ensemble1.intersection(ensemble2)

# Différence entre deux ensembles
difference_resultat = ensemble1.difference(ensemble2)

Vérification d’appartenance:

# Vérification si un élément appartient à l'ensemble
appartient = 2 in ensemble1

Parcours d’un ensemble:

# Parcours des éléments de l'ensemble
for element in mon_ensemble:
    print(element)

Conversion vers un ensemble:

# Conversion d'une liste en ensemble
liste = [1, 2, 3, 4, 5]
ensemble_a_partir_de_liste = set(liste)

La capacité à gérer des données non ordonnées et à éliminer les doublons les rend utiles dans de nombreuses situations.

2.3.1.5. Dictionnaires#

Un dictionnaire est une structure de données qui associe des clés à des valeurs, permettant un accès rapide aux données en fonction de la clé. Contrairement aux listes, les dictionnaires ne sont pas ordonnés avant Python 3.7 (depuis, l’ordre d’insertion est conservé), et chaque clé dans un dictionnaire est unique.

2.3.1.5.1. Pourquoi utiliser un dictionnaire ?#

Les dictionnaires sont extrêmement utiles pour :

  • Associer des informations : Par exemple, relier un nom (clé) à un numéro de téléphone (valeur).

  • Rechercher rapidement des valeurs en fonction de leurs clés, ce qui est beaucoup plus rapide que de parcourir une liste.

#@title #### **Différentes façons de créer des dictionnaires**
d = {'nom' : 'Timothee', 'age' : 1} # Écrivez votre nom et votre âge ici
e = dict(nom='Timothee', age=1) # Inscrivez le nom et l'âge de quelqu'un d'autre ici
#@title #### **Accéder aux éléments du dictionnaire**
print(d['nom'])
print("nom : ", d.get('nom' , 'Pas trouvé'))
print("sexe :   ", d.get('gender', 'Pas trouvé'))
#@title #### **Mise à jour du dictionnaire**
# Ajouter une nouvelle paire variable-valeur :
d['taille'] = (1,50) # un tuple, par exemple votre taille en (mètres,centimètres)
d
#@title #### **Ajouter un dictionnaire**
# update() ajoute deux dictionnaires ensemble
newdict = dict(lieu='Lausanne',nationalite='CH',annee_naissance='2021')
d.update(newdict)
print(d)
#@title #### **Supprimer les éléments du dictionnaire**
# pop(),del()
d2 = d.copy()
d2.pop('age')
del d['age']
print(f"Identique ? {d==d2}")
#@title #### **Dictionnaire transversal**
# itérer sur les clés
for k in d :
    print(k, d[k])
print(list(d.keys()))
print(list(d.values()))
print(list(d.items()))
#@title #### **Triez le dictionnaire**
# sorted()
states_dict = {'AL' : 'Alabama', 'CA' : 'California',
               'NJ' : 'New Jersey', 'NY' : 'New York'}
sorted_keys = sorted(list(states_dict.keys()), reverse=False)
for key in sorted_keys :
    print('{} : {}'.format(key, states_dict[key]))