Pandas & NumPy Academy · Aula

Leitura em partes para arquivos grandes

Processe arquivos que excedem a RAM lendo-os em partes com chunksize em read_csv e agregando os resultados de forma incremental.

Aula 4 de 413 etapas

Leitura em partes para arquivos grandes é uma aula grátis de Pandas & NumPy Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Pandas & NumPy Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Pandas & NumPy Academy inclui 4 aulas no total.

Quando os arquivos excedem a RAM

Um gargalo comum em pipelines de dados de produção é um arquivo CSV maior que a RAM disponível. Se um arquivo tem 20 GB e a máquina possui 16 GB de RAM, pd.read_csv('file.csv') falhará com um MemoryError. A solução é a leitura em partes: carregar o arquivo em partes de tamanho fixo, processar cada parte e acumular os resultados. Isso permite analisar arquivos de tamanho arbitrário sem carregá-los inteiramente na memória.

import pandas as pd
import numpy as np

# Simulate a large CSV by writing one
np.random.seed(0)
sample = pd.DataFrame({
    'date': pd.date_range('2023-01-01', periods=100000, freq='h'),
    'region': np.random.choice(['North','South','East','West'], 100000),
    'sales': np.random.randint(100, 1000, 100000)
})
sample.to_csv('/tmp/large_sales.csv', index=False)
print(f'File size: {pd.io.common.get_handle("/tmp/large_sales.csv", "r").handle.seek(0, 2)/1e6:.1f} MB... (simulated)')
print('Rows:', len(sample))

Parâmetro chunksize em read_csv

Passe chunksize=n para pd.read_csv() a fim de obter um iterador TextFileReader em vez de um DataFrame. Cada iteração fornece as próximas n linhas como um DataFrame. Dessa forma, apenas n linhas ficam na memória de cada vez. Um bom valor inicial para chunksize é 100.000 linhas — pequeno o suficiente para caber na RAM, mas grande o suficiente para manter controlável o custo das operações de entrada e saída. Ajuste esse valor de acordo com a RAM disponível e a quantidade de colunas.

import pandas as pd

# Read file in chunks of 25,000 rows
chunk_iter = pd.read_csv('/tmp/large_sales.csv', chunksize=25000)

# Peek at the first chunk
first_chunk = next(chunk_iter)
print('First chunk shape:', first_chunk.shape)
print(first_chunk.head(3))

Iteração sobre partes

Use um laço for sobre o iterador de partes para processar cada parte. Para operações simples, como contar linhas, somar uma coluna ou filtrar, processe cada parte de forma independente e acumule os resultados em uma lista ou em um total corrente. Sempre use o iterador dentro de uma instrução with ou garanta que ele seja recriado em cada execução do pipeline — os iteradores são consumidos uma única vez e não podem ser reiniciados.

import pandas as pd

total_rows = 0
total_sales = 0.0
chunk_count = 0

for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    total_rows += len(chunk)
    total_sales += chunk['sales'].sum()
    chunk_count += 1

print(f'Chunks processed: {chunk_count}')
print(f'Total rows: {total_rows:,}')
print(f'Total sales: {total_sales:,.0f}')
print(f'Avg sales per row: {total_sales/total_rows:.2f}')

Filtragem de linhas durante a leitura em partes

Aplique os filtros de linhas dentro do laço para descartar os dados indesejados antes que eles se acumulem. Isso mantém baixo o uso de memória, retendo apenas as linhas que correspondem aos seus critérios. Por exemplo, para extrair todas as linhas da região 'North' de um arquivo de 10 GB, filtre cada parte antes de adicioná-la à sua lista de resultados. O pd.concat final processará apenas o subconjunto filtrado, que é muito menor.

import pandas as pd

filtered_chunks = []

for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    # Keep only North region rows
    north = chunk[chunk['region'] == 'North']
    if len(north) > 0:
        filtered_chunks.append(north)

north_df = pd.concat(filtered_chunks, ignore_index=True)
print(f'North region rows: {len(north_df):,}')
print(f'North total sales: {north_df["sales"].sum():,.0f}')

Agregação incremental com GroupBy

