Pandas 3.0 Migration: Der komplette Upgrade-Leitfaden mit Copy-on-Write, neuem String-Dtype und pd.col()

Pandas 3.0 bringt Copy-on-Write als Standard, einen neuen String-Datentyp und pd.col()-Ausdrücke. Dieser Leitfaden zeigt Schritt für Schritt, wie Sie Ihren Code migrieren — mit Codebeispielen und Checkliste.

Warum pandas 3.0 das wichtigste Python-Update 2026 ist

Am 21. Januar 2026 hat das pandas-Team die Version 3.0.0 veröffentlicht — das erste Major-Release seit Jahren. Und ganz ehrlich: Es ist kein sanftes Update. Copy-on-Write wird zum Standard, String-Spalten bekommen einen eigenen Datentyp, und mit pd.col() gibt es eine komplett neue Syntax für Spaltenausdrücke. Kurz gesagt: Das betrifft praktisch jeden, der regelmäßig mit pandas arbeitet.

Aber keine Panik.

In diesem Leitfaden gehen wir Schritt für Schritt durch alle wichtigen Änderungen, zeigen die häufigsten Stolperfallen beim Upgrade und liefern konkreten Code, den Sie direkt in Ihre Projekte übernehmen können. Also, legen wir los.

Voraussetzungen: Was Sie vor dem Upgrade wissen müssen

Bevor Sie jetzt wild pip install --upgrade pandas in die Konsole hauen, gibt es ein paar Dinge zu beachten:

  • Python 3.11 oder höher ist Pflicht — pandas 3.0 unterstützt ältere Python-Versionen schlicht nicht mehr.
  • PyArrow wird dringend empfohlen — der neue String-Datentyp nutzt PyArrow als Backend für deutlich bessere Performance. Ohne PyArrow fällt pandas auf den langsameren NumPy-object-Dtype zurück (und das will man wirklich nicht).
  • Zuerst auf pandas 2.3 upgraden — das offizielle Team empfiehlt, zunächst auf Version 2.3 zu gehen. Dort bekommen Sie Deprecation-Warnungen, die auf nötige Code-Änderungen hinweisen. Vertrauen Sie mir: Dieser Zwischenschritt spart eine Menge Debugging-Frust.

Der empfohlene Upgrade-Pfad

# Schritt 1: Auf pandas 2.3 upgraden
pip install pandas==2.3.*

# Schritt 2: Code testen und alle Warnungen beheben
python -W error::DeprecationWarning mein_skript.py

# Schritt 3: Optional — neue Features vorab aktivieren
import pandas as pd
pd.options.future.infer_string = True
pd.options.mode.copy_on_write = True

# Schritt 4: Auf pandas 3.0 upgraden
pip install pandas==3.0.*

# Schritt 5: PyArrow installieren (empfohlen)
pip install pyarrow

Copy-on-Write: Die größte Verhaltensänderung

Copy-on-Write (CoW) ist die Änderung, die am meisten bestehenden Code betrifft. In früheren pandas-Versionen war das Verhalten bei Indexierungsoperationen oft — na ja — unvorhersehbar. Manchmal bekam man eine Kopie, manchmal eine View auf die Originaldaten. Das führte zu subtilen Bugs und dem berüchtigten SettingWithCopyWarning, den wahrscheinlich jeder pandas-Nutzer schon mal gesehen hat.

Ab pandas 3.0 gilt eine klare Regel: Jedes Ergebnis einer Indexierungsoperation oder Methode verhält sich immer wie eine Kopie. Das Original wird dadurch nie verändert. Intern nutzt pandas zwar weiterhin Views (aus Performance-Gründen), kopiert aber automatisch, sobald eine Änderung vorgenommen wird — daher der Name „Copy-on-Write".

Chained Assignment funktioniert nicht mehr

Das ist die Änderung, über die am meisten gestolpert wird. Chained Assignment — also das Modifizieren eines DataFrames über mehrere Indexierungsschritte hinweg — hat in pandas 3.0 schlicht keine Wirkung mehr:

