\“Stoppen Sie das wiederholte Schreiben von mean() und std() für jede Spalte. Lernen Sie, wie Sie beschreibende Statistiken in Python automatisieren und veröffentlichungsbereite Zusammenfassungen in nur wenigen Schritten erstellen können.\“
Einleitung
Jede Analyse beginnt mit dem Laden eines Datensatzes und dem Versuch, dessen Inhalt zu verstehen. Wie viele Zeilen gibt es? Welche Spalten sind numerisch? Wie viel fehlt? Gibt es extreme Ausreißer? Viele von uns beantworten diese Fragen, indem sie die gleichen Snippets wie df.describe(), df.isna().sum() und df.groupby(…).agg(…) verwenden, die wir bereits tausendmal eingegeben haben, und formatieren die Ausgabe manuell, wenn es Zeit ist, sie in einen Bericht einzufügen.
Diese Vorgehensweise ist ineffizient. Das Python-Ökosystem bietet mittlerweile Werkzeuge, die es ermöglichen, von einem rohen DataFrame zu einer formatierten, teilbaren Zusammenfassung in ein oder zwei Zeilen zu gelangen – und andere, die speziell darauf ausgelegt sind, die Art von \“Tabelle 1\“ zu erstellen, die man hauptsächlich in Forschungsarbeiten sieht. Dieses Tutorial führt Sie durch die 7 Schritte, um eine wiederholbare Pipeline zu erstellen, anstatt eine Sammlung von einmaligen Snippets. Wir verwenden während des gesamten Prozesses den Datensatz der Palmer-Pinguine. Er ist klein, offen zugänglich und enthält eine realistische Mischung aus numerischen und kategorialen Spalten, echte fehlende Werte und eine natürliche Gruppierungsvariable (Art). Lassen Sie uns also beginnen.
1. Einrichten der Umgebung und Laden der Daten
Installieren Sie die Pakete, die wir in diesem Tutorial verwenden werden:
pip install pandas seaborn skimpy tableone great-tables fg-data-profiling
Ein wichtiger Hinweis zu letzterem: Die beliebte Profiling-Bibliothek wurde mehrmals umbenannt. Sie hieß ursprünglich pandas-profiling, wurde 2023 in ydata-profiling umbenannt und erhielt im April 2026 erneut den Namen fg-data-profiling. Das ältere Paket ydata-profiling kann weiterhin installiert und verwendet werden, erhält jedoch keine Updates mehr, sodass neue Projekte fg-data-profiling bevorzugen sollten. Wir werden beide Importstile in Schritt 5 behandeln.
Laden Sie nun die Daten. Seaborn enthält einen integrierten Pinguin-Datensatz, was uns einen Download erspart:
import pandas as pd
import seaborn as sns
df = sns.load_dataset(\"penguins\")
print(df.shape)
print(df.dtypes)
print(df.isna().sum())
Die Ausgabe zeigt:
(344, 7)
species object
island object
bill_length_mm float64
bill_depth_mm float64
flipper_length_mm float64
body_mass_g float64
sex object
Die Spalten sind:
- species: 0
- island: 0
- bill_length_mm: 2
- bill_depth_mm: 2
- flipper_length_mm: 2
- body_mass_g: 2
- sex: 11
Sie sehen sieben Spalten: drei kategoriale (species, island, sex) und vier numerische Messungen (bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g). Die Messungen weisen jeweils 2 fehlende Werte auf, während bei sex 11 fehlen. Merken Sie sich dieses Detail – wir werden sehen, wie jedes Tool diese fehlenden Daten meldet.
2. Grundlinie mit df.describe() erhalten
Die eingebaute Methode describe() von Pandas ist der offensichtliche Ausgangspunkt, und das aus gutem Grund. Sie ist sofort verfügbar und erfordert keine zusätzliche Installation:
df.describe().round(2)
Die Ausgabe zeigt:
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
count 342.00 342.00 342.00 342.00
mean 43.92 17.15 200.92 4201.75
std 5.46 1.97 14.06 801.95
min 32.10 13.10 172.00 2700.00
25% 39.22 15.60 190.00 3550.00
50% 44.45 17.30 197.00 4050.00
75% 48.50 18.70 213.00 4750.00
max 59.60 21.50 231.00 6300.00
Diese Informationen sind durchaus nützlich, aber beachten Sie die blinden Flecken. Standardmäßig ignoriert die Methode kategoriale Spalten vollständig. Sie gibt Ihnen zwar eine Anzahl der nicht-null-Werte, sagt jedoch nie direkt den Prozentsatz der fehlenden Werte an. Zudem endet sie bei der fünfzahligen Zusammenfassung – keine Schiefe, keine Kurtosis, kein Gefühl für die Verteilung. Für eine schnelle Überprüfung ist sie in Ordnung, aber als Grundlage für einen Bericht lässt sie Lücken. Der nächste Schritt schließt einige dieser Lücken, ohne Pandas zu verlassen.
3. Pandas weiter nutzen mit include, .agg() und groupby
Bevor Sie auf externe Pakete zurückgreifen, ist es sinnvoll zu wissen, wie weit Pandas allein kommen kann – denn für viele alltägliche Arbeiten ist dies ausreichend.
Fügen Sie zunächst die kategorialen Spalten in die gleiche Zusammenfassung mit include=\“all\“ ein:
df.describe(include=\"all\").round(2)
Die Ausgabe zeigt:
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
count 344 344 342.00 342.00 342.00 342.00 333
unique 3 3 NaN NaN NaN NaN 2
top Adelie Biscoe NaN NaN NaN NaN Male
freq 152 168 NaN NaN NaN NaN 168
mean NaN NaN 43.92 17.15 200.92 4201.75 NaN
std NaN NaN 5.46 1.97 14.06 801.95 NaN
min NaN NaN 32.10 13.10 172.00 2700.00 NaN
25% NaN NaN 39.22 15.60 190.00 3550.00 NaN
50% NaN NaN 44.45 17.30 197.00 4050.00 NaN
75% NaN NaN 48.50 18.70 213.00 4750.00 NaN
max NaN NaN 59.60 21.50 231.00 6300.00 NaN
Jetzt erhalten Sie unique, top und freq für die Textspalten zusammen mit den numerischen Statistiken (mit NaN in den Zellen, wo eine Statistik nicht zutrifft). Eine Tabelle, jede Spalte.
Erstellen Sie als Nächstes eine benutzerdefinierte Zusammenfassung mit .agg(), um genau zu steuern, welche Statistiken erscheinen – einschließlich derjenigen, die describe() auslässt, wie Schiefe und Kurtosis – und fügen Sie einen Prozentsatz fehlender Daten hinzu:
numeric = df.select_dtypes(\"number\")
summary = numeric.agg([\"mean\", \"median\", \"std\", \"skew\", \"kurt\"]).T
summary[\"missing_pct\"] = df[numeric.columns].isna().mean().mul(100).round(1)
summary.round(2)
Die Ausgabe zeigt:
mean median std skew kurt missing_pct
bill_length_mm 43.92 44.45 5.46 0.05 -0.88 0.6
bill_depth_mm 17.15 17.30 1.97 -0.14 -0.91 0.6
flipper_length_mm 200.92 197.00 14.06 0.35 -0.98 0.6
body_mass_g 4201.75 4050.00 801.95 0.47 -0.72 0.6
Als Drittes – und das ist der Schritt, den viele vergessen – verketten Sie groupby() vor describe(), um eine stratifizierte Zusammenfassung in einer einzigen Zeile zu erhalten:
df.groupby(\"species\")[\"body_mass_g\"].describe().round(1)
Die Ausgabe zeigt:
count mean std min 25% 50% 75% max
species
Adelie 151.0 3700.7 458.6 2850.0 3350.0 3700.0 4000.0 4775.0
Chinstrap 68.0 3733.1 384.3 2700.0 3487.5 3700.0 3950.0 4800.0
Gentoo 123.0 5076.0 504.1 3950.0 4700.0 5000.0 5500.0 6300.0
Das Muster, das es zu verinnerlichen gilt: select_dtypes zur Auswahl von Spalten, .agg([…]) zur Auswahl von Statistiken, groupby zur Auswahl von Strata. Dieses Trio deckt einen überraschend großen Teil der realen Berichtsbedürfnisse ohne externe Abhängigkeiten ab. Die verbleibenden Schritte dienen dazu, Zeit zu sparen, den Bericht zu verfeinern und die Fälle zu behandeln, in denen \“gut genug\“ nicht ausreicht.
4. Reichhaltigere Konsolenzusammenfassungen mit skimpy
Wenn Sie mehr als describe() wünschen, aber nicht alles von Hand zusammenstellen möchten, ist skimpy die richtige Wahl. Es ist wie ein verbessertes describe(), das in Ihrer Konsole oder Ihrem Notebook läuft und jeden Spaltentyp gleichzeitig behandelt. Es funktioniert sowohl mit Pandas- als auch mit Polars-DataFrames.
from skimpy import skim
skim(df)
Ein einziger Aufruf druckt einen strukturierten Bericht: eine Datenzusammenfassung (Zeilen- und Spaltenanzahl), eine Aufschlüsselung nach Datentyp, eine numerische Tabelle mit Mittelwert, Standardabweichung, dem vollständigen Perzentilbereich und ein Inline-ASCII-Histogramm pro Spalte, sowie eine separate Tabelle für Zeichen-Spalten, die Dinge wie Zeichenanzahl und häufigste/wenigste Werte zeigt. Fehlende Daten werden sowohl als Anzahl als auch als Prozentsatz gemeldet, genau dort, wo Sie es erwarten.
Die Ausgabe zeigt:
╭──────────────────────────────────────────────── skimpy summary ─────────────────────────────────────────────────╮
│ Data Summary Data Types │
│ ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓ ┏━━━━━━━━━━━━━┳━━━━━━━┓ │
│ ┃ Dataframe ┃ Values ┃ ┃ Column Type ┃ Count ┃ │
│ ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩ ┡━━━━━━━━━━━━━╇━━━━━━━┩ │
│ │ Number of rows │ 344 │ │ float64 │ 4 │ │
│ │ Number of columns │ 7 │ │ string │ 3 │ │
│ └───────────────────┴────────┘ └─────────────┴───────┘ │
│ number │
│ ┏━━━━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━━┓ │
│ ┃ column ┃ NA ┃ NA % ┃ mean ┃ sd ┃ p0 ┃ p25 ┃ p50 ┃ p75 ┃ p100 ┃ hist ┃ │
│ ┡━━━━━━━━━━━━━━━━━━━━╇━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━━━┩ │
│ │ bill_length_mm │ 2 │ 0.5813953488372093 │ 43.92 │ 5.46 │ 32.1 │ 39.23 │ 44.45 │ 48.5 │ 59.6 │ ▃█▆█▃ │ │
│ │ bill_depth_mm │ 2 │ 0.5813953488372093 │ 17.15 │ 1.975 │ 13.1 │ 15.6 │ 17.3 │ 18.7 │ 21.5 │ ▄▅▆█▆▂ │ │
│ │ flipper_length_mm │ 2 │ 0.5813953488372093 │ 200.9 │ 14.06 │ 172 │ 190 │ 197 │ 213 │ 231 │ ▂██▄▆▃ │ │
│ │ body_mass_g │ 2 │ 0.5813953488372093 │ 4202 │ 802 │ 2700 │ 3550 │ 4050 │ 4750 │ 6300 │ ▂█▆▄▃▁ │ │
│ └────────────────────┴────┴────────────────────┴───────┴───────┴──────┴───────┴───────┴──────┴──────┴────────┘ │
│ string │
│ ┏━━━━━━━━━┳━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┓ │
│ ┃ column ┃ NA ┃ NA % ┃ shortest ┃ longest ┃ min ┃ max ┃ chars/row ┃ words/row ┃ total words┃ │
│ ┡━━━━━━━━━╇━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━┩ │
│ │ species │ 0 │ 0 │ Adelie │ Chinstrap │ Adelie │ Gentoo │ 6.59 │ 1.00 │ 344 │ │
│ │ island │ 0 │ 0 │ Dream │ Torgersen │ Biscoe │ Torgersen │ 6.09 │ 1.00 │ 344 │ │
│ │ sex │ 11 │ 3.19767442 │ Male │ Female │ Female │ Male │ 4.99 │ 0.97 │ 333 │ │
│ └─────────┴────┴────────────┴──────────┴───────────┴────────┴───────────┴────────────┴───────────┴────────────┘ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Die Inline-Histogramme sind das herausragende Merkmal. Sie geben Ihnen einen Eindruck von der Form jeder Verteilung, ohne eine Plotbibliothek öffnen zu müssen. Für interaktive Erkundungen ist skimpy ein guter Mittelweg: weitaus informativer als describe(), aber viel leichter als ein vollständiger HTML-Bericht.
5. Vollständigen interaktiven Bericht mit Profiling erstellen
Wenn Sie das vollständige Bild benötigen – Verteilungen, Korrelationen, Wechselwirkungen, Duplikaterkennung und Datenqualitätswarnungen – ist es besser, einen vollständigen Profilbericht zu erstellen. Dies ist die Einzeiler, die für viele Analysten einen Nachmittag explorativer Diagramme ersetzt hat.
Mit dem gewarteten Paket:
from data_profiling import ProfileReport # fg-data-profiling
profile = ProfileReport(df, title=\"Penguins Profiling Report\", explorative=True)
profile.to_file(\"penguins_report.html\")
Wenn Sie das Legacy-Paket verwenden, ändert sich nur die Importzeile:
import ydata_profiling # legacy ydata-profiling
Die Ausgabe ist eine interaktive HTML-Datei, die Abschnitte für eine Übersicht (Größe, Speicher, doppelte Zeilen), Details pro Variable (beschreibende Statistiken plus Histogramme), Korrelationen über mehrere Koeffizienten, eine Analyse fehlender Werte und automatische Warnungen enthält, die Schiefe, hohe Kardinalität, konstante Spalten usw. kennzeichnen.
Ein Abschnitt des generierten Profilberichts
Der einzige Nachteil ist die Geschwindigkeit: Ein vollständiger Bericht berechnet viel und wird bei großen Datensätzen langsamer. Zwei Argumente beheben das. Verwenden Sie minimal=True, um die teuersten Berechnungen abzuschalten, und profilieren Sie eine Stichprobe anstelle des gesamten Rahmens, wenn Sie nur ein Gefühl für die Daten benötigen:
profile = ProfileReport(df.sample(frac=0.5), minimal=True)
Es gibt auch eine .compare()-Methode, um zwei Datensätze nebeneinander zu stellen – unverzichtbar, um Abweichungen zwischen
Quellen: kdnuggets
Bildquelle: KI generiert
🚀