Pandas Cheat Sheet
Ultimo aggiornamento
Creare un DataFrame
Piccoli DataFrame costruiti a mano, per esempi, test e tabelle di riferimento.
| Attività | Codice |
|---|---|
| Importare pandas e NumPy | import pandas as pd
import numpy as np |
| Da un dict di colonne | df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]}) |
| Da una lista di righe | pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}]) |
| Da un array NumPy | pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"]) |
| Una Series con etichette | s = pd.Series([3, 5, 2], index=["a", "b", "c"]) |
| Un intervallo di date | pd.date_range("2026-01-01", periods=7, freq="D") |
Leggere e scrivere file
Una chiamata carica un file in un DataFrame, un'altra lo riscrive su disco.
| Attività | Codice |
|---|---|
| Leggere un file CSV | df = pd.read_csv("sales.csv") |
| Solo alcune colonne, con date vere | pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"]) |
| Usare una colonna come indice | pd.read_csv("sales.csv", index_col="id") |
| Tab o altro separatore | pd.read_csv("sales.tsv", sep="\t") |
| Altri marcatori come valori mancanti | pd.read_csv("sales.csv", na_values=["n/a", "-"]) |
| Leggere solo le prime righe | pd.read_csv("sales.csv", nrows=1000) |
| Leggere un foglio Excel (serve openpyxl) | pd.read_excel("report.xlsx", sheet_name="2026") |
| Leggere record JSON | pd.read_json("records.json") |
| Leggere da un database SQL | pd.read_sql("SELECT * FROM orders", conn) |
| Scrivere un CSV senza indice | df.to_csv("clean.csv", index=False) |
| Scrivere Excel o Parquet | df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet") |
Esplorare i dati
Le prime chiamate da fare su qualsiasi nuovo dataset.
| Attività | Codice |
|---|---|
| Prime e ultime righe | df.head(), df.tail(3) |
| Numero di righe e colonne | df.shape # (6, 7) |
| Tipi delle colonne e valori non nulli | df.info() |
| Statistiche delle colonne numeriche | df.describe() |
| Riepilogo di tutte le colonne | df.describe(include="all") |
| Nomi e tipi delle colonne | df.columns, df.dtypes |
| Contare ogni valore | df["city"].value_counts() |
| Percentuale di ogni valore | df["city"].value_counts(normalize=True) |
| Valori distinti e quanti sono | df["city"].unique(), df["city"].nunique() |
| Valori mancanti per colonna | df.isna().sum() |
| Campione casuale di righe | df.sample(3, random_state=0) |
| Correlazione tra colonne numeriche | df.corr(numeric_only=True) |
Selezionare colonne e righe
loc seleziona per etichetta e include la fine di uno slice. iloc seleziona per posizione e la esclude.
| Attività | Codice |
|---|---|
| Una colonna, come Series | df["age"] |
| Più colonne, come DataFrame | df[["name", "age"]] |
| Righe e colonne per etichetta | df.loc[0:2, ["name", "age"]] |
| Righe e colonne per posizione | df.iloc[0:2, 0:3] |
| Un singolo valore | df.at[0, "age"], df.iat[0, 3] |
| Ultima riga | df.iloc[-1] |
| Colonne di un certo tipo | df.select_dtypes(include="number") |
| Colonne di testo (pandas 3) | df.select_dtypes(include="str") |
| Colonne per corrispondenza nel nome | df.filter(like="sal"), df.filter(regex="^(age|salary)$") |
| Cercare righe tramite una colonna | df.set_index("name").loc["Ada", "salary"] |
| Tornare a un indice 0, 1, 2 | df.reset_index(drop=True) |
| Eliminare colonne o righe | df.drop(columns=["email"]), df.drop(index=[0, 1]) |
Filtrare le righe
Metti ogni condizione tra parentesi e combinale con & (and), | (or) e ~ (not).
| Attività | Codice |
|---|---|
| Righe che soddisfano una condizione | df[df["age"] > 30] |
| Entrambe le condizioni | df[(df["age"] > 30) & (df["city"] == "Oslo")] |
| Almeno una condizione | df[(df["city"] == "Oslo") | (df["salary"] > 6000)] |
| Valore in una lista | df[df["city"].isin(["Oslo", "Rome"])] |
| Valore non in una lista | df[~df["city"].isin(["Oslo", "Rome"])] |
| Tra due valori, estremi inclusi | df[df["age"].between(30, 40)] |
| Filtrare con una query testuale | df.query("age > 30 and city == 'Oslo'") |
| Usare una variabile in una query | min_age = 30
df.query("age > @min_age") |
| Il testo contiene | df[df["name"].str.contains("e", case=False)] |
| Valori mancanti o presenti | df[df["age"].isna()], df[df["age"].notna()] |
| Filtrare righe e scegliere colonne | df.loc[df["age"] > 30, ["name", "salary"]] |
| Le prime o ultime n righe | df.nlargest(3, "salary"), df.nsmallest(2, "age") |
| Righe con un valore ripetuto | df[df.duplicated(subset=["city"], keep=False)] |
Ordinare e classificare
| Attività | Codice |
|---|---|
| Ordinare per una colonna | df.sort_values("salary") |
| Prima i più grandi | df.sort_values("salary", ascending=False) |
| Ordinare per più colonne | df.sort_values(["city", "salary"], ascending=[True, False]) |
| Prima i valori mancanti | df.sort_values("age", na_position="first") |
| Ordinare per indice | df.sort_index() |
| Classifica, 1 è il più alto | df["salary"].rank(ascending=False) |
Aggiungere e modificare colonne
Le operazioni su intere colonne sono veloci. Usa apply solo quando nessun metodo integrato fa al caso tuo.
| Attività | Codice |
|---|---|
| Nuova colonna da altre colonne | df["bonus"] = df["salary"] * 0.1 |
| Aggiungere una colonna in una catena | df.assign(bonus=lambda d: d["salary"] * 0.1) |
| Valore in base a una condizione | df["level"] = np.where(df["age"] >= 40, "senior", "junior") |
| Valore in base a più condizioni | df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior") |
| Cambiare i valori dove vale una condizione | df.loc[df["city"] == "Lima", "salary"] = 4000 |
| Mappare i valori con un dict | df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"}) |
| Applicare una funzione a una colonna | df["salary"].apply(lambda x: round(x, -3)) |
| Applicare una funzione a ogni cella | df[["age", "salary"]].map(lambda x: x / 1000) |
| Rinominare le colonne | df.rename(columns={"salary": "pay"}) |
| Cambiare il tipo di una colonna | df["salary"].astype("float64") |
| Testo in numeri, valori errati a NaN | pd.to_numeric(s, errors="coerce") |
| Numeri in fasce con etichetta | pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"]) |
| Numeri in gruppi di pari dimensione | pd.qcut(df["salary"], q=4, labels=False) |
| Ciclare sulle righe (lento, ultima risorsa) | for row in df.itertuples():
print(row.name, row.salary) |
Valori mancanti e pulizia
I numeri mancanti appaiono come NaN, le date mancanti come NaT. Ognuna di queste chiamate restituisce un nuovo oggetto, quindi assegna il risultato.
| Attività | Codice |
|---|---|
| Percentuale di mancanti per colonna | df.isna().mean() |
| Eliminare righe con valori mancanti | df.dropna() |
| Eliminare righe senza una colonna chiave | df.dropna(subset=["age"]) |
| Riempire con un valore per colonna | df.fillna({"age": df["age"].median()}) |
| Riempire dalla riga precedente o successiva | df["age"].ffill(), df["age"].bfill() |
| Riempire i buchi per interpolazione | df["age"].interpolate() |
| Trasformare marcatori in valori mancanti | df.replace(["n/a", "-", ""], np.nan) |
| Eliminare righe duplicate | df.drop_duplicates() |
| Tenere l'ultima riga per chiave | df.drop_duplicates(subset=["city"], keep="last") |
| Sistemare i nomi delle colonne | df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") |
| Limitare i valori estremi | df["salary"].clip(lower=4000, upper=6000) |
| Tipo category per testo ripetuto | df["city"] = df["city"].astype("category") |
Colonne di testo (.str)
In pandas 3 le colonne di testo hanno di default il dtype str. I metodi .str lavorano sull'intera colonna e i valori mancanti restano mancanti.
| Attività | Codice |
|---|---|
| Maiuscolo, minuscolo o iniziali maiuscole | df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title() |
| Togliere gli spazi | df["name"].str.strip() |
| Sostituire testo | df["email"].str.replace("mail.com", "example.com") |
| Sostituire con una regex | df["email"].str.replace(r"@.*", "", regex=True) |
| Contiene, inizia con | df["name"].str.contains("a"), df["name"].str.startswith("E") |
| Lunghezza di ogni valore | df["name"].str.len() |
| Primi caratteri | df["name"].str[:2] |
| Dividere in colonne | df["email"].str.split("@", expand=True) |
| Estrarre con un gruppo regex | df["email"].str.extract(r"@(\w+)") |
| Unire colonne di testo | df["name"] + " (" + df["city"] + ")" |
| Riempire i numeri con zeri | df["salary"].astype("str").str.zfill(6) |
| Il nuovo dtype per il testo | df["name"].dtype # str |
Date e orari
L'accessor .dt funziona sulle colonne datetime. Un indice di date ordinato (ts qui sotto) permette lo slicing con stringhe e il resampling.
| Attività | Codice |
|---|---|
| Convertire una colonna in datetime | df["date"] = pd.to_datetime(df["date"]) |
| Convertire con un formato esatto | pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y") |
| Anno, mese, nome del giorno | df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name() |
| Raggruppare le date per mese | df["date"].dt.to_period("M") |
| Giorni tra due date | (pd.Timestamp("2026-12-31") - df["date"]).dt.days |
| Aggiungere giorni o mesi | df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1) |
| Righe in un intervallo di date | df[df["date"].between("2021-01-01", "2022-12-31")] |
| Indice di date, slicing con stringhe | ts = df.set_index("date").sort_index()
ts.loc["2021":"2022"] |
| Totale per mese | ts["salary"].resample("ME").sum() |
Media settimanale; "QE" è trimestrale, "YE" annuale | ts["salary"].resample("W").mean() |
| Media mobile | ts["salary"].rolling(3).mean() |
| Variazione rispetto alla riga precedente | ts["salary"].diff(), ts["salary"].pct_change() |
| Spostare i valori giù di una riga | ts["salary"].shift(1) |
| Totale cumulativo | ts["salary"].cumsum() |
| Impostare e convertire i fusi orari | ts.index.tz_localize("UTC").tz_convert("Europe/Oslo") |
Raggruppare e aggregare
groupby divide le righe per chiave, applica una funzione a ogni gruppo e combina i risultati.
| Attività | Codice |
|---|---|
| Media per gruppo | df.groupby("city")["salary"].mean() |
| Più statistiche insieme | df.groupby("city")["salary"].agg(["mean", "max", "count"]) |
| Colonne risultato con nome | df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count")) |
| Raggruppare per due colonne | df.groupby(["city", "team"])["salary"].sum() |
| Tenere le chiavi come colonne | df.groupby("city", as_index=False)["salary"].mean() |
| Numero di righe per gruppo | df.groupby("city").size() |
| Risultato del gruppo su ogni riga | df["city_avg"] = df.groupby("city")["salary"].transform("mean") |
| Tenere solo alcuni gruppi | df.groupby("city").filter(lambda g: len(g) >= 2) |
| Riga con il massimo per gruppo | df.loc[df.groupby("city")["salary"].idxmax()] |
| Prime n righe per gruppo | df.sort_values("salary", ascending=False).groupby("city").head(2) |
| Numerare le righe di ogni gruppo | df.groupby("city").cumcount() |
| Ciclare sui gruppi | for city, group in df.groupby("city"):
print(city, len(group)) |
| Filtrare, raggruppare e ordinare in una catena | (df.query("age > 30")
.groupby("city", as_index=False)
.agg(avg_pay=("salary", "mean"))
.sort_values("avg_pay", ascending=False)) |
Pivot e rimodellamento
I dati in formato lungo hanno una riga per osservazione. Quelli in formato largo distribuiscono una variabile su più colonne.
| Attività | Codice |
|---|---|
| Tabella pivot | df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0) |
| Tabella pivot con totali | df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True) |
| Contare le combinazioni | pd.crosstab(df["city"], df["team"]) |
| Percentuali per riga | pd.crosstab(df["city"], df["team"], normalize="index") |
| Da largo a lungo | long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value") |
| Da lungo a largo | long.pivot(index="name", columns="field", values="value") |
| Livello interno dell'indice in colonne | df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0) |
| Una riga per elemento della lista | pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags") |
| One-hot encoding di una colonna | pd.get_dummies(df["team"], prefix="team") |
| Scambiare righe e colonne | df.T |
Combinare DataFrame
concat impila i frame, merge li unisce su colonne chiave come una join SQL.
| Attività | Codice |
|---|---|
| Impilare righe | pd.concat([df_2025, df_2026], ignore_index=True) |
| Affiancare | pd.concat([left, right], axis=1) |
| Join su una colonna chiave | orders.merge(customers, on="customer_id", how="left") |
| Tipi di join | how="inner", "left", "right", "outer" |
| Chiavi con nomi diversi | orders.merge(users, left_on="customer_id", right_on="id") |
| Verificare la join e segnare la provenienza | orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True) |
| Join sull'indice | left.join(right, how="inner") |
| Righe senza corrispondenza in un'altra tabella | orders[~orders["customer_id"].isin(customers["customer_id"])] |
Grafici con pandas
pandas disegna con Matplotlib. Ogni chiamata di plot restituisce un ax di Matplotlib, così puoi continuare a personalizzarlo.
| Attività | Codice |
|---|---|
| Grafico a linee di una serie | ts["salary"].plot() |
| Grafico a linee da due colonne | df.plot(x="date", y="salary") |
| Grafico a barre di un riepilogo | df.groupby("city")["salary"].mean().plot.bar() |
| Barre orizzontali ordinate | df["city"].value_counts().sort_values().plot.barh() |
| Istogramma | df["salary"].plot.hist(bins=20) |
| Grafico a dispersione | df.plot.scatter(x="age", y="salary") |
| Box plot per gruppo | df.boxplot(column="salary", by="city") |
| Titolo ed etichetta dell'asse | ax = df.plot(x="date", y="salary", title="Salary")
ax.set_ylabel("EUR") |
| Mostrare o salvare il grafico | plt.show(), ax.figure.savefig("salary.png", dpi=150) |
pandas 3: vecchie forme e cosa scrivere ora
Il codice copiato da tutorial più vecchi spesso dà errore o avvisi in pandas 3. Usa invece le forme della colonna pandas 3.
| Attività | pandas 3 | Vecchia forma |
|---|---|---|
| Funzione su ogni cella | df.map(f) | df.applymap(f) |
| Riempire dalla riga precedente | df.ffill() | df.fillna(method="ffill") |
| Aggiungere righe | pd.concat([df, new_rows]) | df.append(new_rows) |
| Resampling a fine mese | ts["salary"].resample("ME").sum() | resample("M") |
| Date orarie | pd.date_range("2026-01-01", periods=24, freq="h") | freq="H" |
| Impostare un singolo valore | df.loc[0, "age"] = 37 | df["age"][0] = 37 |
| Modificare un sottoinsieme filtrato | high = df[df["salary"] > 5000]
high["salary"] = 0 | high = df[df["salary"] > 5000].copy() |
| È una colonna di testo? | df["name"].dtype == "str" | df["name"].dtype == object |
Il codice pandas che un data analyst usa tutti i giorni, raggruppato per attività: creare e caricare dati, esplorarli, selezionare e filtrare righe, aggiungere colonne, gestire i valori mancanti, lavorare con testo e date, raggruppare e aggregare, rimodellare, unire e creare grafici. Ogni riga è un breve snippet da incollare, con una riga che spiega cosa fa.
Tutto è scritto per pandas 3, la versione che usa Coddy: le colonne di testo hanno il nuovo dtype str, il Copy-on-Write è sempre attivo e le API rimosse come applymap e fillna(method=...) compaiono solo nella tabella delle vecchie forme. Gli esempi usano un DataFrame df con le colonne name, city, team, age, salary, date e email. Sei alle prime armi con Python? Parti dal Python cheat sheet e dal playground Python, e tieni a portata di mano anche i cheat sheet di NumPy, Matplotlib e scikit-learn.
FAQ sul cheat sheet di pandas
Questo cheat sheet di pandas è gratuito?
Quale versione di pandas copre questo cheat sheet?
str, il Copy-on-Write è sempre attivo e le chiamate rimosse in pandas 3 (applymap, fillna(method=...), append, gli alias di frequenza M e H) compaiono solo nell'ultima tabella, accanto a cosa scrivere al loro posto. Quasi tutti gli snippet funzionano anche su pandas 2.2, tranne le righe che si basano sul dtype str o sul Copy-on-Write.Qual è la differenza tra loc e iloc?
loc seleziona per etichetta, cioè i valori dell'indice e i nomi delle colonne, e uno slice come df.loc[0:2] include la fine. iloc seleziona per posizione intera, come una lista Python, quindi df.iloc[0:2] restituisce le prime due righe ed esclude la fine. Usa loc con una condizione per filtrare le righe e scegliere le colonne in un solo passaggio: df.loc[df["age"] > 30, ["name"]].Come filtro un DataFrame con più condizioni?
& per and, | per or e ~ per not: df[(df["age"] > 30) & (df["city"] == "Oslo")]. Gli operatori Python and e or non funzionano su intere colonne. Per filtri lunghi, df.query("age > 30 and city == 'Oslo'") è più leggibile.Qual è la differenza tra merge, join e concat?
merge combina due DataFrame su colonne chiave, come una join SQL, con how="inner", "left", "right" o "outer". join è una scorciatoia che fa il merge sull'indice. concat non confronta nessuna chiave: impila i frame uno sopra l'altro, oppure li affianca con axis=1.Perché la mia assegnazione non modifica il DataFrame?
df["age"][0] = 37 modifica un oggetto temporaneo e lascia df invariato. Scrivila invece come un'unica chiamata loc: df.loc[0, "age"] = 37. Anche metodi come dropna e fillna restituiscono un nuovo oggetto, quindi assegna il risultato.