0Pricing
Pandas & NumPy Academy · Lezione

CSV in streaming con chunksize

Legga un CSV di grandi dimensioni in blocchi di dimensione fissa con pd.read_csv(chunksize=), elabori ogni blocco e concateni o accumuli i risultati.

CSV in streaming con chunksize è una lezione Pandas & NumPy Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Pandas & NumPy Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Pandas & NumPy Academy include 4 lezioni in totale.

Il problema dei file CSV di grandi dimensioni

Quando un file CSV è più grande della RAM disponibile — ad esempio, un file di log da 50 GB su una macchina con 16 GB di memoria — la chiamata pd.read_csv('file.csv') restituisce un errore MemoryError oppure costringe il sistema a usare intensivamente la memoria virtuale, rendendolo inutilizzabilmente lento. La soluzione è la lettura a blocchi: invece di caricare l'intero file in una volta, lo si elabora in parti di dimensione fissa, accumulando i risultati senza mantenere mai tutto il contenuto in memoria contemporaneamente.

Il parametro chunksize in read_csv

Passare chunksize=N a pd.read_csv() restituisce un iteratore TextFileReader anziché un DataFrame. Ogni iterazione produce un DataFrame contenente al massimo N righe. Il file viene letto in modo lazy: non vengono caricati dati finché non si richiede il blocco successivo. Questo iteratore può essere usato in un ciclo for o passato a pd.concat(). Scelga un chunksize abbastanza grande da garantire un I/O efficiente (ad esempio, 10.000–100.000 righe), ma abbastanza piccolo da entrare comodamente in memoria.

import pandas as pd

# Returns a TextFileReader iterator, NOT a DataFrame
chunks = pd.read_csv('sales_data.csv', chunksize=10000)
print(type(chunks))  # <class 'pandas.io.parsers.readers.TextFileReader'>

for chunk in chunks:
    print(f'Chunk shape: {chunk.shape}')
    # process each chunk independently
    break   # just show the first chunk here

Elaborazione indipendente di ogni blocco

Il modello più comune consiste nell'eseguire una trasformazione o un filtro su ogni blocco, raccogliere i risultati in un elenco e infine concatenarli. Ad esempio, potrebbe filtrare le righe che corrispondono a una condizione, calcolare statistiche per blocco oppure selezionare solo le colonne necessarie. Lavorare su sottoinsiemi significa che solo il blocco corrente occupa memoria, mentre il resto del file rimane intatto. Dopo il ciclo, una singola chiamata a pd.concat(results) assembla il DataFrame finale.

import pandas as pd

results = []
for chunk in pd.read_csv('orders.csv', chunksize=50000):
    # Keep only high-value orders
    filtered = chunk[chunk['amount'] > 1000]
    results.append(filtered)

# Combine all filtered chunks
high_value = pd.concat(results, ignore_index=True)
print('High-value orders:', len(high_value))

Accumulo di aggregati tra i blocchi

A volte non è necessario conservare alcuna riga: serve soltanto un aggregato progressivo. Tenga traccia di una somma, di un conteggio o del minimo/massimo progressivo tra i blocchi senza creare un elenco di DataFrame. Questo è il modello più efficiente dal punto di vista della memoria, perché l'utilizzo della memoria rimane costante indipendentemente dalle dimensioni del file. Al termine, calcoli la statistica finale a partire dagli accumulatori.

import pandas as pd

total_revenue = 0.0
total_rows = 0

for chunk in pd.read_csv('sales.csv', chunksize=100000):
    total_revenue += chunk['revenue'].sum()
    total_rows += len(chunk)

print(f'Processed {total_rows:,} rows')
print(f'Total revenue: ${total_revenue:,.2f}')

Specificare dtype per velocizzare la lettura a blocchi

