Hinweis: Der folgende Artikel wurde auf Englisch erstellt und nachträglich maschinell auf Deutsch übersetzt.
Effiziente Verarbeitung großer, strukturierter Datensätze ist zentral für die moderne Datenanalyse. Pandas war lange die Standard-DataFrame-Bibliothek in Python und wird für ihre Flexibilität, ihr umfangreiches Ökosystem und die intuitive API geschätzt. Mit wachsenden Datensätzen, die den Arbeitsspeicher überschreiten, und steigenden Performance-Anforderungen gewinnen neuere Tools wie Polars und DuckDB zunehmend an Bedeutung. Während Polars und Pandas DataFrame-Bibliotheken sind und DuckDB eine eingebettete SQL-Analyse-Engine darstellt, verfolgen alle drei das Ziel, großskalige Datenverarbeitung schneller und einfacher zu machen—durch parallele Ausführung, Lazy-Computing und Out-of-Core-Verarbeitung.
Dieser Artikel vergleicht Pandas, Polars und DuckDB hinsichtlich Performance, Speicherauslastung, Skalierbarkeit, Ergonomie und Interoperabilität. Wir zeigen auf, wann ein DataFrame-first-Workflow besonders geeignet ist, wann SQL-first-Werkzeuge Vorteile bieten und wie sich diese Tools in realen Pipelines ergänzen können.
Hintergrund
Pandas
Pandas ist weiterhin die am weitesten verbreitete DataFrame-Bibliothek und bietet eine ausgereifte API sowie nahtlose Integration in den wissenschaftlichen Python-Stack. Es überzeugt beim Prototyping, bei der Datenbereinigung und in der explorativen Analyse. Standardmäßig führt Pandas Operationen eager (sofort) und überwiegend single-threaded aus; typische Workflows setzen voraus, dass die Daten in den Arbeitsspeicher passen—sehr große Dateien (z. B. mehrere Dutzend GB) können zu Speicherproblemen oder Abstürzen führen, sofern man nicht in Blöcken verarbeitet oder herunter sampelt. Neuere Releases (pandas 2.x) fügen Copy-on-Write und optionale Arrow-gestützte dtypes hinzu, die die Speichereffizienz und Interoperabilität verbessern, aber Pandas ist keine Out-of-Core- oder parallele Analyse-Engine.
Polars
Polars ist eine moderne, spaltenorientierte DataFrame-Bibliothek in Rust mit Python-Bindings und nutzt Apache Arrow für eine effiziente Speicherrepräsentation. Es unterstützt standardmäßig multithreaded Ausführung und bietet sowohl eager als auch lazy APIs. Die Lazy-Engine ermöglicht Query-Optimierung (Projektion und Prädikats-Pushdown) und kann—bei Verwendung mit Streaming-Ausführung—Datensätze verarbeiten, die größer als der RAM sind. Nicht alle Operationen streamen, aber für mittlere bis große Datensätze liefern die Kombination aus Parallelismus, Lazy-Optimierung und Streaming häufig erhebliche Geschwindigkeits- und Speichergewinne. Während das Ökosystem noch wächst und einige pandas-spezifische Features nicht abgedeckt sind, machen die schnelle Entwicklung von Polars und optionale GPU-Beschleunigung es zu einer überzeugenden Wahl für performantes Daten-Processing.
DuckDB
DuckDB ist eine moderne, in-Process OLAP-SQL-Engine in C++, ausgelegt für hochperformante analytische Abfragen. Sie verwendet eine vektorisierte, pipeline-basierte Ausführung mit Optimierer, unterstützt multithreaded Ausführung und kann Datensätze größer als RAM via Streaming-Scans und automatisches Auslagern auf Disk für Operationen wie Sorts und Aggregates verarbeiten. DuckDB kann SQL direkt über CSV-Dateien sowie über In-Memory-DataFrames (pandas, Polars) und Arrow-Tabellen ausführen, was einen nahtlosen SQL-first-Workflow ermöglicht. Es glänzt bei komplexen Joins, Aggregationen und Group-bys. DuckDBs Performance, Skalierbarkeit und Interoperabilität machen es zu einem leistungsstarken Baustein für Analytics-Pipelines.
Methodik
Benchmarking-Grundsätze
Benchmarking bedeutet mehr als dieselbe Operation auf verschiedenen Systemen auszuführen. Um faire und reproduzierbare Ergebnisse sicherzustellen, folgen wir Best Practices für Benchmarking, stellen alle Skripte und Umgebungen zur Überprüfung bereit und halten uns an Leitlinien, die von Experten aufgestellt wurden—wie zum Beispiel von DuckDBs Hannes Mühleisen.
Wir benchmarken jedes Tool mit seinem idiomatischen, integrierten Workflow für große, strukturierte Daten—ohne spezielles Tuning oder externe Erweiterungen. DuckDB wird als SQL-first-Engine verwendet und fragt Dateien direkt ab. Polars wird über seine Lazy-Scan-API mit aktiviertem Streaming genutzt, was der dokumentierte Ansatz für effiziente Verarbeitung großer Dateien ist. Pandas wird mit seinem standardmäßigen eager DataFrame-Aufbau (read_csv) verwendet, da es keine integrierte Out-of-Core- oder Lazy-Engine besitzt. Dieser Ansatz spiegelt wider, wie in der Praxis die Aufgabe in jedem Tool natürlich ohne viel Konfiguration (Thread-Counts, PRAGMAs, alternative Parser, GPU-Backends) gelöst werden kann.
Testaufbau
Wir haben drei zentrale OLAP-Operationen gebenchmarkt—Filtern und Zählen, Filtern mit Gruppierung und Aggregation sowie Gruppierung mit bedingter Aggregation—unter Verwendung eines realen E-Commerce-Datensatzes (CSV, 9 GB, 67 Mio. Zeilen, 9 Spalten). Alle Tests wurden auf einem MacBook Pro von 2021 (M1 Max, 32 GB RAM) mit Python durchgeführt. Unser Benchmarking-Tool stellt konsistente und reproduzierbare Kommandozeilen-Ausführung sicher und erlaubt Nutzern, Tool, Operation, Benchmark-Modus (cold oder hot) und Anzahl der Läufe zu spezifizieren. Ergebnisse werden mit Matplotlib visualisiert; wir sammeln zentrale statistische Kennzahlen: Mittelwert, Standardabweichung und Variationskoeffizient.
Die Speicherauslastung wird gemessen, indem der Speicher unmittelbar vor und nach jedem Funktionsaufruf aufgezeichnet wird; die Differenz entspricht dem Speicher, den die Funktion genutzt hat. Hot-Läufe nutzen den OS-Page-Cache und Bibliothekspuffer. Cold-Läufe werden in isolierten Prozessen mit randomisiertem Datei-Zugriff ausgeführt; der macOS-Page-Cache wird nicht hart geleert, sodass die Resultate „kältere“ statt vollständig ungecachete Szenarien darstellen. Für beide Modi wird jede Operation 10-mal wiederholt.
Wir veröffentlichen die exakten Skripte und Umgebungsdetails, sodass Interessierte die idiomatischen Pfade wie beschrieben in unserem Repository erneut ausführen können. Es sind keine speziellen Flags oder Erweiterungen erforderlich außer der Installation der Standardpakete; Ergebnisse sollten auf ähnlicher Hardware und OS-Konfigurationen stabil sein.
Ergebnisse
Für unseren Artikel konzentrieren wir uns auf die Operation „Filtern und Zählen“ als repräsentatives Beispiel. Detaillierte Werte und Plots für alle Benchmarks sind im Repository verfügbar.
Cold-Runs
Cold-Benchmark-Ergebnisse heben DuckDBs überlegene Speichereffizienz hervor. Polars kann die Ausführungszeit von DuckDB nahezu erreichen und verwendet deutlich weniger Speicher als Pandas, benötigt jedoch immer noch ungefähr das Dreifache im Vergleich zu DuckDB.
Pandas zeigt hohen Overhead durch das Parsen der CSV und die Materialisierung eines DataFrames. In Cold-Runs verbraucht dies grob 10 GB (entsprechend der ~10 GB CSV), und das Filtern auf Kaufereignisse fügt etwa 1 GB hinzu.
1Line # Mem usage Increment Occurrences Line Contents 2============================================================= 3 7 142.7 MiB 142.7 MiB 1 @profile 4 8 def filtering_counting(): 5 9 10600.2 MiB 10457.5 MiB 1 df = pd.read_csv(dataset_path) 6 10 11494.2 MiB 894.0 MiB 1 purchases = df[df["event_type"] == "purchase"] 7 11 11494.4 MiB 0.2 MiB 1 print("Count:", len(purchases))
Im Eager-Modus materialisiert Polars typischerweise einen DataFrame aus dem gesamten Datensatz, was zu erheblicher Speichernutzung führen kann. Im Gegensatz dazu vermeidet Polars beim Lazy-CSV-Scan das Laden des kompletten Datensatzes in den Speicher und verarbeitet stattdessen nur die Zeilen, die für die konkrete Operation benötigt werden. Dieser Ansatz ermöglicht effiziente Batch-Verarbeitung anstelle des vollständigen Ladens und führt zu klaren Speichereinsparungen. Zusätzlich unterstützt Polars’ Lazy-Engine Streaming, was den Speicherverbrauch weiter senkt, indem Daten in kleineren, handhabbaren Blöcken verarbeitet werden.
1Line # Mem usage Increment Occurrences Line Contents 2============================================================= 3 7 142.8 MiB 142.8 MiB 1 @profile 4 8 def filtering_counting(): 5 9 143.2 MiB 0.4 MiB 1 lf = pl.scan_csv(dataset_path) 6 10 1681.7 MiB 1538.5 MiB 1 result = lf.filter(pl.col("event_type") == "purchase").count().collect(streaming=True) 7 11 1681.9 MiB 0.2 MiB 1 print(result)
DuckDB fragt die CSV direkt ab, nutzt nur ~300 MB für den gesamten Prozess und liefert Ergebnisse schneller, da es späte Materialisierung und vektorbasierte Verarbeitung mit Prädikats-Pushdown und vektorisierten Pipelines verwendet.
1Line # Mem usage Increment Occurrences Line Contents 2============================================================= 3 7 143.0 MiB 143.0 MiB 1 @profile 4 8 def filtering_counting(): 5 9 461.9 MiB 318.8 MiB 1 duckdb.sql(f"SELECT COUNT(*) AS purchase_count FROM read_csv_auto('{dataset_path}') WHERE event_type = 'purchase'").show()
Hot-Runs
In Hot-Benchmarks wächst DuckDBs Vorteil. Sowohl Pandas als auch Polars reduzieren nach initialen Läufen die Speichernutzung und wechseln häufig zwischen Freigabe und Verbrauch—bleiben jedoch höher und weniger stabil als DuckDB. Interessanterweise variiert Polars’ Speichernutzung stärker als die von Pandas.
Damit kann Pandas den Abstand zu Polars verringern.
Beim Vergleich von Hot- und Cold-Runs bleibt DuckDBs Speichernutzung konstant niedrig—ein Zeichen starker Optimierung. Keines der Tools erzielt in Hot-Runs bedeutende Zeit-Einsparungen. Pandas erzielt die deutlichste Verbesserung der Speichernutzung in Hot-Runs.
Über 10 Hot-Runs hinweg optimieren sowohl Pandas als auch Polars ihre Speichernutzung weiter und nähern sich (sehr) langsam DuckDBs Cold-Run-Speicherprofil—ohne es zu erreichen.
Diskussion
Diese Benchmarks verdeutlichen, wie Architektur und Ausführungsmodelle das Verhalten von Pandas, Polars und DuckDB auf großen Datensätzen bestimmen—die Ergebnisse hängen von Datenformat, Schema (Strings vs. numerisch), Filterselektivität und Speichercharakteristika ab; unsere Befunde spiegeln eine ~10 GB CSV auf einer lokalen SSD wider.
Pandas ist konsistent langsamer und weniger vorhersagbar im Speicher, weil es aus CSV eager einen vollständigen DataFrame materialisiert und weitgehend single-threaded ausführt. Obwohl viele Operationen auf Array-Ebene via NumPy vektorisiert sind, fehlen Pandas ein Query-Optimierer und eine datenbankartige vektorisierte/pipeline-basierte Engine; integrierte Lazy- oder Out-of-Core-Ausführung gibt es nicht. Für große Dateien bedeutet dies höhere Spitzen-Speicherbelegung, mehr Python-Objekt-Overhead (insbesondere für Strings) und begrenzten Parallelismus. Fortgeschrittene Muster (z. B. read_csv mit chunksize/Iteratoren) können Speicher reduzieren, ändern aber das Programmiermodell und liegen außerhalb unseres idiomatischen Vergleichs.
Polars, in Rust auf Arrow aufgebaut, nutzt Multithreading und eine spaltenorientierte Execution-Engine, um Pandas in der Geschwindigkeit zu übertreffen. Seine Lazy-API mit Streaming-Modus kann für viele Abfragen die vollständige Materialisierung vermeiden, indem Filter/Projektionen in Scans gepusht werden. Streaming ist nicht universell: Operationen, die kontextübergreifende Batches benötigen oder Zwischenstände materialisieren, können weiterhin mehrere Gigabyte RAM verbrauchen. In unserem „Filtern und Zählen“-Workload beobachteten wir eine Spitzen-Speichernutzung von ~5 GB mit Lazy+Streaming; die tatsächlichen Zahlen variieren je nach Schema und Selektivität. Insgesamt verbessert Polars die Speichereffizienz und Laufzeit gegenüber Pandas erheblich, erreicht jedoch typischerweise nicht DuckDBs Werte für große On-Disk-Analysen.
DuckDB liefert konsistent geringen Speicherverbrauch und starke Performance, indem es einen Optimierer mit vektorisierter, pipeline-basierter Ausführung und später Materialisierung kombiniert. Es bringt Prädikate und Projektionen in die Dateiscans, streamt Daten, ohne Tabellen vollständig zu materialisieren, und hält die Spitzen-RSS klein und stabil. Die In-Process-Ausführung mit C++-Datenstrukturen vermeidet Python-Objekt-Overhead, und die Nutzung mehrerer Kerne erfolgt automatisch—ideal für schnelle Ad-hoc-Analysen über große CSV-Dateien.
Warum ist Pandas so langsam?
DuckDB und Polars verarbeiten nur die benötigten Spalten, arbeiten in cache-freundlichen Vektoren und nutzen parallele Pipelines; Pandas baut eager vollständige DataFrames und verfügt weder über einen Query-Optimierer noch über eine datenbankartige Ausführungsengine. Trotz Array-Vektorisierung via NumPy führt die typische Nutzung bei großem Maßstab zu mehr Rechenarbeit, mehr Speichertraffic und begrenztem Parallelismus. Ohne integrierte Lazy/Out-of-Core-Ausführung bleibt der idiomatische Pfad von Pandas speichergebunden. Chunked Reading kann helfen, ändert jedoch den Workflow und ist nicht direkt mit SQL/Lazy-Pipelines vergleichbar.
Warum ist DuckDB schneller als Polars?
DuckDB benötigt weniger Aufwand und konzentriert diesen in engen, optimierten Pipelines. Es pusht aggressiv Filter/Projektionen, nutzt vektorbasierte Pipelines mit später Materialisierung und vermeidet Python-Overhead. Polars ist ebenfalls lazy, spaltenorientiert und multi-threaded, aber viele Workloads materialisieren dennoch Zwischenstände oder benötigen kontextübergreifende Batches; dadurch ist der temporäre Speicher oft höher und die End-to-End-Zeit kann bei großen On-Disk-Analysen hinter DuckDB zurückbleiben.
Fazit
Pandas, Polars und DuckDB glänzen jeweils in unterschiedlichen Bereichen moderner Analytics. Für kleine bis mittlere Datensätze und die reiche Bibliotheksintegration bleibt Pandas ein produktiver Standard. Für größere oder performancekritische Workloads liefern Polars und DuckDB erhebliche Vorteile durch parallele Ausführung und Out-of-Core/Lazy-Pipelines. In unseren CSV-basierten Benchmarks bot DuckDB die konsistenteste Performance und den niedrigsten Spitzen-Speicherverbrauch, indem Dateien direkt abgefragt wurden, ohne Tabellen vollständig zu materialisieren.
DuckDB ist am stärksten für SQL-first, On-Disk-Analysen; Polars glänzt bei schnellen DataFrame-Transformationen (insbesondere mit Lazy + Streaming); Pandas eignet sich ideal für interaktives Munging auf moderat großen, In-Memory-Daten. Die effektivste Strategie ist gemischt: jedes Tool sollte dort verwendet werden, wo seine Architektur zum Problem passt. Ergebnisse können je nach Datenformat (CSV vs. Parquet), Schema (Strings vs. numerisch), Filterselektivität und Hardware variieren.
Bei Fragen, Problemen oder zum Beitrag weiterer Benchmarks gerne ein Issue oder einen Pull-Request im Repository erstellen!
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Blog-Autor*in
Niklas Niggemann
Werkstudent Data & AI
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.