▦ Dati e database

Analisi dei dati con pandas: i primi passi con un caso concreto

Caricare, esplorare, pulire, raggruppare e unire dati con pandas in Python, partendo da un file di vendite e con gli errori da evitare subito.

Quando un foglio di calcolo diventa troppo lento o le stesse operazioni vanno ripetute ogni mese, pandas è spesso il passo successivo naturale. È una libreria Python per lavorare con dati tabellari: permette di caricare un file, pulirlo, trasformarlo e riassumerlo con poche righe di codice, che si possono rieseguire identiche sul file del mese dopo. Vediamo i passaggi fondamentali su un esempio concreto: un'esportazione delle vendite di un negozio online.

Caricare i dati

Il file vendite.csv contiene le colonne data, codice_ordine, prodotto, categoria, quantita, prezzo, regione.

import pandas as pd

df = pd.read_csv(
    "vendite.csv",
    sep=";",
    decimal=",",
    parse_dates=["data"],
    dayfirst=True,
    dtype={"codice_ordine": str},
)

Il risultato è un DataFrame, l'oggetto centrale di pandas: una tabella con righe e colonne con un nome, simile a un foglio di calcolo. I parametri non sono un dettaglio: indicano il separatore italiano, la virgola decimale, quali colonne sono date e in che ordine sono scritti giorno e mese, e che il codice d'ordine è testo anche se contiene solo cifre.

Guardare i dati prima di usarli

Il primo passo di ogni analisi è capire che cosa si ha davanti:

df.shape        # numero di righe e colonne
df.head()       # le prime cinque righe
df.dtypes       # il tipo di ogni colonna
df.describe()   # statistiche di base sulle colonne numeriche
df.isna().sum() # valori mancanti per colonna

Questi cinque comandi rivelano la maggior parte dei problemi. Se prezzo risulta di tipo object invece che numerico, qualche valore non è stato riconosciuto come numero. Se describe() mostra una quantità minima negativa, ci sono resi o errori da capire. Se una colonna ha molti valori mancanti, bisogna decidere come trattarli prima di fare qualunque calcolo.

Pulire

Alcune operazioni frequenti:

# spazi e maiuscole incoerenti
df["regione"] = df["regione"].str.strip().str.title()

# righe completamente duplicate
df = df.drop_duplicates()

# valori non validi
df = df[df["quantita"] > 0]

# nuova colonna calcolata
df["importo"] = df["quantita"] * df["prezzo"]

Ogni operazione restituisce un nuovo risultato, che va riassegnato. È un errore comune scrivere df.drop_duplicates() senza riassegnare e pensare di aver tolto i duplicati.

Le operazioni lavorano su intere colonne alla volta: df["quantita"] * df["prezzo"] moltiplica tutte le righe insieme. Scrivere un ciclo for che scorre le righe una per una funziona, ma è molto più lento e quasi sempre non necessario.

Filtrare

Le righe si selezionano con condizioni tra parentesi quadre:

marzo = df[(df["data"] >= "2025-03-01") & (df["data"] < "2025-04-01")]
nord = df[df["regione"].isin(["Lombardia", "Piemonte", "Veneto"])]

Le condizioni multiple si combinano con & (e) e | (o), e ciascuna va racchiusa tra parentesi. Usare le parole and e or di Python produce un errore.

Raggruppare e riassumere

È l'equivalente di GROUP BY in SQL e delle tabelle pivot nei fogli di calcolo:

per_categoria = (
    df.groupby("categoria")
      .agg(ordini=("codice_ordine", "nunique"),
           pezzi=("quantita", "sum"),
           fatturato=("importo", "sum"))
      .sort_values("fatturato", ascending=False)
)

Il risultato è una tabella con una riga per categoria: il numero di ordini distinti, i pezzi venduti e il fatturato, ordinata dalla categoria più importante. Contare gli ordini con nunique invece che con count evita di contare più volte lo stesso ordine quando contiene più prodotti.

Per un andamento mensile:

mensile = df.groupby(df["data"].dt.to_period("M"))["importo"].sum()

E per una vera tabella pivot, con le regioni in riga e i mesi in colonna:

pivot = df.pivot_table(index="regione",
                       columns=df["data"].dt.to_period("M"),
                       values="importo", aggfunc="sum", fill_value=0)

Unire due tabelle

Se i costi dei prodotti sono in un altro file, si uniscono come con una JOIN:

costi = pd.read_csv("costi.csv", sep=";", decimal=",")
df = df.merge(costi, on="prodotto", how="left", validate="many_to_one")
df["margine"] = df["importo"] - df["quantita"] * df["costo"]

Il parametro validate è un'assicurazione preziosa: se nel file dei costi lo stesso prodotto compare due volte, pandas segnala un errore invece di duplicare silenziosamente le righe delle vendite e gonfiare i totali. Dopo l'unione conviene controllare quanti prodotti non hanno trovato un costo, con df["costo"].isna().sum().

Salvare i risultati

per_categoria.to_csv("riepilogo_categorie.csv", sep=";", decimal=",")
per_categoria.to_excel("riepilogo.xlsx")  # richiede la libreria openpyxl

Il vantaggio rispetto al lavoro manuale è che lo script resta: il mese successivo basta sostituire il file di partenza e rieseguirlo, ottenendo gli stessi passaggi applicati nello stesso modo.

In sintesi

  • read_csv va configurato con separatore, decimali, date e tipi delle colonne di codici.
  • shape, head, dtypes, describe e isna vanno usati prima di ogni analisi.
  • Le operazioni lavorano su colonne intere e il risultato va riassegnato.
  • groupby, agg e pivot_table riassumono i dati; nunique evita conteggi doppi.
  • In merge il parametro validate impedisce duplicazioni silenziose delle righe.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica e in psicologia, unisce l'analisi tecnica alla comprensione del fattore umano.

Leggi anche