join multi-fichiers CSV
Récemment, j’ai dû assembler le contenu de plusieurs fichiers CSV en effectuant une jointure sur une clé unique pour chaque ligne.
Sommaire
Contexte
Les fichiers se composent de 2 colonnes. La première est une clé et la seconde une valeur quelconque. Les clés ne sont pas toutes présentes dans chaque fichier. Les lignes de ces fichiers peuvent être triées par ordre alphabétique sur la clé.
Pour information, le séparateur de champ utilisé est la virgule. De plus, les fichier ne contiennent pas de point virgule (ce qui se révélera pratique plus tard).
Voici les fichier utilisés (et simplifiés) pour les tests :
$ cat fichier1.csv 001,Black Lotus 002,Ancestral Recall 003,Counterspell 005,Island 006,Swamp 007,Mana Drain 008,Boomerang $ cat fichier2.csv 003,Countresort 004,Plaine 005,Île 006,Marais $ cat fichier3.csv 003,Gegenzauber 004,Ebene $ cat fichier4.csv 004,Pianura 007,Risucchia Potere $ cat fichier5.csv 008,Búmerang $ cat resultat.csv 001,Black Lotus,-,-,-,- 002,Ancestral Recall,-,-,-,- 003,Counterspell,Contresort,Gegenzauber,-,- 004,-,Plaine,Ebene,Pianura,- 005,Island,Île,-,-,- 006,Swamp,Marais,-,-,- 007,Mana Drain,-,-,Risucchia Potere,- 008,Boomerang,-,-,-,Búmerang
Solution
Jointure sur 2 fichiers
Pour cela, il existe un programme qui se nomme tout simplement join
:
join -e- -t, -a1 -a2 -j 1 -o 0,1.2,2.2 fichier1.csv fichier2.csv > resultat.csv
Cette commande permet de remplir les cases vides avec un tiret. Le format de sortie utilise le champ "0"
afin de gérer le cas où le premier fichier d’entrée ne contienne pas toutes les clés.
Jointure sur 5 fichiers
A partir de la commande précédente, il est assez facile de réaliser une jointure sur 5 fichiers :
join -e- -t, -a1 -a2 -j 1 -o 0,1.2,2.2 fichier1.csv fichier2.csv > tmp1.csv join -e- -t, -a1 -a2 -j 1 -o 0,1.2,1.3,2.2 tmp1.csv fichier3.csv > tmp2.csv join -e- -t, -a1 -a2 -j 1 -o 0,1.2,1.3,1.4,2.2 tmp2.csv fichier4.csv > tmp3.csv join -e- -t, -a1 -a2 -j 1 -o 0,1.2,1.3,1.4,1.5,2.2 tmp3.csv fichier5.csv > resultat.csv
Même si cette méthode est fonctionnelle, on sent que ce n’est pas très élégant. De plus, si l’on souhaite proposer un script acceptant un nombre variable de fichiers, il faut revoir la méthode.
Jointure sur N fichiers
Afin de pouvoir faire une jointure sur un nombre variable de fichiers, il faut écrire un script bash acceptant un nombre variable d’arguments.
De plus, le script va transformer les fichiers CSV temporaires afin de n’avoir toujours que 2 colonnes. Cette astuce va permettre de n’avoir qu’un seul et unique format de sortie pour join
. Le résultat final sera transformé pour rétablir les N+1 colonnes.
Voici à quoi ressemble le script multijoin.sh
:
#!/bin/bash # load first file for join LEFT_FILE=$1 shift # iterate over all the remaining files to join while (( "$#" )); do RIGHT_FILE=$1 JOINED_FILE="out${#}.csv" TMP_FILES+=("${JOINED_FILE}") join -e- -t, -a1 -a2 -j 1 -o 0,1.2,2.2 "${LEFT_FILE}" "${RIGHT_FILE}" | sed 's/,/;/g;s/;/,/' > "${JOINED_FILE}" LEFT_FILE=$JOINED_FILE shift done # render the result sed 's/;/,/g' "$JOINED_FILE" # remove all the temporary files for TMP_FILE in "${TMP_FILES[@]}"; do rm -f "${TMP_FILE}" done
Il y a 2 astuces dans ce script :
- Il boucle sur les arguments en prenant toujours le premier et en le supprimant avec la commande
shift
. - Dans les fichiers temporaires, tous les séparateurs
","
sont remplacés par un";"
puis le premier";"
est repositionné avec une","
. A la fin, tous les";"
sont remplacés par des","
. Cela permet de n’avoir toujours que 2 colonnes dans les fichiers temporaires (outX.csv
).
Le script s’appelle de cette façon :
$ multijoin fichier1.csv fichier2.csv fichier3.csv fichier4.csv fichier5.csv > resultat.csv
Conclusion
Le script a quelques limitations :
- le script a toutes les contraintes de la commande
join
- la clé doit être dans la première colonne
- les fichiers d’entrée ne peuvent contenir que 2 colonnes (même s’il est possible d’outre passer cette limitation)
- le séparateur de champs doit être la virgule et les fichiers ne doivent pas contenir de point virgule (cette limitation peut également être sautée avec un peu de code)
Le cas n’a pas été rencontré avec les fichiers de données manipulés mais il y a également un bug : si les X premiers fichiers ne contiennent pas une clé, alors il manquera les X-1 premières cellules pour la ligne de la clé en question… 🙁
Néanmoins, cet exemple montre qu’avec des commandes de base et un peu d’astuce, il est possible de mettre en place des traitements qui seraient parfois implémentés avec des outils bien plus lourds ! 😉
https://blog.lecacheur.com/2017/06/20/join-multi-fichiers-csv/https://blog.lecacheur.com/wp-content/uploads/2015/04/bash-148836_640.pnghttps://blog.lecacheur.com/wp-content/uploads/2015/04/bash-148836_640-150x150.pngDéveloppementbash,csvRécemment, j'ai dû assembler le contenu de plusieurs fichiers CSV en effectuant une jointure sur une clé unique pour chaque ligne. Contexte Les fichiers se composent de 2 colonnes. La première est une clé et la seconde une valeur quelconque. Les clés ne sont pas toutes présentes dans chaque fichier. Les...SeBSébastien LECACHEUR23r0@laposte.netAdministratorLe weblogue de SeB
Laisser un commentaire