Le module csv
intégré par défaut à Python permet de lire et écrire facilement des fichiers CSV. Voyons ici comment modifier un tableau de données préalablement récupéré sur une page web, avec la seconde étape de traitement du tableau des variantes pa-hng and hm nai.
Fichier d’entrée
Le fichier que j’utilise ici en entrée est le fichier de sortie du script présenté dans l’article Extraire les données d’un tableau HTML. Le script et son fichier de sortie sont disponibles sur Github.
Il s’agit d’un tableau de données au format TSV, reprenant telles quelles les données du premier tableau présenté sur cette page Wiktionary.
Pourquoi transformer ce fichier ?
Le tableau en entrée comporte 4 colonnes : Chinese gloss, Language (location), Word et Page no. La même glose en chinois se retrouve donc sur autant de lignes qu’elle a d’équivalents dans les différentes variantes présentées, soit 6 lignes au maximum. Cette représentation fonctionne bien si on souhaite comparer l’une de ces variantes au chinois mais n’est peut-être pas la plus adaptée lorsqu’il s’agit de comparer ces 6 variantes entre elles.
Dans cette perspective, la restructuration du tableau aura pour but de représenter sur une même ligne la glose en chinois, et tous ses équivalents dans les langues pa-hng et hm nai. Les étiquettes de colonnes seront donc les suivantes : Chinese gloss, Pa Hng (Guangxi Wenjie), Pa Hng (Guizhou Gundong), Hm Nai (Longhui Huxingshan), Hm Nai (Tongdao Chuansuo), Hm Nai (Longhui Xiaoshajiang) et Hm Nai (Xupu).
Le tableau est long et toutes les données dont on a besoin s’y trouvent déjà, il y a donc tout intérêt à le traiter automatiquement.
Le script Python
Aucune librairie à installer pour les besoins de ce script (écrit sous Python 3.7), seul le module csv a été utilisé.
Cet article détaille la construction du script mais ce dernier n’y est pas reproduit dans son intégralité. Le script complet et commenté (en anglais) ainsi que ses fichiers de sortie sont disponibles sur Github.
Recréer la ligne d’en-tête
Le fichier d’entrée, dont le chemin d’accès est stocké dans la variable full_table
, est lu grâce à une instance de la classe DictReader
.
reader = csv.DictReader(full_table, delimiter='\t')
La classe DictReader
, comme son nom le laisse deviner, lit les données du tableau sous forme de dictionnaire. Ce qui va permettre de constituer la nouvelle ligne d’en-tête en créant une liste alimentée par row['Language (location)']
pour chaque ligne (row
désignant la ligne courante).
languages = ['Chinese gloss']
# (...)
for row in reader:
language = row['Language (location)'].replace("]", "")
if language not in languages:
languages.append(language)
full_table.seek(0)
L’instruction full_table.seek(0)
permet de remonter au début du fichier lorsque ce traitement est terminé (les étapes de traitement suivantes ne fonctionneront pas sinon).
Pour écrire cette ligne d’en-tête dans le fichier de sortie (new_table
), on crée une instance de la classe DictWriter
, qui comme DictReader
s’appuie sur des données structurées en dictionnaire.
writer = csv.DictWriter(new_table, fieldnames=languages, delimiter='\t')
writer.writeheader()
L’objet Dictwriter
a reçu en paramètre fieldnames
la liste des langues obtenue précédemment : les éléments de cette liste peuvent maintenant être utilisés comme clés pour insérer des valeurs dans les cellules du tableau.
Remplir les lignes de données
Dans ce nouveau tableau, chaque ligne de données doit représenter le même mot dans toutes les langues où il est disponible dans le tableau d’origine.
La solution que j’ai adoptée est de créer une liste des gloses en chinois (de la même manière que la liste des langues pour la ligne d’en-tête) qui sera parcourue pour traiter chaque glose une à une. Il faut maintenant reconstituer la ligne de données correspondant à cette glose, en récupérant dans reader
(lecture des données du tableau d’origine) les valeurs qui seront transmises à writer
(écriture du tableau restructuré).
Il ne faut pas oublier que reader
et writer
fonctionnent avec des données structurées en dictionnaire. La ligne de données sera donc un dictionnaire, avec pour clés les étiquettes de la ligne d’en-tête (= les différentes langues) et pour valeurs les différents équivalents de la glose en chinois.
Concevoir la ligne de données comme un dictionnaire est alors très pratique, puisqu’il suffira de l’alimenter avec une syntaxe de type dictionnaire[langue] = mot
. Or, la langue et le mot peuvent être récupérés sans problème grâce à reader
.
Pour une ligne de données, le traitement est donc le suivant :
mini_dico = {}
for gloss in glosses:
mini_dico['Chinese gloss'] = gloss
for row in reader:
if row['Chinese gloss'] == gloss:
lang = row['Language (location)'].replace("]", "")
word = row["Word"]
mini_dico[lang] = word
writer.writerow(mini_dico)
mini_dico = {}
full_table.seek(0)
Bien sûr il ne faut pas oublier d’ajouter la ligne au fichier de sortie grâce à la méthode writerow()
(dont le nom est assez explicite). Il est aussi très important de vider le dictionnaire avant de passer à la ligne suivante (mini_dico = {}
) : si ce n’est pas fait, quand une glose n’a pas d’équivalent dans une variante donnée, la valeur précédente restera en mémoire et sera reportée dans une cellule qui aurait dû rester vide.
Le script complet et son fichier de sortie sont librement téléchargeables. Je mets aussi à disposition une autre version du tableau retravaillé, intégrant des colonnes pour les numéros de page (avec script et fichier de sortie).