Como processar arquivos CSV grandes de forma eficiente em Python

PythonBeginner
Pratique Agora

Introdução

O tratamento de arquivos CSV grandes é um desafio comum para desenvolvedores Python. Este tutorial guia você através de técnicas eficientes para processar esses arquivos de forma eficaz, com foco na otimização do desempenho e do uso da memória. Ao final deste laboratório, você estará equipado com conhecimento prático para lidar com tarefas de processamento de CSV com uso intensivo de dados em Python.

Se você está analisando dados de clientes, processando registros financeiros ou trabalhando com qualquer tipo de dado estruturado, as habilidades aprendidas neste laboratório o ajudarão a processar grandes conjuntos de dados de forma eficiente, sem incorrer em problemas de memória.

Criando e Lendo um Arquivo CSV Simples

CSV (Valores Separados por Vírgula) é um formato de arquivo popular usado para armazenar dados tabulares. Nesta etapa, criaremos um arquivo CSV simples e aprenderemos como lê-lo usando o módulo csv embutido do Python.

Entendendo Arquivos CSV

Um arquivo CSV armazena dados em um formato de texto simples, onde:

  • Cada linha representa uma linha de dados
  • Os valores dentro de cada linha são separados por um delimitador (tipicamente uma vírgula)
  • A primeira linha geralmente contém os cabeçalhos das colunas

Vamos começar criando um arquivo CSV simples para trabalhar.

Criando um Arquivo CSV

Primeiro, vamos criar um diretório para trabalhar e, em seguida, criar um arquivo CSV simples:

  1. Abra o terminal no WebIDE
  2. Crie um novo arquivo Python chamado csv_basics.py no editor

Agora, adicione o seguinte código a csv_basics.py:

import csv

## Data to write to CSV
data = [
    ['Name', 'Age', 'City'],              ## Header row
    ['John Smith', '28', 'New York'],
    ['Sarah Johnson', '32', 'San Francisco'],
    ['Michael Brown', '45', 'Chicago'],
    ['Emily Davis', '36', 'Boston'],
    ['David Wilson', '52', 'Seattle']
]

