(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como processar ficheiros CSV grandes em ETL com pandas

João Barros 07 de July de 2026 5 min de leitura

Trabalhar com ficheiros CSV grandes é uma tarefa muito comum em ETL, mas abrir um ficheiro de vários gigabytes de uma só vez pode esgotar a memória do computador e provocar o temido erro MemoryError. Isto acontece porque muitas rotinas de extração carregam tudo de uma vez, sem pensar no tamanho real dos dados. A solução é processar o ficheiro por partes (chunks) com pandas, lendo e transformando os dados aos poucos. Vamos ver, passo a passo, como processar ficheiros CSV grandes em ETL de forma simples e segura.

Pré-requisitos

  • Python 3.9 ou superior instalado.
  • A biblioteca pandas: instale com pip install pandas.
  • Um ficheiro CSV grande para testar (por exemplo, vendas.csv com colunas como pais e valor).
  • Noções básicas de Python (variáveis e ciclos for).

Passo 1: Perceber o problema

O método habitual, pd.read_csv("vendas.csv"), carrega o ficheiro inteiro para a memória RAM. Se o ficheiro tiver 5 GB, o pandas precisa de vários gigabytes livres, muitas vezes mais do que o tamanho do ficheiro, porque cada coluna ocupa espaço extra. Em máquinas com pouca memória, o resultado é um MemoryError e o programa pára.

Quando a memória RAM se esgota, o sistema operativo começa a usar o disco como memória virtual (swap), e o programa fica extremamente lento antes de, muitas vezes, falhar. A ideia central é nunca ter o ficheiro todo na memória ao mesmo tempo: lemos um bloco de linhas, tratamos esse bloco, libertamos a memória e passamos ao seguinte.

Passo 2: Ler o CSV por chunks

O parâmetro chunksize do read_csv indica quantas linhas cada bloco deve ter. Em vez de um DataFrame, o pandas devolve um iterador que produz um DataFrame de cada vez:

import pandas as pd

# cada bloco tem 100 000 linhas
for chunk in pd.read_csv("vendas.csv", chunksize=100_000):
    print(chunk.shape)

Cada chunk é um DataFrame normal do pandas. Pode aplicar-lhe tudo o que já conhece: filtros, novas colunas, agrupamentos.

Dica: comece com blocos de 100 000 linhas e ajuste conforme a memória disponível. Blocos maiores são mais rápidos, mas ocupam mais RAM; blocos mais pequenos são mais seguros em máquinas modestas.

Passo 3: Transformar e agregar cada bloco

Num pipeline ETL queremos transformar os dados e, muitas vezes, calcular um resumo. O exemplo seguinte limpa cada bloco (mantém apenas valores positivos) e vai somando o total de vendas por país, acumulando o resultado num dicionário:

import pandas as pd

total_por_pais = {}

for chunk in pd.read_csv("vendas.csv", chunksize=100_000):
    # transformar: manter apenas vendas com valor positivo
    chunk = chunk[chunk["valor"] > 0]
    # agregar: somar o valor por pais neste bloco
    soma = chunk.groupby("pais")["valor"].sum()
    for pais, valor in soma.items():
        total_por_pais[pais] = total_por_pais.get(pais, 0) + valor

resultado = pd.Series(total_por_pais).sort_values(ascending=False)
print(resultado)

Repare que só mantemos na memória o dicionário total_por_pais, que é pequeno. Este padrão (resumir cada bloco e somar os resumos) funciona para contagens, somas e médias ponderadas, e é a base de qualquer agregação que não caiba na memória de uma só vez.

Passo 4: Escrever o resultado sem esgotar a memória

Se o objetivo for gravar os dados já tratados noutro ficheiro, escreva cada bloco à medida que o processa. O truque é escrever o cabeçalho apenas no primeiro bloco e usar o modo "a" (append) nos seguintes:

import pandas as pd

primeiro = True
for chunk in pd.read_csv("vendas.csv", chunksize=100_000):
    limpo = chunk[chunk["valor"] > 0]
    limpo.to_csv(
        "vendas_limpas.csv",
        mode="w" if primeiro else "a",
        header=primeiro,
        index=False,
    )
    primeiro = False

Assim, o ficheiro de saída cresce bloco a bloco e nunca precisa de ter tudo na memória.

Passo 5: Reduzir ainda mais a memória (opcional)

Pode poupar bastante memória lendo apenas as colunas necessárias com usecols e indicando tipos mais leves com dtype:

import pandas as pd

for chunk in pd.read_csv(
    "vendas.csv",
    usecols=["pais", "valor"],
    dtype={"valor": "float32"},
    chunksize=100_000,
):
    ...

Ler menos colunas e usar float32 em vez de float64 reduz o espaço ocupado por cada bloco, permitindo blocos maiores ou máquinas mais modestas.

Verificar o resultado

Para confirmar que não perdeu dados, conte as linhas do ficheiro de saída, também por chunks, para não voltar a encher a memória:

import pandas as pd

total = sum(len(c) for c in pd.read_csv("vendas_limpas.csv", chunksize=100_000))
print("Linhas no ficheiro final:", total)

Compare este número com o que esperava depois da limpeza. Durante a execução, abra o Gestor de Tarefas (ou o comando htop no Linux) e confirme que a memória usada pelo Python se mantém estável, em vez de subir sem parar.

Conclusão

Com o parâmetro chunksize consegue processar ficheiros CSV muito maiores do que a memória disponível, mantendo o seu pipeline ETL estável e previsível. A partir daqui, experimente ajustar o tamanho do bloco ao seu hardware e explore alternativas como o polars ou o dask quando precisar de ainda mais velocidade. Qual é o maior ficheiro que já teve de processar, e quanto tempo demorou?