Pandas Cheat Sheet
Zuletzt aktualisiert
DataFrame erstellen
Kleine DataFrames von Hand gebaut, für Beispiele, Tests und Lookup-Tabellen.
| Aufgabe | Code |
|---|---|
| pandas und NumPy importieren | import pandas as pd
import numpy as np |
| Aus einem Dict von Spalten | df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]}) |
| Aus einer Liste von Zeilen | pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}]) |
| Aus einem NumPy-Array | pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"]) |
| Eine Series mit Labels | s = pd.Series([3, 5, 2], index=["a", "b", "c"]) |
| Ein Datumsbereich | pd.date_range("2026-01-01", periods=7, freq="D") |
Dateien lesen und schreiben
Ein Aufruf lädt eine Datei in einen DataFrame, ein Aufruf schreibt sie zurück.
| Aufgabe | Code |
|---|---|
| CSV-Datei lesen | df = pd.read_csv("sales.csv") |
| Nur einige Spalten, mit echten Datumswerten | pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"]) |
| Spalte als Index verwenden | pd.read_csv("sales.csv", index_col="id") |
| Tab oder anderes Trennzeichen | pd.read_csv("sales.tsv", sep="\t") |
| Weitere Marker als fehlend werten | pd.read_csv("sales.csv", na_values=["n/a", "-"]) |
| Nur die ersten Zeilen lesen | pd.read_csv("sales.csv", nrows=1000) |
| Excel-Blatt lesen (braucht openpyxl) | pd.read_excel("report.xlsx", sheet_name="2026") |
| JSON-Records lesen | pd.read_json("records.json") |
| Aus einer SQL-Datenbank lesen | pd.read_sql("SELECT * FROM orders", conn) |
| CSV ohne Index schreiben | df.to_csv("clean.csv", index=False) |
| Excel oder Parquet schreiben | df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet") |
Daten untersuchen
Die ersten Aufrufe für jeden neuen Datensatz.
| Aufgabe | Code |
|---|---|
| Erste und letzte Zeilen | df.head(), df.tail(3) |
| Anzahl Zeilen und Spalten | df.shape # (6, 7) |
| Spaltentypen und Nicht-Null-Werte | df.info() |
| Kennzahlen der numerischen Spalten | df.describe() |
| Übersicht über alle Spalten | df.describe(include="all") |
| Spaltennamen und Typen | df.columns, df.dtypes |
| Jeden Wert zählen | df["city"].value_counts() |
| Anteil jedes Werts | df["city"].value_counts(normalize=True) |
| Eindeutige Werte und ihre Anzahl | df["city"].unique(), df["city"].nunique() |
| Fehlende Werte pro Spalte | df.isna().sum() |
| Zufällige Stichprobe von Zeilen | df.sample(3, random_state=0) |
| Korrelation numerischer Spalten | df.corr(numeric_only=True) |
Spalten und Zeilen auswählen
loc wählt nach Label aus und schließt das Ende eines Slice ein. iloc wählt nach Position aus und schließt es aus.
| Aufgabe | Code |
|---|---|
| Eine Spalte als Series | df["age"] |
| Mehrere Spalten als DataFrame | df[["name", "age"]] |
| Zeilen und Spalten nach Label | df.loc[0:2, ["name", "age"]] |
| Zeilen und Spalten nach Position | df.iloc[0:2, 0:3] |
| Ein einzelner Wert | df.at[0, "age"], df.iat[0, 3] |
| Letzte Zeile | df.iloc[-1] |
| Spalten eines Typs | df.select_dtypes(include="number") |
| Textspalten (pandas 3) | df.select_dtypes(include="str") |
| Spalten nach Namensmuster | df.filter(like="sal"), df.filter(regex="^(age|salary)$") |
| Zeilen über eine Spalte nachschlagen | df.set_index("name").loc["Ada", "salary"] |
| Zurück zum Index 0, 1, 2 | df.reset_index(drop=True) |
| Spalten oder Zeilen entfernen | df.drop(columns=["email"]), df.drop(index=[0, 1]) |
Zeilen filtern
Setze jede Bedingung in Klammern und verknüpfe sie mit & (und), | (oder) und ~ (nicht).
| Aufgabe | Code |
|---|---|
| Zeilen, die eine Bedingung erfüllen | df[df["age"] > 30] |
| Beide Bedingungen | df[(df["age"] > 30) & (df["city"] == "Oslo")] |
| Eine der Bedingungen | df[(df["city"] == "Oslo") | (df["salary"] > 6000)] |
| Wert in einer Liste | df[df["city"].isin(["Oslo", "Rome"])] |
| Wert nicht in einer Liste | df[~df["city"].isin(["Oslo", "Rome"])] |
| Zwischen zwei Werten, Grenzen inklusive | df[df["age"].between(30, 40)] |
| Mit einem Query-String filtern | df.query("age > 30 and city == 'Oslo'") |
| Variable in einer Query nutzen | min_age = 30
df.query("age > @min_age") |
| Text enthält | df[df["name"].str.contains("e", case=False)] |
| Fehlende oder vorhandene Werte | df[df["age"].isna()], df[df["age"].notna()] |
| Zeilen filtern und Spalten wählen | df.loc[df["age"] > 30, ["name", "salary"]] |
| Größte oder kleinste n Zeilen | df.nlargest(3, "salary"), df.nsmallest(2, "age") |
| Zeilen mit doppeltem Wert | df[df.duplicated(subset=["city"], keep=False)] |
Sortieren und ranken
| Aufgabe | Code |
|---|---|
| Nach einer Spalte sortieren | df.sort_values("salary") |
| Größte zuerst | df.sort_values("salary", ascending=False) |
| Nach mehreren Spalten sortieren | df.sort_values(["city", "salary"], ascending=[True, False]) |
| Fehlende Werte zuerst | df.sort_values("age", na_position="first") |
| Nach dem Index sortieren | df.sort_index() |
| Werte ranken, 1 ist der höchste | df["salary"].rank(ascending=False) |
Spalten hinzufügen und ändern
Operationen auf ganzen Spalten sind schnell. Greif nur zu apply, wenn keine eingebaute Methode die Aufgabe erledigt.
| Aufgabe | Code |
|---|---|
| Neue Spalte aus anderen | df["bonus"] = df["salary"] * 0.1 |
| Spalte in einer Kette hinzufügen | df.assign(bonus=lambda d: d["salary"] * 0.1) |
| Wert aus einer Bedingung | df["level"] = np.where(df["age"] >= 40, "senior", "junior") |
| Wert aus mehreren Bedingungen | df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior") |
| Werte ändern, wo eine Bedingung gilt | df.loc[df["city"] == "Lima", "salary"] = 4000 |
| Werte per Dict zuordnen | df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"}) |
| Funktion auf eine Spalte anwenden | df["salary"].apply(lambda x: round(x, -3)) |
| Funktion auf jede Zelle anwenden | df[["age", "salary"]].map(lambda x: x / 1000) |
| Spalten umbenennen | df.rename(columns={"salary": "pay"}) |
| Spaltentyp ändern | df["salary"].astype("float64") |
| Text in Zahlen, ungültige Werte zu NaN | pd.to_numeric(s, errors="coerce") |
| Zahlen in benannte Klassen | pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"]) |
| Zahlen in gleich große Gruppen | pd.qcut(df["salary"], q=4, labels=False) |
| Über Zeilen iterieren (langsam, letzter Ausweg) | for row in df.itertuples():
print(row.name, row.salary) |
Fehlende Werte und Bereinigung
Fehlende Zahlen erscheinen als NaN, fehlende Datumswerte als NaT. Jeder dieser Aufrufe gibt ein neues Objekt zurück, also weise das Ergebnis zu.
| Aufgabe | Code |
|---|---|
| Anteil fehlender Werte pro Spalte | df.isna().mean() |
| Zeilen mit fehlendem Wert entfernen | df.dropna() |
| Zeilen ohne Schlüsselspalte entfernen | df.dropna(subset=["age"]) |
| Mit einem Wert pro Spalte füllen | df.fillna({"age": df["age"].median()}) |
| Aus vorheriger oder nächster Zeile füllen | df["age"].ffill(), df["age"].bfill() |
| Lücken interpolieren | df["age"].interpolate() |
| Marker in fehlende Werte umwandeln | df.replace(["n/a", "-", ""], np.nan) |
| Doppelte Zeilen entfernen | df.drop_duplicates() |
| Letzte Zeile pro Schlüssel behalten | df.drop_duplicates(subset=["city"], keep="last") |
| Spaltennamen aufräumen | df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") |
| Extremwerte kappen | df["salary"].clip(lower=4000, upper=6000) |
| Category-Typ für wiederholten Text | df["city"] = df["city"].astype("category") |
Textspalten (.str)
In pandas 3 bekommen Textspalten standardmäßig den str dtype. Die .str Methoden arbeiten auf der ganzen Spalte, und fehlende Werte bleiben fehlend.
| Aufgabe | Code |
|---|---|
| Groß-, Klein- oder Titelschreibung | df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title() |
| Leerzeichen entfernen | df["name"].str.strip() |
| Text ersetzen | df["email"].str.replace("mail.com", "example.com") |
| Mit Regex ersetzen | df["email"].str.replace(r"@.*", "", regex=True) |
| Enthält, beginnt mit | df["name"].str.contains("a"), df["name"].str.startswith("E") |
| Länge jedes Werts | df["name"].str.len() |
| Erste Zeichen | df["name"].str[:2] |
| In Spalten aufteilen | df["email"].str.split("@", expand=True) |
| Mit Regex-Gruppe extrahieren | df["email"].str.extract(r"@(\w+)") |
| Textspalten verbinden | df["name"] + " (" + df["city"] + ")" |
| Zahlen mit Nullen auffüllen | df["salary"].astype("str").str.zfill(6) |
| Der neue Text-dtype | df["name"].dtype # str |
Datum und Uhrzeit
Der .dt Accessor funktioniert auf Datetime-Spalten. Ein sortierter Datumsindex (ts unten) ermöglicht Slicing per Text und Resampling.
| Aufgabe | Code |
|---|---|
| Spalte in Datetimes umwandeln | df["date"] = pd.to_datetime(df["date"]) |
| Mit exaktem Format parsen | pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y") |
| Jahr, Monat, Wochentag | df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name() |
| Datumswerte nach Monat gruppieren | df["date"].dt.to_period("M") |
| Tage zwischen zwei Daten | (pd.Timestamp("2026-12-31") - df["date"]).dt.days |
| Tage oder Monate addieren | df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1) |
| Zeilen in einem Zeitraum | df[df["date"].between("2021-01-01", "2022-12-31")] |
| Datumsindex, per Text geslict | ts = df.set_index("date").sort_index()
ts.loc["2021":"2022"] |
| Summe pro Monat | ts["salary"].resample("ME").sum() |
Wochenmittel; "QE" ist quartalsweise, "YE" jährlich | ts["salary"].resample("W").mean() |
| Gleitender Durchschnitt | ts["salary"].rolling(3).mean() |
| Änderung zur vorherigen Zeile | ts["salary"].diff(), ts["salary"].pct_change() |
| Werte eine Zeile nach unten schieben | ts["salary"].shift(1) |
| Laufende Summe | ts["salary"].cumsum() |
| Zeitzonen setzen und umrechnen | ts.index.tz_localize("UTC").tz_convert("Europe/Oslo") |
Gruppieren und aggregieren
groupby teilt die Zeilen nach Schlüssel auf, wendet eine Funktion auf jede Gruppe an und fügt die Ergebnisse zusammen.
| Aufgabe | Code |
|---|---|
| Mittelwert pro Gruppe | df.groupby("city")["salary"].mean() |
| Mehrere Kennzahlen auf einmal | df.groupby("city")["salary"].agg(["mean", "max", "count"]) |
| Benannte Ergebnisspalten | df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count")) |
| Nach zwei Spalten gruppieren | df.groupby(["city", "team"])["salary"].sum() |
| Schlüssel als Spalten behalten | df.groupby("city", as_index=False)["salary"].mean() |
| Anzahl Zeilen pro Gruppe | df.groupby("city").size() |
| Gruppenergebnis in jeder Zeile | df["city_avg"] = df.groupby("city")["salary"].transform("mean") |
| Nur bestimmte Gruppen behalten | df.groupby("city").filter(lambda g: len(g) >= 2) |
| Zeile mit dem Maximum pro Gruppe | df.loc[df.groupby("city")["salary"].idxmax()] |
| Top n Zeilen pro Gruppe | df.sort_values("salary", ascending=False).groupby("city").head(2) |
| Zeilen in jeder Gruppe nummerieren | df.groupby("city").cumcount() |
| Über die Gruppen iterieren | for city, group in df.groupby("city"):
print(city, len(group)) |
| Filtern, gruppieren und sortieren in einer Kette | (df.query("age > 30")
.groupby("city", as_index=False)
.agg(avg_pay=("salary", "mean"))
.sort_values("avg_pay", ascending=False)) |
Pivotieren und umformen
Long-Daten haben eine Zeile pro Beobachtung. Wide-Daten verteilen eine Variable über mehrere Spalten.
| Aufgabe | Code |
|---|---|
| Pivot-Tabelle | df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0) |
| Pivot-Tabelle mit Summen | df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True) |
| Kombinationen zählen | pd.crosstab(df["city"], df["team"]) |
| Anteile pro Zeile | pd.crosstab(df["city"], df["team"], normalize="index") |
| Wide zu Long | long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value") |
| Long zu Wide | long.pivot(index="name", columns="field", values="value") |
| Innere Indexebene zu Spalten | df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0) |
| Eine Zeile pro Listeneintrag | pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags") |
| Spalte One-Hot-kodieren | pd.get_dummies(df["team"], prefix="team") |
| Zeilen und Spalten tauschen | df.T |
DataFrames kombinieren
concat stapelt Frames, merge verknüpft sie über Schlüsselspalten wie ein SQL-Join.
| Aufgabe | Code |
|---|---|
| Zeilen stapeln | pd.concat([df_2025, df_2026], ignore_index=True) |
| Nebeneinander stellen | pd.concat([left, right], axis=1) |
| Über eine Schlüsselspalte joinen | orders.merge(customers, on="customer_id", how="left") |
| Join-Typen | how="inner", "left", "right", "outer" |
| Schlüssel mit unterschiedlichen Namen | orders.merge(users, left_on="customer_id", right_on="id") |
| Join prüfen und Herkunft jeder Zeile markieren | orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True) |
| Über den Index joinen | left.join(right, how="inner") |
| Zeilen ohne Treffer in einer anderen Tabelle | orders[~orders["customer_id"].isin(customers["customer_id"])] |
Plotten mit pandas
pandas zeichnet mit Matplotlib. Jeder Plot-Aufruf gibt eine Matplotlib-ax zurück, die du weiter gestalten kannst.
| Aufgabe | Code |
|---|---|
| Liniendiagramm einer Series | ts["salary"].plot() |
| Liniendiagramm aus zwei Spalten | df.plot(x="date", y="salary") |
| Balkendiagramm einer Zusammenfassung | df.groupby("city")["salary"].mean().plot.bar() |
| Sortierte horizontale Balken | df["city"].value_counts().sort_values().plot.barh() |
| Histogramm | df["salary"].plot.hist(bins=20) |
| Streudiagramm | df.plot.scatter(x="age", y="salary") |
| Boxplot pro Gruppe | df.boxplot(column="salary", by="city") |
| Titel und Achsenbeschriftung | ax = df.plot(x="date", y="salary", title="Salary")
ax.set_ylabel("EUR") |
| Diagramm anzeigen oder speichern | plt.show(), ax.figure.savefig("salary.png", dpi=150) |
pandas 3: alte Schreibweisen und was du jetzt schreibst
Code aus älteren Tutorials schlägt in pandas 3 oft fehl oder wirft Warnungen. Schreib stattdessen die Varianten aus der Spalte pandas 3.
| Aufgabe | pandas 3 | Alte Schreibweise |
|---|---|---|
| Funktion auf jede Zelle | df.map(f) | df.applymap(f) |
| Aus vorheriger Zeile füllen | df.ffill() | df.fillna(method="ffill") |
| Zeilen anhängen | pd.concat([df, new_rows]) | df.append(new_rows) |
| Resampling zum Monatsende | ts["salary"].resample("ME").sum() | resample("M") |
| Stündliche Datumswerte | pd.date_range("2026-01-01", periods=24, freq="h") | freq="H" |
| Einen Wert setzen | df.loc[0, "age"] = 37 | df["age"][0] = 37 |
| Gefilterte Teilmenge ändern | high = df[df["salary"] > 5000]
high["salary"] = 0 | high = df[df["salary"] > 5000].copy() |
| Ist das eine Textspalte? | df["name"].dtype == "str" | df["name"].dtype == object |
Der pandas-Code, den Datenanalysten jeden Tag brauchen, sortiert nach Aufgabe: Daten erstellen und laden, untersuchen, Zeilen auswählen und filtern, Spalten hinzufügen, fehlende Werte bereinigen, mit Text und Datumswerten arbeiten, gruppieren und aggregieren, umformen, zusammenführen und plotten. Jede Zeile ist ein kurzes Snippet zum Einfügen, plus eine Zeile dazu, was es tut.
Alles hier ist für pandas 3 geschrieben, die Version, die Coddy verwendet: Textspalten nutzen den neuen str dtype, Copy-on-Write ist immer aktiv, und entfernte APIs wie applymap und fillna(method=...) stehen nur in der Tabelle mit alten Schreibweisen. Die Beispiele verwenden einen DataFrame df mit den Spalten name, city, team, age, salary, date und email. Neu in Python? Starte mit dem Python Spickzettel und dem Python Playground, und halte die Spickzettel zu NumPy, Matplotlib und scikit-learn neben diesem bereit.
FAQ zum pandas Spickzettel
Ist dieser pandas Spickzettel kostenlos?
Welche pandas-Version deckt dieser Spickzettel ab?
str dtype, Copy-on-Write ist immer aktiv, und in pandas 3 entfernte Aufrufe (applymap, fillna(method=...), append, die Frequenz-Aliase M und H) stehen nur in der letzten Tabelle, neben dem, was du stattdessen schreibst. Fast jedes Snippet läuft auch mit pandas 2.2, außer den Zeilen, die auf den str dtype oder auf Copy-on-Write angewiesen sind.Was ist der Unterschied zwischen loc und iloc?
loc wählt nach Label aus: Indexwerte und Spaltennamen, und ein Slice wie df.loc[0:2] schließt sein Ende ein. iloc wählt nach ganzzahliger Position aus, wie bei einer Python-Liste, also gibt df.iloc[0:2] die ersten zwei Zeilen zurück und schließt das Ende aus. Nutze loc mit einer Bedingung, um Zeilen zu filtern und Spalten in einem Schritt auszuwählen: df.loc[df["age"] > 30, ["name"]].Wie filtere ich einen DataFrame nach mehreren Bedingungen?
& für und, | für oder und ~ für nicht: df[(df["age"] > 30) & (df["city"] == "Oslo")]. Pythons and und or funktionieren nicht auf ganzen Spalten. Bei langen Filtern ist df.query("age > 30 and city == 'Oslo'") leichter zu lesen.Was ist der Unterschied zwischen merge, join und concat?
merge kombiniert zwei DataFrames über Schlüsselspalten, wie ein SQL-Join, mit how="inner", "left", "right" oder "outer". join ist eine Abkürzung, die über den Index mergt. concat gleicht überhaupt keine Schlüssel ab: Es stapelt Frames übereinander oder mit axis=1 nebeneinander.Warum ändert meine Zuweisung den DataFrame nicht?
df["age"][0] = 37 ändern ein temporäres Objekt und lassen df unverändert. Schreib es stattdessen als einen loc Aufruf: df.loc[0, "age"] = 37. Methoden wie dropna und fillna geben ebenfalls ein neues Objekt zurück, also weise das Ergebnis zu.