## Writing data to a CSV file
with open('sample_data.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(data)

print("CSV file 'sample_data.csv' created successfully.")

Execute este código executando o seguinte comando no terminal:

python3 csv_basics.py

Saída esperada:

CSV file 'sample_data.csv' created successfully.

Isso criará um novo arquivo CSV chamado sample_data.csv no seu diretório atual. Você pode visualizar o conteúdo deste arquivo executando:

cat sample_data.csv

Saída esperada:

Name,Age,City
John Smith,28,New York
Sarah Johnson,32,San Francisco
Michael Brown,45,Chicago
Emily Davis,36,Boston
David Wilson,52,Seattle

Lendo um Arquivo CSV

Agora, vamos ler o arquivo CSV que acabamos de criar. Crie um novo arquivo chamado read_csv.py com o seguinte código:

import csv

## Reading a CSV file
with open('sample_data.csv', 'r') as file:
    reader = csv.reader(file)

    print("Contents of sample_data.csv:")
    print("--------------------------")

    for row in reader:
        print(row)

## Reading and accessing specific columns
print("\nReading specific columns:")
print("--------------------------")

with open('sample_data.csv', 'r') as file:
    reader = csv.reader(file)
    headers = next(reader)  ## Skip the header row

    for row in reader:
        name = row[0]
        age = row[1]
        city = row[2]
        print(f"Name: {name}, Age: {age}, City: {city}")

Execute este código com:

python3 read_csv.py

Saída esperada:

Contents of sample_data.csv:
--------------------------
['Name', 'Age', 'City']
['John Smith', '28', 'New York']
['Sarah Johnson', '32', 'San Francisco']
['Michael Brown', '45', 'Chicago']
['Emily Davis', '36', 'Boston']
['David Wilson', '52', 'Seattle']

Reading specific columns:
--------------------------
Name: John Smith, Age: 28, City: New York
Name: Sarah Johnson, Age: 32, City: San Francisco
Name: Michael Brown, Age: 45, City: Chicago
Name: Emily Davis, Age: 36, City: Boston
Name: David Wilson, Age: 52, City: Seattle

Entendendo o Módulo CSV

O módulo csv do Python fornece duas classes principais:

  • csv.reader: Lê arquivos CSV e retorna cada linha como uma lista de strings
  • csv.writer: Escreve dados em arquivos CSV

Este módulo lida com todas as complexidades de lidar com diferentes formatos CSV, como escapar caracteres especiais e lidar com aspas.

Nesta etapa, você aprendeu como criar e ler um arquivo CSV simples. Na próxima etapa, exploraremos maneiras mais eficientes de lidar com arquivos CSV maiores.

Usando DictReader para Processamento Conveniente de CSV

Na etapa anterior, trabalhamos com as funções básicas csv.reader e csv.writer. Agora, vamos explorar uma maneira mais conveniente de processar arquivos CSV usando a classe csv.DictReader, que é especialmente útil ao trabalhar com dados que possuem cabeçalhos de coluna.

O que é DictReader?

csv.DictReader lê arquivos CSV e retorna cada linha como um dicionário onde:

  • As chaves são retiradas dos cabeçalhos das colunas (primeira linha do arquivo CSV por padrão)
  • Os valores são os dados correspondentes de cada linha

Essa abordagem torna seu código mais legível e menos propenso a erros, porque você pode referenciar colunas por nome em vez de por índice.

Crie um Arquivo de Teste Maior

Primeiro, vamos criar um arquivo CSV um pouco maior para demonstrar os benefícios do DictReader. Crie um novo arquivo chamado create_users_data.py com o seguinte código:

import csv
import random

## Generate some sample user data
def generate_users(count):
    users = [['id', 'name', 'email', 'age', 'country']]  ## Header row

    domains = ['gmail.com', 'yahoo.com', 'outlook.com', 'example.com']
    countries = ['USA', 'Canada', 'UK', 'Australia', 'Germany', 'France', 'Japan', 'Brazil']
    first_names = ['John', 'Jane', 'Michael', 'Emily', 'David', 'Sarah', 'Robert', 'Lisa']
    last_names = ['Smith', 'Johnson', 'Brown', 'Davis', 'Wilson', 'Miller', 'Jones', 'Taylor']

    for i in range(1, count + 1):
        first_name = random.choice(first_names)
        last_name = random.choice(last_names)
        name = f"{first_name} {last_name}"
        email = f"{first_name.lower()}.{last_name.lower()}@{random.choice(domains)}"
        age = random.randint(18, 65)
        country = random.choice(countries)

        users.append([str(i), name, email, str(age), country])

    return users

## Create a CSV file with 100 users
users_data = generate_users(100)

## Write data to CSV file
with open('users_data.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(users_data)

print(f"Created 'users_data.csv' with 100 user records")

Execute o script para criar o arquivo:

python3 create_users_data.py

Saída esperada:

Created 'users_data.csv' with 100 user records

Vamos examinar as primeiras linhas deste novo arquivo:

head -n 5 users_data.csv

Você deve ver a linha de cabeçalho seguida por 4 linhas de dados:

id,name,email,age,country
1,John Smith,john.smith@gmail.com,25,USA
2,Emily Brown,emily.brown@yahoo.com,32,Canada
3,David Jones,david.jones@outlook.com,45,UK
4,Sarah Wilson,sarah.wilson@example.com,28,Australia

Usando DictReader para Processar o Arquivo CSV

Agora, vamos criar um script para processar este arquivo usando DictReader. Crie um novo arquivo chamado dict_reader_example.py com o seguinte código:

import csv

## Read the CSV file using DictReader
with open('users_data.csv', 'r') as file:
    csv_reader = csv.DictReader(file)

    ## Print the field names (column headers)
    print(f"Column headers: {csv_reader.fieldnames}")
    print("\nFirst 5 records:")
    print("-----------------")

    ## Print the first 5 records
    for i, row in enumerate(csv_reader):
        if i < 5:
            ## Access fields by name
            print(f"User {row['id']}: {row['name']}, {row['age']} years old, from {row['country']}")
            print(f"  Email: {row['email']}")
        else:
            break

## Using DictReader for data analysis
with open('users_data.csv', 'r') as file:
    csv_reader = csv.DictReader(file)

    ## Calculate average age
    total_age = 0
    user_count = 0

    ## Count users by country
    countries = {}

    for row in csv_reader:
        user_count += 1
        total_age += int(row['age'])

        ## Count users by country
        country = row['country']
        if country in countries:
            countries[country] += 1
        else:
            countries[country] = 1

    avg_age = total_age / user_count if user_count > 0 else 0

    print("\nData Analysis:")
    print("--------------")
    print(f"Total users: {user_count}")
    print(f"Average age: {avg_age:.2f} years")
    print("\nUsers by country:")

    for country, count in sorted(countries.items(), key=lambda x: x[1], reverse=True):
        print(f"  {country}: {count} users")

Execute este script:

python3 dict_reader_example.py

Saída esperada (seus valores exatos podem variar, pois os dados são gerados aleatoriamente):

Column headers: ['id', 'name', 'email', 'age', 'country']

First 5 records:
-----------------
User 1: John Smith, 25 years old, from USA
  Email: john.smith@gmail.com
User 2: Emily Brown, 32 years old, from Canada
  Email: emily.brown@yahoo.com
User 3: David Jones, 45 years old, from UK
  Email: david.jones@outlook.com
User 4: Sarah Wilson, 28 years old, from Australia
  Email: sarah.wilson@example.com
User 5: Michael Taylor, 37 years old, from Germany
  Email: michael.taylor@example.com

Data Analysis:
--------------
Total users: 100
Average age: 41.35 years

Users by country:
  USA: 16 users
  Canada: 14 users
  Japan: 13 users
  UK: 12 users
  Germany: 12 users
  Australia: 12 users
  France: 11 users
  Brazil: 10 users

Benefícios de Usar DictReader

Como você pode ver, usar DictReader oferece várias vantagens:

  1. Código legível: Você pode acessar campos por nome em vez de lembrar as posições dos índices
  2. Autodocumentação: O código mostra claramente qual campo você está acessando
  3. Flexibilidade: Se a ordem das colunas mudar no arquivo CSV, seu código ainda funcionará, desde que os nomes das colunas permaneçam os mesmos

Essa abordagem é particularmente útil ao trabalhar com dados do mundo real que possuem muitas colunas ou quando a ordem das colunas pode mudar com o tempo.

Na próxima etapa, exploraremos técnicas eficientes para processar arquivos CSV maiores sem carregar tudo na memória de uma vez.

Processando Arquivos CSV Grandes de Forma Eficiente

Em cenários do mundo real, você pode precisar processar arquivos CSV que têm vários gigabytes de tamanho. Carregar esses arquivos inteiramente na memória pode fazer com que seu aplicativo trave ou diminua a velocidade significativamente. Nesta etapa, exploraremos técnicas para processar arquivos CSV grandes de forma eficiente.

O Desafio da Memória com Arquivos Grandes

Ao trabalhar com arquivos CSV, existem três abordagens comuns, cada uma com diferentes requisitos de memória:

  1. Carregar o arquivo inteiro na memória - Simples, mas usa a maior quantidade de memória
  2. Transmitir o arquivo linha por linha - Usa memória mínima, mas pode ser mais lento para operações complexas
  3. Chunking (divisão em blocos) - Um meio termo que processa o arquivo em blocos gerenciáveis

Vamos explorar cada uma dessas abordagens com exemplos práticos.

Criando um Arquivo de Amostra Maior

Primeiro, vamos criar um arquivo de amostra maior para demonstrar essas técnicas. Crie um novo arquivo chamado create_large_csv.py:

import csv
import random
from datetime import datetime, timedelta

def create_large_csv(filename, num_rows):
    ## Define column headers
    headers = ['transaction_id', 'date', 'customer_id', 'product_id', 'amount', 'status']

    ## Create a file with the specified number of rows
    with open(filename, 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(headers)

        ## Generate random data
        start_date = datetime(2022, 1, 1)
        status_options = ['completed', 'pending', 'failed', 'refunded']

        for i in range(1, num_rows + 1):
            ## Generate random values
            transaction_id = f"TXN-{i:08d}"
            days_offset = random.randint(0, 365)
            date = (start_date + timedelta(days=days_offset)).strftime('%Y-%m-%d')
            customer_id = f"CUST-{random.randint(1001, 9999)}"
            product_id = f"PROD-{random.randint(101, 999)}"
            amount = round(random.uniform(10.0, 500.0), 2)
            status = random.choice(status_options)

            ## Write row to CSV
            writer.writerow([transaction_id, date, customer_id, product_id, amount, status])

            ## Print progress indicator for every 10,000 rows
            if i % 10000 == 0:
                print(f"Generated {i} rows...")

## Create a CSV file with 50,000 rows (about 5-10 MB)
create_large_csv('transactions.csv', 50000)
print("Large CSV file 'transactions.csv' has been created.")

Execute este script para criar o arquivo:

python3 create_large_csv.py

Saída esperada:

Generated 10000 rows...
Generated 20000 rows...
Generated 30000 rows...
Generated 40000 rows...
Generated 50000 rows...
Large CSV file 'transactions.csv' has been created.

Você pode verificar o tamanho do arquivo com:

ls -lh transactions.csv

Saída esperada (o tamanho pode variar ligeiramente):

-rw-r--r-- 1 labex labex 3.8M Apr 15 12:30 transactions.csv

Abordagem 1: Processamento Linha por Linha (Streaming)

A abordagem mais eficiente em termos de memória é processar um arquivo CSV linha por linha. Crie um arquivo chamado streaming_example.py:

import csv
import time

def process_csv_streaming(filename):
    print(f"Processing {filename} using streaming (line by line)...")
    start_time = time.time()

    ## Track some statistics
    row_count = 0
    total_amount = 0
    status_counts = {'completed': 0, 'pending': 0, 'failed': 0, 'refunded': 0}

    ## Process the file line by line
    with open(filename, 'r') as file:
        reader = csv.DictReader(file)

        for row in reader:
            ## Increment row counter
            row_count += 1

            ## Process row data
            amount = float(row['amount'])
            status = row['status']

            ## Update statistics
            total_amount += amount
            status_counts[status] += 1

    ## Calculate and display results
    end_time = time.time()
    processing_time = end_time - start_time

    print(f"\nResults:")
    print(f"Processed {row_count:,} rows in {processing_time:.2f} seconds")
    print(f"Total transaction amount: ${total_amount:,.2f}")
    print(f"Average transaction amount: ${total_amount/row_count:.2f}")
    print("\nTransaction status breakdown:")
    for status, count in status_counts.items():
        percentage = (count / row_count) * 100
        print(f"  {status}: {count:,} ({percentage:.1f}%)")

## Process the file
process_csv_streaming('transactions.csv')

Execute este script:

python3 streaming_example.py

Saída esperada (seus números exatos podem variar):

Processing transactions.csv using streaming (line by line)...

Results:
Processed 50,000 rows in 0.17 seconds
Total transaction amount: $12,739,853.35
Average transaction amount: $254.80

Transaction status breakdown:
  completed: 12,432 (24.9%)
  pending: 12,598 (25.2%)
  failed: 12,414 (24.8%)
  refunded: 12,556 (25.1%)

Abordagem 2: Processamento em Blocos (Chunked Processing)

Para operações mais complexas ou quando você precisa processar dados em lotes, você pode usar uma abordagem em blocos. Crie um arquivo chamado chunked_example.py:

import csv
import time

def process_csv_chunked(filename, chunk_size=10000):
    print(f"Processing {filename} using chunks of {chunk_size} rows...")
    start_time = time.time()

    ## Track some statistics
    row_count = 0
    total_amount = 0
    status_counts = {'completed': 0, 'pending': 0, 'failed': 0, 'refunded': 0}

    ## Process the file in chunks
    with open(filename, 'r') as file:
        reader = csv.DictReader(file)

        chunk = []
        for row in reader:
            ## Add row to current chunk
            chunk.append(row)

            ## When chunk reaches desired size, process it
            if len(chunk) >= chunk_size:
                ## Process the chunk
                for row_data in chunk:
                    ## Update statistics
                    row_count += 1
                    amount = float(row_data['amount'])
                    status = row_data['status']

                    total_amount += amount
                    status_counts[status] += 1

                print(f"Processed chunk of {len(chunk)} rows... ({row_count:,} total)")
                ## Clear the chunk for next batch
                chunk = []

        ## Process any remaining rows in the last chunk
        if chunk:
            for row_data in chunk:
                row_count += 1
                amount = float(row_data['amount'])
                status = row_data['status']

                total_amount += amount
                status_counts[status] += 1

            print(f"Processed final chunk of {len(chunk)} rows... ({row_count:,} total)")

    ## Calculate and display results
    end_time = time.time()
    processing_time = end_time - start_time

    print(f"\nResults:")
    print(f"Processed {row_count:,} rows in {processing_time:.2f} seconds")
    print(f"Total transaction amount: ${total_amount:,.2f}")
    print(f"Average transaction amount: ${total_amount/row_count:.2f}")
    print("\nTransaction status breakdown:")
    for status, count in status_counts.items():
        percentage = (count / row_count) * 100
        print(f"  {status}: {count:,} ({percentage:.1f}%)")

## Process the file with chunks of 10,000 rows
process_csv_chunked('transactions.csv', chunk_size=10000)

Execute este script:

python3 chunked_example.py

Saída esperada:

Processing transactions.csv using chunks of 10000 rows...
Processed chunk of 10000 rows... (10,000 total)
Processed chunk of 10000 rows... (20,000 total)
Processed chunk of 10000 rows... (30,000 total)
Processed chunk of 10000 rows... (40,000 total)
Processed chunk of 10000 rows... (50,000 total)

Results:
Processed 50,000 rows in 0.20 seconds
Total transaction amount: $12,739,853.35
Average transaction amount: $254.80

Transaction status breakdown:
  completed: 12,432 (24.9%)
  pending: 12,598 (25.2%)
  failed: 12,414 (24.8%)
  refunded: 12,556 (25.1%)

Comparação de Uso de Memória

As principais diferenças entre essas abordagens são:

  1. Streaming (linha por linha):

    • Usa memória mínima, independentemente do tamanho do arquivo
    • Melhor para arquivos muito grandes
    • Operações simples em cada linha
  2. Processamento em blocos:

    • Usa mais memória do que streaming, mas ainda é eficiente
    • Bom para operações que precisam processar linhas em lotes
    • Fornece atualizações de progresso durante o processamento
    • Pode ser combinado com multiprocessamento para processamento paralelo

Para a maioria dos propósitos práticos, a abordagem de streaming é recomendada, a menos que você precise especificamente de recursos de processamento em lotes. Ele fornece a melhor eficiência de memória, mantendo um bom desempenho.

Na próxima etapa, exploraremos o uso de bibliotecas de terceiros como pandas para recursos de processamento CSV ainda mais poderosos.

Usando Pandas para Processamento Avançado de CSV

Embora o módulo csv embutido do Python seja poderoso, a biblioteca pandas oferece funcionalidade mais avançada para análise e manipulação de dados. Nesta etapa, exploraremos como usar pandas para processamento eficiente de CSV.

Instalando Pandas

Primeiro, vamos instalar a biblioteca pandas:

pip install pandas

Saída esperada:

Collecting pandas
  Downloading pandas-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.3/12.3 MB 42.6 MB/s eta 0:00:00
...
Successfully installed pandas-2.0.0 numpy-1.24.3 python-dateutil-2.8.2 pytz-2023.3 tzdata-2023.3

Lendo Arquivos CSV com Pandas

Pandas facilita a leitura, análise e manipulação de dados CSV. Crie um arquivo chamado pandas_basic.py:

import pandas as pd
import time

def process_with_pandas(filename):
    print(f"Processing {filename} with pandas...")
    start_time = time.time()

    ## Read the CSV file into a DataFrame
    df = pd.read_csv(filename)

    ## Display basic information
    print(f"\nDataFrame Info:")
    print(f"Shape: {df.shape} (rows, columns)")
    print(f"Column names: {', '.join(df.columns)}")

    ## Display the first 5 rows
    print("\nFirst 5 rows:")
    print(df.head())

    ## Basic statistics
    print("\nSummary statistics for numeric columns:")
    print(df.describe())

    ## Group by analysis
    print("\nTransaction counts by status:")
    status_counts = df['status'].value_counts()
    print(status_counts)

    ## Calculate average amount by status
    print("\nAverage transaction amount by status:")
    avg_by_status = df.groupby('status')['amount'].mean()
    print(avg_by_status)

    ## Calculate total amount by date (first 5 dates)
    print("\nTotal transaction amount by date (first 5 dates):")
    total_by_date = df.groupby('date')['amount'].sum().sort_values(ascending=False).head(5)
    print(total_by_date)

    end_time = time.time()
    print(f"\nProcessed in {end_time - start_time:.2f} seconds")

## Process the transactions file
process_with_pandas('transactions.csv')

Execute este script:

python3 pandas_basic.py

Saída esperada (truncada para brevidade):

Processing transactions.csv with pandas...

DataFrame Info:
Shape: (50000, 6) (rows, columns)
Column names: transaction_id, date, customer_id, product_id, amount, status

First 5 rows:
  transaction_id        date customer_id product_id   amount    status
0    TXN-00000001  2022-12-19   CUST-5421   PROD-383  385.75  refunded
1    TXN-00000002  2022-02-01   CUST-7078   PROD-442  286.83  completed
2    TXN-00000003  2022-12-24   CUST-2356   PROD-701  364.87    failed
3    TXN-00000004  2022-04-09   CUST-3458   PROD-854  247.73   pending
4    TXN-00000005  2022-03-07   CUST-6977   PROD-307  298.69  completed

Summary statistics for numeric columns:
              amount
count  50000.000000
mean     254.797067
std      141.389125
min       10.010000
25%      127.732500
50%      254.865000
75%      381.387500
max      499.990000

Transaction counts by status:
pending     12598
refunded    12556
completed   12432
failed      12414
Name: status, dtype: int64

Average transaction amount by status:
status
completed    255.028733
failed       254.709444
pending      254.690785
refunded     254.760390
Name: amount, dtype: float64

Total transaction amount by date (first 5 dates):
date
2022-01-20    38883.19
2022-08-30    38542.49
2022-03-10    38331.67
2022-11-29    38103.61
2022-06-24    37954.87
Name: amount, dtype: float64

Processed in 0.11 seconds

Processando Arquivos CSV Grandes com Pandas

Embora pandas seja poderoso, ele pode consumir muita memória ao carregar arquivos CSV grandes. Para arquivos muito grandes, você pode usar o parâmetro chunksize para ler o arquivo em blocos. Crie um arquivo chamado pandas_chunked.py:

import pandas as pd
import time

def process_large_csv_with_pandas(filename, chunk_size=10000):
    print(f"Processing {filename} with pandas using chunks of {chunk_size} rows...")
    start_time = time.time()

    ## Initialize variables to store aggregated results
    total_rows = 0
    total_amount = 0
    status_counts = {'completed': 0, 'pending': 0, 'failed': 0, 'refunded': 0}
    daily_totals = {}

    ## Process the file in chunks
    for chunk_num, chunk in enumerate(pd.read_csv(filename, chunksize=chunk_size)):
        ## Update row count
        chunk_rows = len(chunk)
        total_rows += chunk_rows

        ## Update total amount
        chunk_amount = chunk['amount'].sum()
        total_amount += chunk_amount

        ## Update status counts
        for status, count in chunk['status'].value_counts().items():
            status_counts[status] += count

        ## Update daily totals
        for date, group in chunk.groupby('date'):
            if date in daily_totals:
                daily_totals[date] += group['amount'].sum()
            else:
                daily_totals[date] = group['amount'].sum()

        print(f"Processed chunk {chunk_num + 1} ({total_rows:,} rows so far)")

    ## Calculate results
    end_time = time.time()
    processing_time = end_time - start_time

    print(f"\nResults after processing {total_rows:,} rows in {processing_time:.2f} seconds:")
    print(f"Total transaction amount: ${total_amount:,.2f}")
    print(f"Average transaction amount: ${total_amount/total_rows:.2f}")

    print("\nTransaction status breakdown:")
    for status, count in status_counts.items():
        percentage = (count / total_rows) * 100
        print(f"  {status}: {count:,} ({percentage:.1f}%)")

    ## Show top 5 days by transaction amount
    print("\nTop 5 days by transaction amount:")
    top_days = sorted(daily_totals.items(), key=lambda x: x[1], reverse=True)[:5]
    for date, amount in top_days:
        print(f"  {date}: ${amount:,.2f}")

## Process the transactions file with chunks
process_large_csv_with_pandas('transactions.csv', chunk_size=10000)

Execute este script:

python3 pandas_chunked.py

Saída esperada:

Processing transactions.csv with pandas using chunks of 10000 rows...
Processed chunk 1 (10,000 rows so far)
Processed chunk 2 (20,000 rows so far)
Processed chunk 3 (30,000 rows so far)
Processed chunk 4 (40,000 rows so far)
Processed chunk 5 (50,000 rows so far)

Results after processing 50,000 rows in 0.34 seconds:
Total transaction amount: $12,739,853.35
Average transaction amount: $254.80

Transaction status breakdown:
  completed: 12,432 (24.9%)
  pending: 12,598 (25.2%)
  failed: 12,414 (24.8%)
  refunded: 12,556 (25.1%)

Top 5 days by transaction amount:
  2022-01-20: $38,883.19
  2022-08-30: $38,542.49
  2022-03-10: $38,331.67
  2022-11-29: $38,103.61
  2022-06-24: $37,954.87

Filtrando e Transformando Dados com Pandas

Um dos grandes benefícios do pandas é a capacidade de filtrar e transformar dados facilmente. Crie um arquivo chamado pandas_filter.py:

import pandas as pd
import time

def filter_and_transform(filename):
    print(f"Filtering and transforming data from {filename}...")
    start_time = time.time()

    ## Read the CSV file
    df = pd.read_csv(filename)

    ## 1. Filter: Get only completed transactions
    completed = df[df['status'] == 'completed']
    print(f"Number of completed transactions: {len(completed)}")

    ## 2. Filter: Get high-value transactions (over $400)
    high_value = df[df['amount'] > 400]
    print(f"Number of high-value transactions (>${400}): {len(high_value)}")

    ## 3. Filter: Transactions from first quarter of 2022
    df['date'] = pd.to_datetime(df['date'])  ## Convert to datetime
    q1_2022 = df[(df['date'] >= '2022-01-01') & (df['date'] <= '2022-03-31')]
    print(f"Number of transactions in Q1 2022: {len(q1_2022)}")

    ## 4. Add a new column: transaction_month
    df['month'] = df['date'].dt.strftime('%Y-%m')

    ## 5. Group by month and status
    monthly_by_status = df.groupby(['month', 'status']).agg({
        'transaction_id': 'count',
        'amount': 'sum'
    }).rename(columns={'transaction_id': 'count'})

    ## Calculate success rate by month (completed / total)
    print("\nTransaction success rates by month:")
    for month, month_data in df.groupby('month'):
        total = len(month_data)
        completed = len(month_data[month_data['status'] == 'completed'])
        success_rate = (completed / total) * 100
        print(f"  {month}: {success_rate:.1f}% ({completed}/{total})")

    ## Save filtered data to a new CSV file
    completed_high_value = df[(df['status'] == 'completed') & (df['amount'] > 300)]
    output_file = 'high_value_completed.csv'
    completed_high_value.to_csv(output_file, index=False)

    end_time = time.time()
    print(f"\nFiltering completed in {end_time - start_time:.2f} seconds")
    print(f"Saved {len(completed_high_value)} high-value completed transactions to {output_file}")

## Filter and transform the data
filter_and_transform('transactions.csv')

Execute este script:

python3 pandas_filter.py

Saída esperada:

Filtering and transforming data from transactions.csv...
Number of completed transactions: 12432
Number of high-value transactions (>$400): 6190
Number of transactions in Q1 2022: 12348

Transaction success rates by month:
  2022-01: 24.8% (1048/4225)
  2022-02: 25.0% (1010/4034)
  2022-03: 25.4% (1042/4089)
  2022-04: 24.2% (978/4052)
  2022-05: 24.4% (1047/4297)
  2022-06: 24.4% (1046/4280)
  2022-07: 24.7% (1071/4341)
  2022-08: 25.1% (1090/4343)
  2022-09: 26.1% (1091/4177)
  2022-10: 24.1% (1008/4182)
  2022-11: 24.8% (1009/4075)
  2022-12: 25.2% (992/3905)

Filtering completed in 0.38 seconds
Saved 6304 high-value completed transactions to high_value_completed.csv

Vantagens de Usar Pandas

Como você pode ver nesses exemplos, pandas oferece várias vantagens para o processamento de CSV:

  1. Funcionalidade rica: Métodos embutidos para filtrar, agrupar, agregar e transformar dados
  2. Desempenho: Código C otimizado por baixo dos panos para operações rápidas em grandes conjuntos de dados
  3. Análise de dados fácil: Maneiras simples de calcular estatísticas e obter insights
  4. Recursos de visualização: Fácil integração com bibliotecas de plotagem (não mostradas nos exemplos)
  5. Processamento em blocos: Capacidade de lidar com arquivos maiores do que a memória disponível

Para a maioria das tarefas de análise de dados envolvendo arquivos CSV, pandas é a abordagem recomendada, a menos que você tenha restrições de memória específicas que exijam o uso do módulo csv puro do Python.

Resumo

Neste tutorial, você aprendeu várias abordagens para processar arquivos CSV de forma eficiente em Python, desde conjuntos de dados pequenos até os grandes que exigem um gerenciamento cuidadoso da memória:

  1. Processamento Básico de CSV: Usando o módulo csv embutido do Python para ler e escrever arquivos CSV com csv.reader e csv.writer.

  2. Processamento Baseado em Dicionário: Usando csv.DictReader para trabalhar com dados CSV de uma forma mais intuitiva, acessando campos por nome em vez de índice.

  3. Técnicas de Processamento Eficientes:

    • Streaming (transmissão): Processando arquivos linha por linha para uso mínimo de memória
    • Chunking (divisão em blocos): Processando arquivos em lotes para melhor gerenciamento de memória
  4. Processamento Avançado com Pandas:

    • Lendo arquivos CSV em DataFrames
    • Analisando e filtrando dados
    • Processando arquivos grandes em blocos
    • Transformando e exportando dados

Essas técnicas fornecem um conjunto de ferramentas abrangente para lidar com arquivos CSV de qualquer tamanho em Python. Para a maioria das tarefas de análise de dados, pandas é a biblioteca recomendada devido à sua rica funcionalidade e desempenho. No entanto, para arquivos muito grandes ou tarefas de processamento simples, as abordagens de streaming e chunking com o módulo csv embutido podem ser mais eficientes em termos de memória.

Ao aplicar a técnica apropriada com base em seus requisitos específicos, você pode processar arquivos CSV de qualquer tamanho de forma eficiente, sem ter problemas de memória.