import pandas as pd

df = pd.DataFrame({
    "name": ["Anna", "Boris", "Clara"],
    "gehalt": [65000, 55000, 72000],
    "abteilung": ["IT", "Marketing", "IT"]
})

# FALSCH — funktioniert in pandas 3.0 nicht mehr!
# Die Zuweisung ändert nur eine temporäre Kopie, nicht df.
df["gehalt"][df["abteilung"] == "IT"] = 70000

# RICHTIG — direkte Modifikation mit .loc
df.loc[df["abteilung"] == "IT", "gehalt"] = 70000

print(df)
# Ausgabe:
#     name  gehalt  abteilung
# 0   Anna   70000         IT
# 1  Boris   55000  Marketing
# 2  Clara   70000         IT

Falls Sie sich fragen, warum das überhaupt so funktionierte: In früheren Versionen hing es im Grunde vom Zufall ab, ob die Änderung auf dem Original oder einer Kopie landete. Jetzt ist das Verhalten wenigstens eindeutig.

Defensives .copy() ist nicht mehr nötig

Viele Entwickler (mich eingeschlossen) haben jahrelang vorsorglich .copy() aufgerufen, um den SettingWithCopyWarning loszuwerden. Da Copy-on-Write jetzt Standard ist, können Sie diese Aufrufe getrost entfernen:

# Vorher (pandas < 3.0) — defensives .copy()
subset = df[df["gehalt"] > 60000].copy()
subset["bonus"] = subset["gehalt"] * 0.1

# Nachher (pandas 3.0) — .copy() nicht mehr nötig
subset = df[df["gehalt"] > 60000]
subset["bonus"] = subset["gehalt"] * 0.1
# subset ist automatisch unabhängig von df

Performance-Vorteile durch CoW

Copy-on-Write verbessert nicht nur die Vorhersagbarkeit, sondern auch die Performance. Da pandas intern Views nutzt und erst beim Schreiben tatsächlich kopiert, werden unnötige Kopieroperationen vermieden. In der Praxis heißt das:

  • Weniger Speicherverbrauch bei Subset-Operationen
  • Schnellere Ausführung bei reinen Leseoperationen
  • Konsistentes Verhalten — keine bösen Überraschungen mehr

Neuer String-Datentyp: Von object zu str

Das hier ist eine Änderung, auf die viele lange gewartet haben. Bisher hat pandas String-Spalten als NumPy object-Datentyp gespeichert — ein generischer Typ, der im Prinzip alles aufnehmen kann. Das war weder performant noch typsicher. Ab pandas 3.0 gibt es endlich einen dedizierten str-Datentyp, der standardmäßig für String-Daten verwendet wird.

Was sich ändert

import pandas as pd

# pandas < 3.0
ser = pd.Series(["Berlin", "München", "Hamburg"])
print(ser.dtype)
# Ausgabe: object

# pandas 3.0
ser = pd.Series(["Berlin", "München", "Hamburg"])
print(ser.dtype)
# Ausgabe: str

Intern nutzt der neue str-Dtype PyArrow als Backend (sofern installiert). Das bringt spürbare Performance-Verbesserungen — laut den Entwicklern bis zu 5–10x schnellere Verarbeitung bei textlastigen Workloads. Das klingt vielleicht übertrieben, aber bei großen DataFrames mit vielen Textspalten macht sich der Unterschied wirklich bemerkbar.

Häufige Fehler beim String-Dtype-Wechsel

Der neue String-Dtype kann bestehenden Code brechen, wenn dieser auf den object-Dtype prüft. Das ist ein Fehler, den man leicht übersieht:

import pandas as pd

df = pd.DataFrame({"stadt": ["Berlin", "München", "Hamburg"]})

# FALSCH — gibt in pandas 3.0 False zurück!
if df["stadt"].dtype == "object":
    print("String-Spalte gefunden")

# RICHTIG — funktioniert mit allen pandas-Versionen
if pd.api.types.is_string_dtype(df["stadt"]):
    print("String-Spalte gefunden")

