mygems

Comment fusionner plusieurs fichiers CSV en un seul

La concaténation aligne les colonnes par position : un en-tête dans un autre ordre fausse tout en silence. Fusionnez par nom de colonne, puis vérifiez.

Par uos ·

Si les fichiers ont des colonnes identiques dans le même ordre, la fusion correcte la plus rapide est une commande shell d'une ligne qui garde le premier en-tête et supprime les autres :

head -n 1 first.csv > merged.csv
tail -q -n +2 *.csv >> merged.csv

Tout le reste de cette page existe parce que cette hypothèse de colonnes identiques dans le même ordre est fausse bien plus souvent qu'on ne le croit, et l'échec est silencieux. Une fusion qui décale deux colonnes produit un fichier qui s'ouvre sans problème, a l'air correct, et est faux.

D'abord, vérifier que les en-têtes correspondent

Faites-le avant la fusion, pas après. Tous les problèmes qui suivent sont des variantes de celui-ci.

for f in *.csv; do echo "$f: $(head -n 1 "$f")"; done

Cherchez quatre choses dans le résultat :

  • Un ordre de colonnes différent. name,email,date dans un fichier et name,date,email dans un autre. La concaténation met des dates dans la colonne e-mail, et rien ne proteste.
  • Des noms différents pour le même champ. Email, email, E-mail, Email Address. Fréquent quand les fichiers viennent d'exports différents, ou du même export à des années différentes.
  • Des colonnes en plus ou en moins. Un export plus récent a gagné un champ. Les lignes n'ont plus la même largeur.
  • Une marque d'ordre des octets (BOM) sur le premier fichier. §BOM§name n'est pas name. Elle est invisible dans tous les éditeurs et casse les jointures par nom de colonne.

Si tous les en-têtes sont identiques à l'octet près, utilisez la version en deux lignes ci-dessus et arrêtez de lire. Sinon, la concaténation est le mauvais outil et il vous faut quelque chose qui fusionne par nom de colonne.

Fusionner par nom de colonne

DuckDB, si les fichiers sont gros ou si les en-têtes diffèrent

Une seule commande règle les deux problèmes, ordre de colonnes différent et colonnes manquantes, parce qu'elle aligne sur le nom et non sur la position :

duckdb -c "COPY (SELECT * FROM read_csv('*.csv', union_by_name=true, filename=true)) TO 'merged.csv' (HEADER)"

union_by_name=true fait le gros du travail : les colonnes sont associées par en-tête, et un fichier auquel il manque une colonne reçoit NULL au lieu d'une ligne décalée. filename=true ajoute une colonne indiquant de quel fichier vient chaque ligne, dont vous aurez besoin plus souvent que prévu. C'est le seul moyen de savoir, après coup, dans quel export une ligne erronée est arrivée.

DuckDB lit en flux, donc la taille des fichiers est limitée par le disque et non par la RAM. Cent fichiers totalisant plusieurs gigaoctets n'ont rien d'étonnant.

Python, si vous devez d'abord harmoniser les noms

Quand le même champ s'appelle Email dans un fichier et E-mail Address dans un autre, aucun outil ne peut deviner. Normalisez explicitement :

import glob, pandas as pd

rename = {"E-mail Address": "email", "Email": "email"}
frames = []
for path in glob.glob("*.csv"):
    df = pd.read_csv(path, dtype=str)
    df.columns = [c.strip().lstrip("§BOM§") for c in df.columns]
    frames.append(df.rename(columns=rename).assign(source=path))

pd.concat(frames, ignore_index=True).to_csv("merged.csv", index=False)

Deux détails comptent plus que la fusion elle-même. dtype=str empêche pandas de deviner les types fichier par fichier. Sans cela, une colonne d'identifiants clients numérique dans un fichier et alphanumérique dans un autre donne un mélange de 1024 et de "01024", et les zéros de tête sont perdus pour de bon. Et .assign(source=path) est la même colonne de provenance que DuckDB vous donne gratuitement.

