mygems

Como juntar vários arquivos CSV em um só

Concatenar alinha as colunas por posição, então um cabeçalho em outra ordem estraga o resultado sem avisar. Veja como juntar por nome e verificar.

Por uos ·

Se os arquivos têm as mesmas colunas na mesma ordem, a forma mais rápida e correta de juntá-los é uma linha de shell que mantém o primeiro cabeçalho e descarta os demais:

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

Todo o resto desta página existe porque essa suposição, colunas idênticas na mesma ordem, é falsa com muito mais frequência do que as pessoas imaginam, e a falha é silenciosa. Uma junção que desalinha duas colunas gera um arquivo que abre normalmente, parece normal e está errado.

Primeiro, confira se os cabeçalhos coincidem

Faça isso antes de juntar, não depois. Todos os problemas seguintes são variações deste.

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

Leia a saída procurando quatro coisas:

  • Ordem de colunas diferente. name,email,date em um arquivo e name,date,email em outro. A concatenação põe datas na coluna de e-mail, e nada reclama.
  • Nomes diferentes para o mesmo campo. Email, email, E-mail, Email Address. Comum quando os arquivos vieram de exportações diferentes, ou da mesma exportação em anos diferentes.
  • Colunas a mais ou a menos. Uma exportação posterior ganhou um campo. Agora as linhas têm larguras diferentes.
  • Uma marca de ordem de bytes (BOM) no primeiro arquivo. name não é name. Ela é invisível em qualquer editor e quebra junções por nome de coluna.

Se todos os cabeçalhos forem idênticos byte a byte, use a versão de duas linhas acima e pare de ler. Se não forem, a concatenação é a ferramenta errada e você precisa de algo que junte por nome de coluna.

Juntando por nome de coluna

DuckDB, se os arquivos são grandes ou os cabeçalhos diferem

Um comando resolve os dois problemas, ordens de colunas diferentes e colunas ausentes, porque alinha pelo nome e não pela posição:

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

union_by_name=true é a parte que faz o trabalho: as colunas são casadas pelo cabeçalho, e um arquivo sem uma coluna recebe NULL nela, em vez de uma linha deslocada. filename=true adiciona uma coluna registrando de qual arquivo cada linha veio, algo de que você vai precisar mais do que imagina. É a única forma de responder "em qual exportação essa linha ruim chegou" depois do fato.

O DuckDB processa em fluxo, então o tamanho dos arquivos é limitado pelo disco e não pela RAM. Cem arquivos somando vários gigabytes é algo corriqueiro.

Python, se você precisa harmonizar os nomes antes

Quando o mesmo campo se chama Email em um arquivo e E-mail Address em outro, nenhuma ferramenta adivinha. Normalize 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("") 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)

Dois detalhes importam mais do que a junção em si. dtype=str impede o pandas de inferir tipos arquivo por arquivo. Sem isso, uma coluna de ID de cliente numérica em um arquivo e alfanumérica em outro sai como uma mistura de 1024 e "01024", e os zeros à esquerda se perdem para sempre. E .assign(source=path) é a mesma coluna de origem que o DuckDB entrega de graça.

Power Query, num computador sem terminal

No Excel, Dados → Obter Dados → De uma Pasta aponta para um diretório e combina todos os arquivos dentro dele, casando pelo cabeçalho. Faz isso bem, atualiza quando chegam novos arquivos à pasta e é a resposta certa para quem vai repetir essa junção todo mês.

O limite dele é o limite do Excel: o resultado ainda precisa caber em 1.048.576 linhas para entrar numa planilha. Você pode carregar para o modelo de dados e pular a planilha por completo, algo que a maioria não sabe e que eleva bastante o teto.

A diferença entre empilhar e cruzar

"Juntar" significa duas operações diferentes, e escolher a errada desperdiça uma tarde inteira.

Você temVocê querOperação
Doze exportações mensais, mesmas colunasUm arquivo, mais linhasAppend (empilhar)
Clientes em um arquivo, pedidos em outroUm arquivo, mais colunasJoin (cruzar por uma chave)

Tudo o que vimos até aqui é append. Um join precisa de uma chave em comum e de uma decisão sobre o que acontece com as linhas sem correspondência, e é aí que nascem as duplicatas, porque uma chave que não é única dos dois lados multiplica linhas em silêncio. Se o número de linhas depois de um join é maior que o de qualquer entrada, foi isso que aconteceu.

Para um join, SQL é a ferramenta 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)"

No celular

O cenário realista no celular é um punhado de arquivos no Mail ou no app Arquivos, três exportações regionais ou alguns meses de extratos, e nenhum notebook até segunda-feira.

Duas coisas ajudam aqui. Empilhar arquivos com cabeçalhos idênticos é copiar e colar, desde que o app consiga colar um bloco de linhas em vez de uma célula por vez. E a verificação que importa, comparar cabeçalhos, é mais rápida no celular do que em qualquer outro lugar: abra cada arquivo e leia a primeira linha.

CSV Editor - Smart CSViPhone e iPad