NumPy-Kompatibilität beachten

Da der neue str-Dtype ein pandas Extension-Dtype ist (und kein NumPy np.dtype), können Sie ihn nicht direkt an NumPy-Funktionen übergeben:

import numpy as np
import pandas as pd

ser = pd.Series(["a", "b", "c"])

# FALSCH — funktioniert nicht mit dem neuen str-Dtype
# np.issubdtype(ser.dtype, np.object_)

# RICHTIG — pandas-eigene Typ-Prüfung verwenden
pd.api.types.is_string_dtype(ser)  # True

PyArrow und Unicode

Ein kleiner Stolperstein: Wenn PyArrow als Backend aktiv ist, kann der String-Dtype nur gültige Unicode-Daten speichern. Ungültige Surrogates führen zu einem Fehler:

# Wirft einen UnicodeEncodeError mit PyArrow-Backend
# ser = pd.Series(["\ud83d"])

# Lösung: Explizit object-Dtype angeben
ser = pd.Series(["\ud83d"], dtype="object")

In den meisten Fällen werden Sie dieses Problem nie sehen. Aber wenn Sie mit Daten aus externen Quellen arbeiten, die nicht immer sauberes Unicode liefern, sollten Sie das im Hinterkopf behalten.

pd.col() — Die neue Spaltenausdrucks-Syntax

Inspiriert von Polars und PySpark führt pandas 3.0 die Funktion pd.col() ein. Damit können Sie auf DataFrame-Spalten verweisen und Ausdrücke aufbauen — ganz ohne Lambda-Funktionen. Und ehrlich gesagt: Das war längst überfällig.

Warum pd.col() besser ist als Lambda

Lambda-Funktionen haben in pandas mehrere Probleme: Sie sind schwer zu lesen, nicht introspektierbar und anfällig für Scoping-Bugs. Hier ein klassisches Beispiel, das schon vielen Kopfschmerzen bereitet hat:

import pandas as pd

df = pd.DataFrame({"x": [1, 2, 3]})

# Scoping-Bug mit Lambda
results = {}
for factor in [10, 20, 30]:
    results[f"x_mal_{factor}"] = lambda df: df["x"] * factor

df = df.assign(**results)
print(df)
# Überraschung: Alle drei Spalten multiplizieren mit 30!
# Lambdas speichern die Variable „factor", nicht deren Wert.
# Nach der Schleife ist factor = 30.

Die pd.col()-Lösung

import pandas as pd

df = pd.DataFrame({
    "preis": [10.0, 25.0, 15.0],
    "menge": [3, 1, 5],
    "temp_c": [0, 20, 100]
})

# Vorher — mit Lambda
df_alt = df.assign(
    gesamt=lambda x: x["preis"] * x["menge"]
)

# Nachher — mit pd.col()
df_neu = df.assign(
    gesamt=pd.col("preis") * pd.col("menge")
)

# Auch String-Methoden funktionieren
df2 = pd.DataFrame({"stadt": ["berlin", "münchen", "hamburg"]})
df2 = df2.assign(stadt_gross=pd.col("stadt").str.upper())
print(df2)
# Ausgabe:
#      stadt stadt_gross
# 0   berlin     BERLIN
# 1  münchen    MÜNCHEN
# 2  hamburg    HAMBURG

# Temperaturumrechnung
df = df.assign(temp_f=pd.col("temp_c") * 9/5 + 32)
print(df[["temp_c", "temp_f"]])
# Ausgabe:
#    temp_c  temp_f
# 0       0    32.0
# 1      20    68.0
# 2     100   212.0

Deutlich lesbarer als die Lambda-Variante, oder?

Einschränkungen von pd.col()

pd.col() funktioniert überall dort, wo ein Callable akzeptiert wird — also in DataFrame.assign(), DataFrame.loc() und mehr. Eine wichtige Einschränkung gibt es allerdings: In GroupBy-Operationen kann pd.col() noch nicht verwendet werden. Das soll in zukünftigen Versionen kommen, aber Stand jetzt müssen Sie dort weiterhin mit Lambda oder String-Aliasen arbeiten.