Power Query, sur un poste de bureau sans terminal

Dans Excel, Données → Obtenir des données → À partir d'un dossier pointe vers un répertoire et combine tous les fichiers qu'il contient, en associant les en-têtes. Il le fait bien, il se rafraîchit quand de nouveaux fichiers arrivent dans le dossier, et c'est la bonne réponse pour qui répétera cette fusion chaque mois.

Sa limite est celle d'Excel : le résultat fusionné doit tenir en 1 048 576 lignes pour arriver sur une feuille. Vous pouvez charger dans le modèle de données à la place et vous passer complètement de la feuille, ce que la plupart des gens ignorent et qui relève nettement le plafond.

Ajouter ou joindre : la différence

« Fusionner » recouvre deux opérations différentes, et choisir la mauvaise fait perdre un après-midi.

Vous avezVous voulezOpération
Douze exports mensuels, mêmes colonnesUn fichier, plus de lignesAjout (empiler)
Des clients dans un fichier, des commandes dans un autreUn fichier, plus de colonnesJointure (rapprocher sur une clé)

Tout ce qui précède est de l'ajout. Une jointure demande une clé commune et une décision sur le sort des lignes sans correspondance, et c'est là que naissent les doublons : une clé qui n'est pas unique des deux côtés multiplie les lignes sans bruit. Si le nombre de lignes après une jointure dépasse celui de chaque entrée, c'est ce qui s'est passé.

Pour une jointure, le SQL est l'outil honnête :

duckdb -c "COPY (SELECT c.*, o.order_total FROM read_csv('customers.csv') c LEFT JOIN read_csv('orders.csv') o USING (customer_id)) TO 'merged.csv' (HEADER)"

Sur un téléphone

Le scénario réaliste sur téléphone : une poignée de fichiers dans Mail ou Fichiers, trois exports régionaux ou quelques mois de relevés, et pas d'ordinateur portable avant lundi.

Deux choses aident ici. Ajouter des fichiers aux en-têtes identiques revient à un copier-coller, à condition que l'app sache coller un bloc de lignes plutôt qu'une cellule à la fois. Et la vérification qui compte, comparer les en-têtes, est plus rapide sur un téléphone qu'ailleurs : ouvrir chaque fichier, lire la première ligne.

CSV Editor - Smart CSViPhone et iPad

Ouvre chaque export en quelques secondes, colle une plage de lignes dans une autre feuille et réécrit chaque ligne à l'enregistrement, y compris dans la version gratuite. Grâce à l'insertion de colonnes et de lignes, réconcilier un fichier qui a gagné un champ est une vraie modification, pas un contournement.

Ce que ça fait

Au-delà d'une poignée de fichiers, attendez d'être sur un ordinateur. Fusionner trente exports à la main sur un écran tactile n'est pas une bonne idée, et la raison tient au taux d'erreur plus qu'à la corvée.

Après la fusion, quatre vérifications

Faites-les à chaque fois. Elles prennent une minute et attrapent pratiquement tous les échecs de fusion qui comptent.

  1. Nombre de lignes. Le total fusionné doit être la somme des lignes de chaque entrée, moins un en-tête par fichier. wc -l *.csv avant, wc -l merged.csv après.
  2. Nombre d'en-têtes. grep -c "^name," merged.csv, ou quelle que soit la première colonne. Plus d'un signifie qu'un en-tête s'est retrouvé en ligne de données.
  3. Largeur des colonnes. Chaque ligne doit avoir le même nombre de champs. Des largeurs inégales indiquent une virgule non protégée quelque part, pas un bug de fusion.
  4. Contrôler la jonction. Regardez la dernière ligne du premier fichier et la première ligne du second dans le résultat. Un décalage s'y voit d'abord.

La deuxième vérification a une subtilité : si une ligne d'en-tête se retrouve dans les données, le tri la dispersera au milieu du fichier et elle devient très difficile à retrouver. Attrapez-la maintenant.