Abre cada exportação em segundos, cola um intervalo de linhas em outra planilha e grava todas as linhas ao salvar, inclusive na versão gratuita. Com inserção de colunas e linhas, ajustar um arquivo que ganhou um campo é uma edição de verdade, não um improviso.

O que ele faz

Para mais do que um punhado de arquivos, espere um computador. Juntar trinta exportações à mão não é coisa para fazer numa tela de toque, e o motivo é a taxa de erros, não o tédio.

Depois de juntar, quatro verificações

Faça sempre. Levam um minuto e pegam praticamente todas as falhas de junção que importam.

  1. Contagem de linhas. O total deve ser a soma das linhas de todas as entradas, menos um cabeçalho por arquivo. wc -l *.csv antes, wc -l merged.csv depois.
  2. Contagem de cabeçalhos. grep -c "^name," merged.csv, ou o nome da primeira coluna. Mais de um resultado significa que um cabeçalho virou linha de dados.
  3. Largura das colunas. Toda linha deve ter o mesmo número de campos. Larguras desiguais indicam uma vírgula sem aspas em algum lugar, não um bug da junção.
  4. Confira a fronteira. Olhe a última linha do arquivo um e a primeira do arquivo dois no resultado. O desalinhamento aparece ali primeiro.

A segunda verificação tem uma sutileza: se uma linha de cabeçalho for parar no meio dos dados, a ordenação vai espalhá-la pelo arquivo e ela fica muito difícil de achar depois. Pegue isso agora.

CSV File Viewer - Smart CSViPhone e iPad · Android

Executa SQL direto no arquivo resultante, então um GROUP BY na coluna de origem mostra se cada arquivo contribuiu com o número de linhas esperado, e toda a verificação cabe em uma consulta.

O que ele faz

Codificação, a falha que sobrevive a todas as verificações

Arquivos de sistemas diferentes costumam estar em codificações diferentes, e a concatenação não converte. Um arquivo UTF-8 anexado a um Windows-1252 produz um arquivo que não é nenhum dos dois: os nomes aparecem certos na primeira metade e como é na segunda.

Converta antes de juntar, não depois:

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

file -I *.csv tenta adivinhar o que você tem, mas erra bastante em arquivos curtos. O sinal confiável é abrir cada arquivo e olhar uma linha com um caractere fora do ASCII.

Perguntas

Como combino vários arquivos CSV em um só?
Se todos os arquivos têm cabeçalhos idênticos byte a byte, mantenha o primeiro cabeçalho e anexe o restante: `head -n 1 first.csv > merged.csv` e depois `tail -q -n +2 *.csv >> merged.csv`. Se os cabeçalhos diferem na ordem ou no conteúdo, a concatenação desalinha as colunas em silêncio, então use uma ferramenta que case pelo nome da coluna, como o read_csv do DuckDB com union_by_name=true, ou o Obter Dados De uma Pasta do Excel.
Por que meu CSV juntado ficou com colunas nos lugares errados?
Porque a concatenação casa por posição, não por nome. Dois arquivos com as mesmas colunas em ordem diferente geram um arquivo com valores deslocados, e nada informa erro. Sempre compare as linhas de cabeçalho antes de juntar: `for f in *.csv; do echo "$f: $(head -n 1 "$f")"; done`.
Como junto arquivos CSV com colunas diferentes?
Case pelo nome da coluna e preencha as lacunas. O DuckDB faz isso em um comando com `union_by_name=true`, gerando NULL para uma coluna que o arquivo não tem. No pandas, `pd.concat` em frames lidos com `dtype=str` faz o mesmo. Campos que são a mesma coisa com nomes diferentes, Email e E-mail Address, precisam ser renomeados explicitamente antes; nenhuma ferramenta consegue inferir isso.
Como sei de qual arquivo veio cada linha do CSV juntado?
Adicione uma coluna de origem durante a junção. O read_csv do DuckDB aceita filename=true e a adiciona automaticamente; no pandas, `.assign(source=path)` em cada frame faz o mesmo. É a única forma de rastrear uma linha ruim até a exportação de origem depois, e transforma a verificação em um único GROUP BY.
Como sei se a junção do CSV deu certo?
Quatro verificações: o total de linhas é igual à soma das entradas menos um cabeçalho por arquivo; a linha de cabeçalho aparece exatamente uma vez; todas as linhas têm o mesmo número de campos; e a fronteira entre o primeiro e o segundo arquivo está correta. Um cabeçalho que virou linha de dados é a falha que vale pegar cedo, porque a ordenação o espalha pelo meio do arquivo.
Por que os caracteres acentuados quebraram em metade do meu arquivo juntado?
As entradas estavam em codificações diferentes, e anexar não converte. Um arquivo Windows-1252 anexado a um UTF-8 deixa os nomes certos em uma metade e com mojibake na outra. Converta tudo para UTF-8 primeiro com `iconv -f WINDOWS-1252 -t UTF-8 old.csv > old-utf8.csv` e depois junte.

uos — Desenvolve o CSV Editor e o Smart CSV Viewer, e já viu uma junção deslocar uma coluna em uma posição por 200.000 linhas sem um único erro.