Corso Python / Pulire e analizzare dati tabellari con pandas
pandas permette di leggere e analizzare dati in righe e colonne attraverso un DataFrame. Prima di aggregare, scegli i tipi, identifica gli importi non validi e rendi visibile il numero delle esclusioni: un totale ha significato solo se sai quali righe contiene.
Scegliere i tipi delle colonne e installare pandas
Un DataFrame rappresenta una tabella; una Series è una struttura monodimensionale usata, tra l’altro, per una colonna. Il tipo inferito da un CSV non coincide necessariamente con il significato dei dati: un ID come 001 deve rimanere una stringa se gli zeri iniziali sono importanti. Qui leggiamo le colonne come stringhe e convertiamo esplicitamente soltanto amount.
pandas è un pacchetto esterno, non un modulo della libreria standard. Seleziona un ambiente virtuale compatibile con il tuo Python e sistema operativo, poi installa in quell’ambiente:
python -m pip install pandas
La guida introduttiva ufficiale di pandas presenta lettura, selezione e aggregazione. Se pip segnala incompatibilità, verifica la versione dell’interprete e i requisiti del pacchetto prima di cambiare l’installazione di sistema.
Eseguire una validazione e un riepilogo completi
In una cartella di esercitazione distinta salva questo file UTF-8 come pandas_demo.py. Il CSV è incluso come stringa, perciò non serve ancora un file di dati esterno:
from io import StringIO
import pandas as pd
raw = "id,category,amount\ne1,food,3500\ne2,food,8000\ne3,transport,2500\ne4,food,bad\n"
df = pd.read_csv(StringIO(raw), dtype="string", keep_default_na=False)
amounts = pd.to_numeric(df["amount"], errors="coerce")
valid = amounts.notna() & (amounts >= 0) & (amounts % 1 == 0)
clean = df.loc[valid].copy()
clean["amount"] = amounts.loc[valid].astype("int64")
report = clean.groupby("category", as_index=False, sort=True)["amount"].sum()
print("accepted:", len(clean), "rejected:", int((~valid).sum()))
for row in report.itertuples(index=False):
print(row.category, row.amount)
Dal terminale nella cartella del programma, con l’ambiente selezionato, esegui python pandas_demo.py. Usa python3 se è il nome effettivo dell’interprete nel tuo ambiente macOS/Ubuntu. Il risultato previsto è:
accepted: 3 rejected: 1
food 11500
transport 2500
StringIO rende leggibile la stringa come un file. keep_default_na=False evita di interpretare automaticamente i marcatori testuali di valori mancanti durante questa lettura. La conversione numerica marca poi bad come mancante; la maschera sceglie i valori presenti, non negativi e senza parte frazionaria. Le tre righe accettate producono 11.500 per food e 2.500 per transport.
groupby raggruppa per categoria, sum somma gli importi e as_index=False mantiene la categoria come colonna del risultato. sort=True ordina le chiavi dei gruppi. itertuples(index=False) permette di stampare ogni riga senza includere l’indice del DataFrame.
Interpretare valori mancanti, maschere e copie
pd.to_numeric(..., errors="coerce") contrassegna come mancanti i testi che non riesce a convertire. Non li rende uguali a zero e non autorizza a ignorarli. Controlla la maschera valid, il conteggio di ~valid e le righe escluse. La documentazione di to_numeric descrive conversione e possibili perdite di precisione.
df.loc[valid].copy() crea la tabella selezionata su cui assegnare gli importi convertiti, lasciando il testo originale disponibile in df. L’assegnazione usa gli indici corrispondenti delle righe accettate. Puoi esaminare df.loc[~valid] per conservare gli scarti e spiegare come hai ottenuto il risultato.
L’esempio presume importi piccoli rappresentabili in int64. Il controllo numerico non è una validazione completa dei record: non rifiuta automaticamente ID duplicati, categorie vuote o identificatori mancanti. Aggiungi queste regole secondo il contratto dei tuoi dati. Anche la somma dei gruppi deve restare nel campo del tipo scelto; per interi enormi o importi decimali esatti serve una politica esplicita di precisione, intervallo e aggregazione.
Non confondere un valore matematicamente intero con una grammatica composta solo da cifre: un testo come 8.0 può diventare 8 e superare questa maschera. Il progetto è centrato su conversione e valori, mentre il programma CSV della libreria standard applica un contratto testuale più rigoroso agli importi.
Per leggere un file vero sostituisci soltanto la riga che costruisce df con pd.read_csv("expenses-input.csv", dtype={"id":"string", "category":"string", "amount":"string"}), lasciando conversione e maschera successive. In questa variante non è specificato keep_default_na=False: valgono le impostazioni predefinite per i valori mancanti. La documentazione di read_csv aiuta a scegliere consapevolmente marcatori e tipi. Riempire tutti gli importi mancanti con zero cancellerebbe la distinzione tra “nessun importo” e “importo realmente nullo”.
Salvare e riconciliare il rapporto
Per esportare il risultato, aggiungi questa riga alla fine di pandas_demo.py, dopo la creazione e la stampa di report:
report.to_csv("category-report.csv", index=False, encoding="utf-8")
Usa un percorso di uscita nuovo: la modalità predefinita di to_csv sostituisce un file omonimo. index=False omette la colonna dell’indice; non elimina la colonna category. Consulta la documentazione di DataFrame.to_csv per le opzioni di scrittura.
- Con l’ingresso originale verifica tre righe accettate, una esclusa e un totale di 14.000 sia nei dati puliti sia nel rapporto.
- Cambia soltanto
badin0: il conteggio diventa quattro accettate e zero escluse, mentre il totale rimane 14.000. - Cambia quel valore in
-1: tornano tre accettate e una esclusa. Confronta i conteggi, non soltanto il totale invariato. - Ripristina
bade controlla che accettate più escluse corrispondano alle quattro righe di partenza.
Apri il CSV prodotto e verifica le due categorie e i loro totali, senza una colonna aggiuntiva per l’indice. Conserva ingresso e scarti quando serve una traccia delle trasformazioni: il solo file aggregato non permette di ricostruire quali righe siano state escluse.




