Pandas: Series e DataFrame, il foglio di calcolo che vive dentro Python

Hai un CSV da analizzare e stai per aprirlo con un ciclo for e un dizionario? Fermati: Pandas ti dà Series e DataFrame, tabelle indicizzate che si interrogano in una riga. Tutorial pratico con codice eseguibile per partire da zero.

Hai presente quel momento in cui qualcuno ti passa un file CSV — vendite, misurazioni, risultati di un esperimento — e tu apri l’editor pronto a scrivere open("dati.csv"), un ciclo for, uno split(",") e un dizionario per accumulare i totali? Venti righe dopo funziona, ma è fragile: una virgola dentro un campo e tutto si rompe, una colonna in più e riscrivi mezzo script. Oggi impari lo strumento che rende tutto questo una riga sola: Pandas, la libreria che porta il foglio di calcolo dentro Python — ma programmabile, riproducibile e molto più veloce delle tue dita su Excel.

Series e DataFrame, selezione e filtri, raggruppamenti e valori mancanti, e i grafici: nel libro.

Leggi «Pandas e Matplotlib: dati e visualizzazione» nel libro →

Prerequisiti in una riga: se non ricordi bene NumPy, ti basta sapere che i suoi array sono liste «compatte» di numeri dello stesso tipo, su cui le operazioni si applicano a tutti gli elementi in un colpo solo senza cicli — Pandas è costruito proprio sopra NumPy. Ambiente: Python 3.12 in un ambiente virtuale (python -m venv .venv e poi pip install pandas, oppure uv venv && uv pip install pandas se usi uv), un editor come VS Code o PyCharm.

Perché Pandas, se ho già NumPy?

Un array NumPy è perfetto quando i dati sono omogenei: una griglia di numeri, tutti dello stesso tipo. Ma i dati veri raramente sono così. Un dataset di clienti ha nomi (stringhe), età (interi), spese (decimali), date di iscrizione. E soprattutto: le righe e le colonne significano qualcosa. La colonna 2 non è «la colonna 2», è «la spesa mensile»; la riga 40 non è «la riga 40», è «il cliente Rossi». NumPy ti costringe a ricordare a memoria queste corrispondenze; Pandas le scrive direttamente sulla struttura dati. È la differenza tra un magazzino di scatoloni anonimi tutti uguali e uno schedario con l’etichetta su ogni cassetto: il contenuto può essere lo stesso, ma nel secondo trovi quello che cerchi senza contare i cassetti.

La Series: un array con le etichette attaccate

Il mattone base di Pandas è la Series: una singola colonna di dati con un’etichetta per ogni valore. Immaginala come una colonna di foglio di calcolo dove ogni cella ha un post-it a fianco che dice a cosa si riferisce.

# series_base.py — Python 3.12, richiede: pip install pandas
import pandas as pd

# Una Series da un dizionario: le chiavi diventano l'indice
temperature = pd.Series({
    "Milano": 31.5,
    "Roma": 34.0,
    "Napoli": 33.2,
    "Torino": 29.8,
})

print(temperature)
# Milano    31.5
# Roma      34.0
# Napoli    33.2
# Torino    29.8
# dtype: float64

# Accedi per etichetta, come in un dizionario...
print(temperature["Roma"])        # 34.0

# ...ma operi su tutto in un colpo, come in NumPy
print(temperature - temperature.mean())   # scarto dalla media, città per città
print(temperature[temperature > 31])      # solo le città sopra i 31 gradi

Guarda l’ultima riga: hai filtrato i dati scrivendo la condizione, non il ciclo che la verifica. Questo è lo stile che Pandas ti chiede di adottare — dichiari cosa vuoi, non come ottenerlo. Se arrivi da anni di cicli for all’inizio sembra magia; dopo una settimana non torni più indietro.

Il DataFrame: tante Series che condividono lo stesso indice

Un DataFrame è la tabella completa: più colonne (ognuna è una Series) allineate su uno stesso indice di righe. Se la Series è una colonna con i post-it, il DataFrame è lo schedario intero: ogni cassetto (riga) ha la sua etichetta, e ogni tipo di contenuto (colonna) ha il suo nome.

# dataframe_base.py
import pandas as pd

# Un DataFrame da un dizionario di liste: chiavi = nomi colonna
citta = pd.DataFrame(
    {
        "temperatura": [31.5, 34.0, 33.2, 29.8],
        "umidita": [58, 45, 62, 66],
        "regione": ["Lombardia", "Lazio", "Campania", "Piemonte"],
    },
    index=["Milano", "Roma", "Napoli", "Torino"],  # etichette di riga
)

print(citta)
#         temperatura  umidita    regione
# Milano         31.5       58  Lombardia
# Roma           34.0       45      Lazio
# Napoli         33.2       62   Campania
# Torino         29.8       66   Piemonte

# Estrarre una colonna restituisce... una Series!
print(citta["temperatura"])

# Le prime domande da fare SEMPRE a un DataFrame nuovo:
print(citta.shape)      # (4, 3) — 4 righe, 3 colonne
print(citta.dtypes)     # il tipo di ogni colonna
citta.info()            # riepilogo: tipi, valori non nulli, memoria
print(citta.describe()) # statistiche (media, min, max...) delle colonne numeriche