Per impostazione predefinita, Pandas deduce i dtype delle colonne dai dati, operazione che richiede la scansione di ogni blocco due volte (una per la deduzione e una per l'analisi). Fornire l'argomento dtype evita questo sovraccarico e impedisce anche incoerenze nei dtype tra i blocchi. Ad esempio, una colonna contenente quasi solo interi ma con una cella vuota potrebbe essere interpretata come float64 in un blocco e come object in un altro. Specificare esplicitamente i dtype garantisce una lettura coerente e più veloce di tutti i blocchi.

import pandas as pd

dtype_map = {
    'order_id': 'int32',
    'customer_id': 'int32',
    'amount': 'float32',
    'category': 'category'
}

for chunk in pd.read_csv('orders.csv',
                         chunksize=50000,
                         dtype=dtype_map,
                         parse_dates=['order_date']):
    print(chunk.dtypes)
    break

Selezionare solo le colonne necessarie

Utilizzi il parametro usecols per caricare solo le colonne necessarie all'analisi. Se un CSV contiene 50 colonne ma l'aggregazione ne usa soltanto 3, non è necessario analizzare le altre 47. La combinazione di usecols e chunksize riduce notevolmente sia il tempo di I/O sia l'utilizzo della memoria. È una delle ottimizzazioni più semplici e incisive per l'elaborazione di CSV di grandi dimensioni.

import pandas as pd

# Only read the three columns we actually need
for chunk in pd.read_csv(
    'large_transactions.csv',
    chunksize=100000,
    usecols=['date', 'amount', 'region']
):
    print(chunk.columns.tolist())
    print(chunk.memory_usage(deep=True).sum() / 1e6, 'MB per chunk')
    break

Aggregazione GroupBy nei blocchi

Eseguire un'aggregazione groupby tra più blocchi richiede l'accumulo di risultati parziali. Calcoli il groupby all'interno di ogni blocco, quindi combini i risultati usando un secondo groupby sui risultati parziali concatenati. Ad esempio, per ottenere le vendite totali per regione da un file di 10 GB, raccolga in un elenco le somme per regione calcolate in ogni blocco, quindi concateni i risultati ed esegua nuovamente il raggruppamento. Questo modello di aggregazione in due passaggi viene talvolta chiamato approccio map-reduce.

import pandas as pd

partials = []
for chunk in pd.read_csv('sales.csv',
                         chunksize=100000,
                         usecols=['region', 'revenue']):
    partial = chunk.groupby('region')['revenue'].sum()
    partials.append(partial)

# Combine partial sums
final = pd.concat(partials).groupby(level=0).sum()
print('Revenue by region:')
print(final.sort_values(ascending=False))

Gestire gli errori di analisi tra i blocchi

I file CSV di grandi dimensioni provenienti da fonti esterne contengono spesso righe malformate: virgole aggiuntive, codifica errata o righe troncate. Utilizzi on_bad_lines='skip' (Pandas 1.3+) oppure error_bad_lines=False (versioni precedenti di Pandas) per ignorare silenziosamente le righe errate, e encoding='latin-1' se l'analisi UTF-8 non riesce. Tenga traccia dei blocchi che hanno prodotto errori usando un try-except intorno all'elaborazione di ogni blocco, così da creare una pipeline robusta che non si interrompa a causa di una singola riga errata in un file da 10 milioni di righe.

import pandas as pd

bad_chunks = []
all_chunks = []

for i, chunk in enumerate(pd.read_csv(
    'raw_data.csv',
    chunksize=50000,
    on_bad_lines='skip',
    encoding='utf-8',
    encoding_errors='replace'
)):
    try:
        # Your transformation here
        all_chunks.append(chunk)
    except Exception as e:
        bad_chunks.append((i, str(e)))
        print(f'Chunk {i} error: {e}')

print(f'Processed {len(all_chunks)} chunks, {len(bad_chunks)} errors')

Scrittura a blocchi nei file di output

Quando anche l'output elaborato è di grandi dimensioni, scriva i risultati in modo incrementale invece di accumulare tutto in memoria e scrivere al termine. Apra un file CSV e aggiunga ogni blocco elaborato usando mode='a' e header=False per i blocchi successivi. In questo modo l'utilizzo della memoria della pipeline di output rimane costante e può esaminare i risultati parziali prima del completamento dell'intera esecuzione.

import pandas as pd

first_chunk = True
for chunk in pd.read_csv('input.csv', chunksize=100000):
    # Transform
    processed = chunk[chunk['status'] == 'active'].copy()
    processed['revenue_usd'] = processed['revenue'] * 1.10

    # Write incrementally
    mode = 'w' if first_chunk else 'a'
    processed.to_csv('output.csv',
                     mode=mode,
                     header=first_chunk,
                     index=False)
    first_chunk = False

print('Done writing output.csv')

Stimare la dimensione ottimale dei blocchi

La scelta di chunksize richiede un compromesso: un valore troppo piccolo comporta molte iterazioni del ciclo Python e un sovraccarico elevato; uno troppo grande fa sì che i blocchi non entrino nella RAM. Un approccio pratico consiste nel caricare un blocco, misurarne l'utilizzo della memoria con chunk.memory_usage(deep=True).sum() e impostare chunksize in modo che ogni blocco utilizzi circa il 10–20% della RAM disponibile. psutil.virtual_memory().available di Python restituisce la RAM disponibile durante l'esecuzione, consentendo di calcolare chunksize in modo adattivo.

import pandas as pd

# Sample 1000 rows to estimate per-row memory
sample = pd.read_csv('big_file.csv', nrows=1000)
bytes_per_row = sample.memory_usage(deep=True).sum() / 1000
print(f'Bytes per row: {bytes_per_row:.0f}')

# Target: use at most 500 MB per chunk
target_bytes = 500 * 1024 * 1024
optimal_chunksize = int(target_bytes / bytes_per_row)
print(f'Recommended chunksize: {optimal_chunksize:,}')

Combinare in modo efficiente i risultati dei blocchi

Quando accumula molti DataFrame relativi ai blocchi in un elenco e li concatena in seguito, tenga presente che chiamare pd.concat su centinaia di DataFrame piccoli è lento a causa delle ripetute allocazioni di memoria. Un modello migliore consiste nell'aggregare all'interno di ogni blocco e memorizzare solo il piccolo risultato aggregato, non l'intero blocco. Se ha davvero bisogno di tutte le righe, scrivere progressivamente in un file Parquet (usando pyarrow) è più veloce di usare pd.concat al termine.

import pandas as pd

# Efficient: aggregate first, small list of scalars
running_total = 0
running_count = 0

for chunk in pd.read_csv('sales.csv', chunksize=100000):
    running_total += chunk['amount'].sum()
    running_count += chunk['amount'].count()

print(f'Mean amount: {running_total / running_count:.2f}')

# Avoid: accumulating full chunk DataFrames
# results = []
# for chunk in reader:
#     results.append(chunk)   # memory grows to full file size
# df = pd.concat(results)     # slow for hundreds of chunks

Verifica rapida

Verifichi la Sua comprensione dei concetti di analisi dei dati presentati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che chunksize in pd.read_csv restituisce un iteratore di DataFrame che consente di elaborare file di grandi dimensioni con un uso efficiente della memoria, che gli argomenti usecols e dtype riducono la memoria necessaria per ogni blocco e velocizzano l'analisi, e che gli accumulatori progressivi (somma, conteggio, risultati parziali) evitano di creare un elenco contenente tutti i blocchi. Nella prossima lezione analizzeremo più in dettaglio i modelli di aggregazione incrementale tra i blocchi.

Domande Frequenti

La lezione «CSV in streaming con chunksize» è gratuita?

Sì — il testo completo di «CSV in streaming con chunksize» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Pandas & NumPy Academy, passa a CoddyKit PRO. Il corso Pandas & NumPy Academy include 4 lezioni in totale.

Cosa imparerò in «CSV in streaming con chunksize»?

Legga un CSV di grandi dimensioni in blocchi di dimensione fissa con pd.read_csv(chunksize=), elabori ogni blocco e concateni o accumuli i risultati. Eserciti Pandas & NumPy Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Pandas & NumPy Academy?

Non è richiesta alcuna esperienza precedente. Pandas & NumPy Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «CSV in streaming con chunksize»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Pandas & NumPy Academy?

Sì. Ogni lezione Pandas & NumPy Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. CSV in streaming con chunksize
  2. Aggregazione incrementale tra i blocchi
  3. Introduzione ai DataFrame Dask
  4. Parquet: archiviazione colonnare veloce
← Torna a Pandas & NumPy Academy