Si los archivos tienen columnas idénticas en el mismo orden, la forma correcta más rápida de combinarlos es una línea de shell que conserva el primer encabezado y descarta los demás:
head -n 1 first.csv > merged.csv
tail -q -n +2 *.csv >> merged.csv
Todo lo demás en esta página existe porque esa suposición de columnas idénticas en el mismo orden es falsa mucho más a menudo de lo que la gente espera, y el fallo es silencioso. Una combinación que desalinea dos columnas produce un archivo que se abre bien, se ve bien y está mal.
Primero, comprueba si los encabezados coinciden
Hazlo antes de combinar, no después. Cada problema que sigue es una variación de este.
for f in *.csv; do echo "$f: $(head -n 1 "$f")"; done
Busca cuatro cosas en la salida:
- Distinto orden de columnas.
name,email,dateen un archivo yname,date,emailen otro. Al concatenar, las fechas acaban en la columna de correo y nada se queja. - Nombres distintos para el mismo campo.
Email,email,E-mail,Email Address. Es habitual cuando los archivos vienen de exportaciones distintas, o de la misma exportación en años distintos. - Columnas de más o de menos. Una exportación posterior ganó un campo. Ahora las filas tienen anchos distintos.
- Una marca de orden de bytes (BOM) en el primer archivo.
§BOM§nameno esname. Es invisible en cualquier editor y rompe los cruces por nombre de columna.
Si todos los encabezados son idénticos byte a byte, usa la versión de dos líneas de arriba y deja de leer. Si no, concatenar es la herramienta equivocada y necesitas algo que combine por nombre de columna.
Combinar por nombre de columna
DuckDB, si los archivos son grandes o los encabezados difieren
Un solo comando resuelve los dos problemas, el orden distinto de columnas y las columnas que faltan, porque alinea por nombre y no por posición:
duckdb -c "COPY (SELECT * FROM read_csv('*.csv', union_by_name=true, filename=true)) TO 'merged.csv' (HEADER)"
union_by_name=true es lo que hace el trabajo: las columnas se emparejan por
encabezado, y un archivo al que le falta una columna recibe NULL en lugar de
una fila desplazada. filename=true agrega una columna que registra de qué
archivo viene cada fila, algo que vas a querer más a menudo de lo que crees. Es
la única forma de responder después "en qué exportación llegó esta fila mala".
DuckDB procesa en flujo, así que el tamaño de los archivos lo limita el disco y no la RAM. Cien archivos que suman varios gigabytes no tienen nada de raro.
Python, si primero tienes que unificar los nombres
Cuando el mismo campo se llama Email en un archivo y E-mail Address en otro,
ninguna herramienta puede adivinarlo. Normaliza de forma explícita:
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)
Dos detalles importan más que la combinación en sí. dtype=str evita que pandas
infiera tipos archivo por archivo. Sin eso, una columna de ID de cliente que es
numérica en un archivo y alfanumérica en otro sale como una mezcla de 1024 y
"01024", y los ceros a la izquierda se pierden para siempre. Y
.assign(source=path) es la misma columna de procedencia que DuckDB te da sin
esfuerzo.
Power Query, en un escritorio sin terminal
En Excel, Datos → Obtener datos → Desde carpeta apunta a un directorio y combina todos los archivos que hay en él, emparejando por encabezado. Lo hace bien, se actualiza cuando llegan archivos nuevos a la carpeta y es la respuesta correcta para quien va a repetir esta combinación cada mes.
Su límite es el de Excel: el resultado combinado tiene que caber en 1.048.576 filas para llegar a una hoja. Puedes cargarlo en el modelo de datos y olvidarte de la hoja, algo que la mayoría no sabe y que sube el techo bastante.
La diferencia entre anexar y cruzar
"Combinar" significa dos operaciones distintas, y elegir la equivocada te hace perder una tarde.
| Tienes | Quieres | Operación |
|---|---|---|
| Doce exportaciones mensuales, mismas columnas | Un archivo, más filas | Anexar (apilar) |
| Clientes en un archivo, pedidos en otro | Un archivo, más columnas | Cruce (join, emparejar por una clave) |
Todo lo anterior es anexar. Un cruce necesita una clave compartida y una decisión sobre qué pasa con las filas sin pareja, y es donde se crean duplicados: una clave que no es única en ambos lados multiplica las filas sin avisar. Si el número de filas tras un cruce es mayor que el de cualquiera de las entradas, eso es lo que pasó.
Para un cruce, SQL es la herramienta honesta:
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)"
En el teléfono
El escenario realista en el teléfono: un puñado de archivos en Mail o Archivos, tres exportaciones regionales o unos meses de extractos, y ningún portátil hasta el lunes.
Dos cosas ayudan aquí. Anexar archivos con encabezados idénticos es copiar y pegar, siempre que la app pueda pegar un bloque de filas y no una celda a la vez. Y la comprobación que importa, comparar encabezados, es más rápida en el teléfono que en ningún otro sitio: abre cada archivo y lee la fila de arriba.
Abre cada exportación en segundos, pega un rango de filas en otra hoja y escribe de vuelta cada fila al guardar, también en la versión gratuita. Con la inserción de columnas y filas, reconciliar un archivo que ganó un campo es una edición real y no un apaño.
Qué hacePara más de un puñado de archivos, espera a tener un escritorio. Combinar treinta exportaciones a mano en una pantalla táctil no es buena idea, y la razón es la tasa de errores, no el aburrimiento.
Después de combinar, cuatro comprobaciones
Hazlas siempre. Llevan un minuto y atrapan prácticamente todos los fallos de combinación que importan.
- Número de filas. El total combinado debe ser la suma de las filas de cada
entrada, menos un encabezado por archivo.
wc -l *.csvantes,wc -l merged.csvdespués. - Número de encabezados.
grep -c "^name," merged.csv, o como se llame la primera columna. Más de uno significa que un encabezado terminó como fila de datos. - Ancho de columnas. Todas las filas deben tener el mismo número de campos. Anchos desiguales indican una coma sin comillas en algún sitio, no un fallo de la combinación.
- Revisa la frontera. Mira la última fila del primer archivo y la primera del segundo en el resultado. La desalineación se nota ahí primero.
La segunda comprobación tiene su sutileza: si una línea de encabezado acaba en los datos, al ordenar se dispersará por el medio del archivo y luego será muy difícil de encontrar. Atrápala ahora.
Ejecuta SQL directamente sobre el archivo combinado, así que un GROUP BY sobre la columna de origen te dice que cada archivo aportó las filas que debía, toda la verificación en una consulta.
Qué haceCodificación, el fallo que sobrevive a todas las comprobaciones
Los archivos de sistemas distintos suelen estar en codificaciones distintas, y
concatenar no convierte nada. Un archivo UTF-8 anexado a uno Windows-1252
produce un archivo que no es ninguno de los dos: los nombres se ven bien en la
primera mitad y como é en la segunda.
Convierte antes de combinar, no después:
iconv -f WINDOWS-1252 -t UTF-8 old.csv > old-utf8.csv
file -I *.csv intentará adivinar lo que tienes, aunque adivina mal en archivos
cortos. La señal fiable es abrir cada archivo y mirar una fila con algún
carácter no ASCII.