As agregações de GroupBy entre partes são mais complexas que somas simples, pois os grupos podem se estender por várias partes. O padrão é: calcular as somas e contagens por grupo em cada parte, concatenar esses resultados parciais e executar um groupby final sobre os resultados parciais acumulados. Nunca chame groupby().mean() em cada parte e depois faça a média das médias — isso produz resultados incorretos quando os tamanhos dos grupos diferem entre as partes.

import pandas as pd

partials = []

for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    # Compute sum and count per region in this chunk
    partial = chunk.groupby('region')['sales'].agg(['sum', 'count'])
    partials.append(partial)

# Combine all partial results
combined = pd.concat(partials).groupby(level=0).sum()
combined['mean'] = combined['sum'] / combined['count']

print('Regional aggregation (chunked):')
print(combined.round(2))

Especificação eficiente de tipos no momento do carregamento

Reduza a memória durante o carregamento em partes especificando antecipadamente os dtypes das colunas em read_csv. Isso impede que o Pandas aloque tipos grandes para cada parte e depois os descarte. Passe um dicionário como dtype={'region': 'category', 'sales': 'int32'} para read_csv(). Combinada com a leitura em partes, essa técnica pode reduzir o pico de memória para menos de 10% do carregamento ingênuo do arquivo inteiro.

import pandas as pd

specified_dtypes = {
    'region': 'category',
    'sales': 'int32'
}

total_sales = 0
for chunk in pd.read_csv(
    '/tmp/large_sales.csv',
    chunksize=25000,
    dtype=specified_dtypes,
    parse_dates=['date']
):
    total_sales += chunk['sales'].sum()
    # Only 25k rows * (category + int32 + datetime) in RAM at once

print(f'Total sales (memory-efficient): {total_sales:,.0f}')

Gravação incremental dos resultados

Quando a saída do processamento de cada parte também precisa ser gravada em um arquivo, grave cada parte processada à medida que ela fica pronta, em vez de acumular tudo na memória. Use mode='a' (adicionar) e header=False (após a primeira parte) com to_csv(). Isso mantém o uso de memória tanto da entrada quanto da saída limitado ao tamanho da parte, tornando o pipeline capaz de processar arquivos de tamanho arbitrário em uma máquina com memória limitada.

import pandas as pd
import os

output_path = '/tmp/filtered_output.csv'

# Remove previous output if it exists
if os.path.exists(output_path):
    os.remove(output_path)

first_chunk = True
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
    # Process: keep only high-value rows
    processed = chunk[chunk['sales'] > 800].copy()
    
    # Write: header only on first chunk, append thereafter
    processed.to_csv(output_path,
                     mode='a',
                     header=first_chunk,
                     index=False)
    first_chunk = False

result = pd.read_csv(output_path)
print(f'High-value rows written: {len(result):,}')
print(f'Average sales (>800): {result["sales"].mean():.1f}')

Introdução ao Dask como alternativa de substituição direta

Dask fornece uma API semelhante à do Pandas que executa operações de forma tardia e paralela entre partes automaticamente. Em vez de escrever um laço manual para processar partes, você escreve dask_df = dd.read_csv('file.csv') e, em seguida, usa as mesmas operações do Pandas — groupby, filtro e mesclagem. Chame .compute() ao final para iniciar a execução. O Dask é a solução mais prática quando o seu pipeline envolve operações complexas em várias etapas, difíceis de implementar manualmente com leitura em partes.

# pip install dask
# import dask.dataframe as dd

# Read the large CSV as a Dask DataFrame (lazy — no data loaded yet)
# ddf = dd.read_csv('/tmp/large_sales.csv')

# Operations are lazy — no computation happens until .compute()
# result = ddf.groupby('region')['sales'].mean().compute()
# print(result)

# Key Dask advantages:
# - Automatic chunking (no manual chunksize loops)
# - Parallel execution (multi-core)
# - Familiar Pandas API
# - Works with files larger than RAM

print('Dask extends Pandas to datasets larger than RAM with minimal API changes.')

Escolha do tamanho das partes

O tamanho ideal da parte equilibra dois fatores opostos: partes pequenas demais, como 1.000 linhas, causam um custo elevado por parte, devido à configuração da entrada e saída do arquivo e à criação do DataFrame, e tornam o laço mais demorado. Partes grandes demais, como 10 milhões de linhas, frustram o objetivo ao carregar dados demais na RAM de uma só vez. Um ponto de partida prático é usar partes de 50–200 MB na memória. Para um DataFrame com 10 colunas de média de 8 bytes cada, 100.000 linhas correspondem a aproximadamente 8 MB por parte — um padrão seguro que deixa bastante margem disponível.