Datetime-Auflösung: Von Nanosekunden zu Mikrosekunden

In früheren Versionen hat pandas Datetime-Daten standardmäßig mit Nanosekunden-Auflösung gespeichert. Das klingt erstmal nach „mehr Genauigkeit = besser", führte aber zu einem nervigen Problem: Datumsangaben vor dem Jahr 1678 oder nach 2262 konnten nicht dargestellt werden und erzeugten einen OutOfBoundsDatetime-Fehler. Wer jemals mit historischen Daten gearbeitet hat, kennt diesen Schmerz.

Ab pandas 3.0 ist die Standard-Auflösung Mikrosekunden (oder die Auflösung des Inputs). Damit wird der darstellbare Datumsbereich massiv erweitert:

import pandas as pd

# pandas < 3.0 — OutOfBoundsDatetime-Fehler!
# pd.Timestamp("1600-01-01")

# pandas 3.0 — funktioniert problemlos
ts = pd.Timestamp("1600-01-01")
print(ts)
# Ausgabe: 1600-01-01 00:00:00

# Auflösung prüfen
ser = pd.Series(pd.to_datetime(["2026-01-01", "2026-06-15"]))
print(ser.dtype)
# Ausgabe: datetime64[us]  (Mikrosekunden statt ns)

Falls Sie aus irgendeinem Grund weiterhin Nanosekunden-Auflösung brauchen, können Sie diese explizit angeben:

# Explizite Nanosekunden-Auflösung
ser = pd.Series(pd.to_datetime(["2026-01-01"]).as_unit("ns"))
print(ser.dtype)
# Ausgabe: datetime64[ns]

Entfernte Funktionen und APIs

Pandas 3.0 hat ordentlich aufgeräumt und zahlreiche Funktionen entfernt, die schon länger als deprecated markiert waren. Hier sind die wichtigsten, die Sie kennen sollten:

Entfernte Methoden und Parameter

  • DataFrame.last() und Series.last() — entfernt
  • Categorical.to_list() — verwenden Sie stattdessen .tolist()
  • method und limit Parameter in NDFrame.replace() — entfernt
  • obj Argument in GroupBy.get_group() — entfernt
  • Direkte JSON-Strings an read_json() — verwenden Sie io.StringIO()

Entfernte Zeiteinheiten-Aliase

Mehrere veraltete Offset-Aliase wurden entfernt und durch ihre vollständigen Äquivalente ersetzt. Diese Änderung ist schnell gemacht, aber man muss halt erstmal wissen, dass sie nötig ist:

  • 'H''h' (Stunden)
  • 'T' / 't''min' (Minuten)
  • 'S''s' (Sekunden)
  • 'L' / 'l''ms' (Millisekunden)
  • 'U''us' (Mikrosekunden)
  • 'N''ns' (Nanosekunden)
import pandas as pd

# Vorher (veraltet)
# pd.date_range("2026-01-01", periods=5, freq="T")

# Nachher (korrekt)
pd.date_range("2026-01-01", periods=5, freq="min")

Verhaltensänderungen bei GroupBy

Ein Detail, das leicht übersehen wird: Wenn Sie nach einer Liste mit nur einem Element gruppieren, gibt die Iteration jetzt Tupel der Länge 1 zurück — statt wie bisher einzelne Werte:

import pandas as pd

df = pd.DataFrame({"a": [1, 1, 2], "b": [10, 20, 30]})

# pandas 3.0: Tupel der Länge 1
for key, group in df.groupby(["a"]):
    print(type(key), key)
# Ausgabe:
# <class 'tuple'> (1,)
# <class 'tuple'> (2,)

Neue Deprecation-Policy

Pandas 3.0 führt eine neue 3-stufige Deprecation-Policy ein, die Downstream-Paketen (und uns allen) mehr Zeit für Anpassungen gibt:

  1. Stufe 1: DeprecationWarning — nur für Entwickler sichtbar (standardmäßig unterdrückt)
  2. Stufe 2: FutureWarning — für alle Nutzer sichtbar, in der letzten Minor-Version vor dem nächsten Major-Release
  3. Stufe 3: Entfernung im nächsten Major-Release