Nota il dettaglio importante: colonne diverse possono avere tipi diversi (numeri decimali, interi, stringhe), ma dentro una colonna il tipo è uno solo. È il compromesso perfetto tra la rigidità di NumPy e il caos delle liste Python.

L’indice: il superpotere silenzioso

La parola chiave di tutto l’articolo è indice. In una lista Python la posizione è l’unico modo di raggiungere un elemento; in Pandas ogni riga ha un’etichetta stabile che la identifica, e ogni colonna un nome. Quando allinei, filtri o unisci dati, Pandas usa le etichette — non le posizioni — per far combaciare le cose. Se sommi due Series con città in ordine diverso, Pandas le allinea da solo per etichetta: niente più bug da «riga sbagliata».

DataFrame = tabella indicizzata temperatura umidita regione Milano Roma Napoli Torino 31.558Lombardia 34.045Lazio 33.262Campania 29.866Piemonte df[«temperatura»] Series Milano31.5 Roma34.0 Napoli33.2 Torino29.8 indice (etichette di riga) nomi di colonna valori della Series estratta Ogni colonna è una Series; tutte condividono lo stesso indice.

La tabella indicizzata: l’indice (terracotta) etichetta le righe, i nomi di colonna (teal) etichettano le Series. Estrarre una colonna restituisce una Series che porta con sé l’indice.

In NumPy sei tu a ricordare cosa significa la colonna 2; in Pandas è la tabella stessa a saperlo. L’indice non è un dettaglio: è il contratto che tiene allineati i tuoi dati.

Dati veri: read_csv in una riga

Finora abbiamo costruito le tabelle a mano, ma nella vita reale i dati arrivano da file. Ed è qui che Pandas ripaga davvero: quel parser CSV artigianale di venti righe diventa una chiamata sola, che gestisce per te virgolette, tipi di dato e intestazioni.

# leggi_csv.py
import pandas as pd

# Prima crea un CSV di prova (nella pratica lo riceverai già pronto)
with open("vendite.csv", "w", encoding="utf-8") as f:
    f.write("data,prodotto,quantita,prezzo\n")
    f.write("2026-07-01,tastiera,3,49.90\n")
    f.write("2026-07-01,mouse,5,19.90\n")
    f.write("2026-07-02,monitor,2,189.00\n")
    f.write("2026-07-03,tastiera,1,49.90\n")

# La riga che sostituisce il parser fatto a mano:
vendite = pd.read_csv("vendite.csv", parse_dates=["data"])

print(vendite.head())        # le prime righe, per dare un'occhiata
print(vendite.dtypes)        # 'data' è datetime, 'quantita' int, 'prezzo' float

# Una colonna calcolata da altre due: niente cicli, solo aritmetica
vendite["incasso"] = vendite["quantita"] * vendite["prezzo"]
print(vendite)
print(f"Totale incassato: {vendite['incasso'].sum():.2f} euro")

Il parametro parse_dates merita una nota: dice a Pandas di interpretare quella colonna come date vere, non stringhe. Da lì in poi puoi filtrare per mese, calcolare differenze tra giorni, raggruppare per settimana — tutto gratis. Esistono cugini per ogni formato: read_excel, read_json, read_parquet, read_sql.

Quando Pandas NON serve

Onestà prima di tutto: Pandas non è la risposta a ogni problema con i dati. Se devi leggere un file di configurazione o dieci righe di valori, il modulo csv della standard library è più che sufficiente e non aggiunge una dipendenza. Se lavori con dataset da decine di gigabyte che non stanno in RAM, Pandas soffre: lì guarda Polars (API simile, molto più veloce) o DuckDB (SQL direttamente sui file). E se i tuoi dati sono una griglia numerica omogenea per calcolo puro — pixel, matrici, tensori — resta su NumPy: le etichette che non usi sono solo peso. Pandas brilla nella zona di mezzo, che poi è dove vive il 90% del lavoro dati reale: tabelle eterogenee da qualche riga a qualche milione, da esplorare, pulire e trasformare.

Prova tu

Esercizio concreto: costruisci un DataFrame con i dati di 6-8 film che conosci bene — colonne titolo, anno, durata_min, voto (il tuo, da 1 a 10), con il titolo come indice. Poi rispondi a tre domande usando solo Pandas, senza cicli: qual è la durata media? Quali film hanno voto sopra la tua media? Aggiungi una colonna voto_per_ora (voto diviso durata in ore) e scopri quale film ti dà «più soddisfazione al minuto». Se ci riesci senza scrivere nemmeno un for, hai capito lo spirito di Pandas.

Per approfondire: la guida ufficiale «10 minutes to pandas» su pandas.pydata.org è il punto di partenza perfetto (e mente sul tempo: dedicale un’ora vera). Per andare a fondo, Python for Data Analysis di Wes McKinney — il creatore di Pandas — alla 3ª edizione è il riferimento definitivo, disponibile anche gratis online su wesmckinney.com/book. Nel prossimo articolo prendiamo questo DataFrame e impariamo a interrogarlo sul serio: selezioni, filtri e groupby.

I commenti sono riservati agli iscritti.

Accedi per commentare