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,datedans un fichier etname,date,emaildans 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§namen'est pasname. 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 avez | Vous voulez | Opération |
|---|---|---|
| Douze exports mensuels, mêmes colonnes | Un fichier, plus de lignes | Ajout (empiler) |
| Des clients dans un fichier, des commandes dans un autre | Un fichier, plus de colonnes | Jointure (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.
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 faitAu-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.
- 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 *.csvavant,wc -l merged.csvaprès. - 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. - 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.
- 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.
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 faitL'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.