Das ist eine wirklich gute Änderung. Wenn Sie Ihren Code regelmäßig gegen die neueste Minor-Version testen und Warnungen beheben, sollten zukünftige Major-Upgrades deutlich reibungsloser verlaufen. Kein „Oh nein, alles ist kaputt" mehr am Upgrade-Tag.

Praxis-Checkliste: Ihr pandas 3.0 Upgrade in 7 Schritten

Zum Abschluss hier nochmal alles kompakt zusammengefasst. Am besten arbeiten Sie diese Liste der Reihe nach ab:

  1. Python-Version prüfen — Stellen Sie sicher, dass Python 3.11+ installiert ist
  2. Auf pandas 2.3 upgraden — Beheben Sie alle Deprecation-Warnungen
  3. PyArrow installierenpip install pyarrow für die beste String-Performance
  4. Chained Assignment ersetzen — Suchen Sie nach Mustern wie df["col"][mask] = ... und ersetzen Sie sie durch df.loc[mask, "col"] = ...
  5. Dtype-Prüfungen aktualisieren — Ersetzen Sie dtype == "object" durch pd.api.types.is_string_dtype()
  6. Offset-Aliase aktualisieren — Ersetzen Sie veraltete Kürzel (H, T, S, L, U, N) durch die neuen (h, min, s, ms, us, ns)
  7. Tests ausführen — Führen Sie Ihre gesamte Testsuite aus und prüfen Sie die Ergebnisse sorgfältig

FAQ — Häufig gestellte Fragen zu pandas 3.0

Ist pandas 3.0 abwärtskompatibel?

Kurze Antwort: Nein, nicht vollständig. Pandas 3.0 enthält mehrere Breaking Changes, allen voran Copy-on-Write als Standard, den neuen String-Datentyp und die Entfernung vieler veralteter Funktionen. Deshalb empfiehlt das Entwicklerteam dringend, zuerst auf pandas 2.3 zu upgraden und alle Deprecation-Warnungen zu beheben, bevor der Sprung auf 3.0 erfolgt.

Brauche ich PyArrow für pandas 3.0?

Zwingend erforderlich ist es nicht, aber Sie sollten es wirklich installieren. Ohne PyArrow fällt der neue String-Datentyp auf den langsameren NumPy-object-Dtype zurück. Mit PyArrow bekommen Sie bis zu 5–10x bessere Performance bei der Verarbeitung von Text-Daten und profitieren vom effizienteren Speicherformat.

Was passiert mit meinem bestehenden Code, der .copy() verwendet?

Gute Nachricht: Bestehende .copy()-Aufrufe sind weiterhin gültig — sie sind nur nicht mehr nötig. Da Copy-on-Write jetzt Standard ist, verhält sich jedes Subset automatisch wie eine Kopie. Sie können die überflüssigen .copy()-Aufrufe nach und nach entfernen, um Ihren Code aufzuräumen.

Kann ich pandas 2.x und 3.0 parallel verwenden?

In derselben Python-Umgebung kann nur eine pandas-Version installiert sein. Für eine sichere Migration empfiehlt es sich, mit virtuellen Umgebungen zu arbeiten: Erstellen Sie eine neue Umgebung mit pandas 3.0, testen Sie dort Ihren Code, und wechseln Sie erst, wenn alles grün ist.

Funktioniert pd.col() mit GroupBy-Operationen?

Leider noch nicht. In pandas 3.0 können pd.col()-Ausdrücke noch nicht in GroupBy-Operationen verwendet werden. Das soll in zukünftigen Versionen kommen. Für GroupBy-Aggregationen nutzen Sie vorerst weiterhin die bisherige Syntax mit String-Aliasen oder Lambda-Funktionen.

Über den Autor Editorial Team

Our team of expert writers and editors.