import pandas as pd
import timeit

# Benchmark different chunk sizes
for chunksize in [10000, 50000, 100000]:
    t = timeit.timeit(
        lambda: sum(chunk['sales'].sum()
                    for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=chunksize)),
        number=3
    )
    print(f'chunksize={chunksize:>7,}: {t/3:.3f}s per pass')

Parquet versus CSV para dados grandes

Se você controla o formato do arquivo, prefira Parquet ao CSV para conjuntos de dados grandes. O Parquet é um formato binário colunar que carrega apenas as colunas solicitadas, comprime muito melhor que o CSV, preserva os tipos de dados, sem necessidade de inferi-los novamente no carregamento, e é lido de 5 a 20 vezes mais rápido que um CSV equivalente. Converta um CSV grande para Parquet uma vez com pd.read_csv('file.csv', chunksize=500000) + to_parquet e, depois, use pd.read_parquet(columns=['col1','col2']) em todas as leituras seguintes.

import pandas as pd

# Convert our CSV to Parquet (do this once)
df = pd.read_csv('/tmp/large_sales.csv', parse_dates=['date'])
df['region'] = df['region'].astype('category')
df['sales'] = df['sales'].astype('int32')
df.to_parquet('/tmp/large_sales.parquet', index=False)

# Now read only the columns needed — much faster than CSV
sales_by_region = pd.read_parquet(
    '/tmp/large_sales.parquet',
    columns=['region', 'sales']
)
print('Parquet read result:')
print(sales_by_region.groupby('region')['sales'].mean().round(1))
print('\ndtypes preserved:', sales_by_region.dtypes.to_dict())

Padrão completo de um pipeline em partes

Um padrão completo para processar arquivos grandes: 1) especifique os tipos de dados no momento da leitura; 2) filtre as linhas o mais cedo possível dentro do laço; 3) calcule agregações parciais por parte, como soma + contagem, nunca a média diretamente; 4) após o laço, combine os resultados parciais e calcule as estatísticas finais; 5) grave a saída incrementalmente se os resultados forem grandes. Esse padrão processa arquivos de qualquer tamanho em qualquer máquina, desde que o chunksize seja ajustado adequadamente.

Verificação rápida

Teste sua compreensão sobre a leitura em partes apresentada nesta lição.

Recapitulação da lição

Nesta lição, você aprendeu que chunksize em read_csv retorna um iterador que fornece um DataFrame por parte, permitindo processar incrementalmente arquivos maiores que a RAM; agregações parciais, como soma + contagem, são acumuladas corretamente entre as partes, enquanto a média não pode ser calculada diretamente como uma média das médias; e o formato Parquet é a melhor alternativa de longo prazo ao CSV para conjuntos de dados grandes, graças à compressão, à velocidade e à preservação dos tipos de dados. Com isso, o curso Dicas de desempenho do Pandas está concluído — você está pronto para os cursos avançados B2!

Grátis para começar

Aprenda Python com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
30
Aulas
120

Perguntas Frequentes

A aula “Leitura em partes para arquivos grandes” é grátis?

Sim — o texto completo de “Leitura em partes para arquivos grandes” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Pandas & NumPy Academy, atualize para CoddyKit PRO. O curso de Pandas & NumPy Academy inclui 4 aulas no total.

O que vou aprender em “Leitura em partes para arquivos grandes”?

Processe arquivos que excedem a RAM lendo-os em partes com chunksize em read_csv e agregando os resultados de forma incremental. Você pratica Pandas & NumPy Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Pandas & NumPy Academy?

Nenhuma experiência prévia é necessária. Pandas & NumPy Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Leitura em partes para arquivos grandes”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Pandas & NumPy Academy?

Sim. Cada aula de Pandas & NumPy Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Análise de desempenho com timeit e memory_profiler
  2. Evitando iterrows e laços Python
  3. Tipos de dados eficientes para reduzir a memória
  4. Leitura em partes para arquivos grandes
← Voltar para Pandas & NumPy Academy