CSV File Viewer - Smart CSViPhone et iPad · Android

Exécute du SQL directement sur le fichier fusionné : un GROUP BY sur la colonne source montre que chaque fichier a bien apporté le nombre de lignes attendu, toute la vérification en une requête.

Ce que ça fait

L'encodage, l'échec qui survit à toutes les vérifications

Les fichiers de systèmes différents sont souvent dans des encodages différents, et la concaténation ne convertit rien. Un fichier UTF-8 ajouté à un fichier Windows-1252 donne un fichier qui n'est ni l'un ni l'autre : les noms s'affichent bien dans la première moitié et sous la forme é dans la seconde.

Convertissez avant la fusion, pas après :

iconv -f WINDOWS-1252 -t UTF-8 old.csv > old-utf8.csv

file -I *.csv devinera ce que vous avez, mais il devine mal sur les fichiers courts. Le moyen fiable est d'ouvrir chaque fichier et de regarder une ligne contenant un caractère non ASCII.

Questions

Comment fusionner plusieurs fichiers CSV en un seul ?
Si tous les fichiers ont des en-têtes identiques à l'octet près, gardez le premier en-tête et ajoutez le reste : `head -n 1 first.csv > merged.csv` puis `tail -q -n +2 *.csv >> merged.csv`. Si les en-têtes diffèrent par l'ordre ou le contenu, la concaténation décale les colonnes sans rien dire. Utilisez alors un outil qui associe par nom de colonne, comme read_csv de DuckDB avec union_by_name=true, ou « À partir d'un dossier » d'Excel.
Pourquoi mon CSV fusionné a-t-il des colonnes au mauvais endroit ?
Parce que la concaténation associe par position, pas par nom. Deux fichiers avec les mêmes colonnes dans un ordre différent donnent un fichier aux valeurs décalées, sans aucune erreur. Comparez toujours les lignes d'en-tête avant de fusionner : `for f in *.csv; do echo "$f: $(head -n 1 "$f")"; done`.
Comment fusionner des fichiers CSV qui n'ont pas les mêmes colonnes ?
Associez par nom de colonne et comblez les trous. DuckDB le fait en une commande avec `union_by_name=true`, en donnant NULL pour une colonne absente d'un fichier. Dans pandas, `pd.concat` sur des tableaux lus avec `dtype=str` fait pareil. Les champs identiques sous des noms différents, Email et E-mail Address, doivent d'abord être renommés explicitement : aucun outil ne peut le deviner.
Comment savoir de quel fichier vient chaque ligne du CSV fusionné ?
Ajoutez une colonne de provenance pendant la fusion. read_csv de DuckDB accepte `filename=true` et l'ajoute automatiquement ; dans pandas, `.assign(source=path)` pour chaque tableau fait la même chose. C'est le seul moyen de remonter d'une ligne erronée à son export, et la vérification se résume à un seul GROUP BY.
Comment vérifier qu'une fusion de CSV a réussi ?
Quatre vérifications : le nombre de lignes fusionnées égale la somme des entrées moins un en-tête par fichier ; la chaîne d'en-tête apparaît exactement une fois ; chaque ligne a le même nombre de champs ; et la jonction entre le premier et le second fichier est correcte. Un en-tête devenu ligne de données est l'échec à attraper tôt, car le tri le dispersera au milieu du fichier.
Pourquoi les caractères accentués sont-ils cassés dans la moitié de mon fichier fusionné ?
Les entrées avaient des encodages différents, et l'ajout ne convertit pas. Un fichier Windows-1252 ajouté à un fichier UTF-8 donne des noms corrects dans une moitié et du mojibake dans l'autre. Convertissez d'abord tout en UTF-8 avec `iconv -f WINDOWS-1252 -t UTF-8 old.csv > old-utf8.csv`, puis fusionnez.

uos — Développe CSV Editor et Smart CSV Viewer, et a vu une fusion décaler une colonne d'un cran sur 200 000 lignes sans la moindre erreur.