# Einführung
Es wird geschätzt, dass die Datenbereinigung und -aufbereitung bis zu 80 % des täglichen Arbeitsablaufs eines Datenwissenschaftlers ausmacht. Weil Pandas ist die Standardbibliothek zur Datenbearbeitung in Python. Die Effizienz Ihrer Vorgänge bestimmt direkt, wie schnell Sie von rohen, schmutzigen Datensätzen zu modellbereiten Funktionen wechseln können. Und es gibt gute Gründe, Ihre Reinigungs- und Vorbereitungszeit zu verlängern: Dies bedeutet direkt, dass mehr Zeit für die Modellierung, Analyse und Vermittlung von Erkenntnissen zur Verfügung steht.
Allerdings schreiben viele Entwickler Pandas-Code, der Customary-Python-Schleifenstrukturen nachahmt oder crucial, zustandsverändernde Aktualisierungen verwendet. Diese Ansätze weisen mehrere Probleme auf: Sie können zu Verwirrung führen SettingWithCopyWarningblähen die RAM-Auslastung durch redundante Kopien auf und verlangsamen die Ausführungsgeschwindigkeit durch die Vermeidung von Vektorisierung.
Um Datenpipelines in Produktionsqualität zu schreiben, müssen Sie von der grundlegenden Syntax zu idiomatischen Pandas-Entwurfsmustern übergehen. In diesem Artikel gehen wir drei wesentliche Pandas-Methods durch, um Ihre Daten effizient zu bereinigen und aufzubereiten:
- deklarative Methodenverkettung
- Speicher- und Geschwindigkeitsoptimierung über Kategorialfunktionen und vektorisierte String-Accessoren
- Gruppenbewusste Imputation unter Verwendung
.rework()
# 1. Deklarative Methodenverkettung mit .assign(), .question()Und .pipe()
Beim Vorbereiten von Daten ist es üblich, eine Reihe von Änderungen vorzunehmen: Zeichenfolgenwerte bereinigen, neue mathematische Spalten erstellen, Ausreißer filtern, Felder umbenennen usw.
Bei einem naiven Ansatz werden diese Vorgänge nacheinander geschrieben, wobei der DataFrame direkt verändert oder wiederholt derselben Variablen neu zugewiesen wird. Dies erschwert nicht nur das Lesen und Debuggen von Code, sondern das Ändern von Slices in DataFrames löst auch häufig das berüchtigte Drawback aus SettingWithCopyWarning. Mit dieser Warnung weist Pandas darauf hin, dass nicht garantiert werden kann, ob Sie eine Kopie oder den ursprünglichen Array-Puffer im Speicher ändern.
Indem Sie Ihre Datenbereinigungspipeline in Klammern setzen, können Sie Pandas-Methoden sequentiell verketten. Benutzen .assign() um neue Spalten zu deklarieren, .question() zur Zeilenfilterung und .pipe() Durch die Anwendung benutzerdefinierter Funktionen bleiben Ihre Abläufe linear, lesbar und vor Nebenwirkungen geschützt.
Dieser zwingende Stil modifiziert den DataFrame Schritt für Schritt, wodurch das Risiko von Warnmeldungen besteht und die Isolierung von Zwischenstufen schwierig wird:
import pandas as pd
import numpy as np
# Pattern uncooked gross sales information
information = {
'sale_date': ('2026-01-01', '2026-01-02', 'invalid_date', '2026-01-04'),
'item_code': (' PROD_A ', ' PROD_B', 'PROD_C ', ' PROD_D '),
'worth': (100.0, 250.0, -99.0, 150.0),
'amount': (2, 1, 5, 3)
}
df = pd.DataFrame(information)
# Naive multi-step cleansing
df('sale_date') = pd.to_datetime(df('sale_date'), errors="coerce")
df('item_code') = df('item_code').str.strip()
df('total_revenue') = df('worth') * df('amount')
# Filtering out unhealthy dates and invalid costs
df = df(df('sale_date').notna())
df = df(df('worth') > 0)
# Renaming columns for consistency
df.rename(columns={'item_code': 'product_id'}, inplace=True)
print(df)
Hier strukturieren wir genau dieselbe Logik in eine einzige, zusammenhängende, von oben nach unten verlaufende Pipeline um. Wir verwenden eine benutzerdefinierte Hilfsfunktion mit .pipe() So behandeln Sie benutzerdefinierte Anomalien:
import pandas as pd
import numpy as np
information = {
'sale_date': ('2026-01-01', '2026-01-02', 'invalid_date', '2026-01-04'),
'item_code': (' PROD_A ', ' PROD_B', 'PROD_C ', ' PROD_D '),
'worth': (100.0, 250.0, -99.0, 150.0),
'amount': (2, 1, 5, 3)
}
df_raw = pd.DataFrame(information)
# Customized modular cleansing step
def clean_item_codes(df):
df('item_code') = df('item_code').str.strip()
return df
# Technique Chaining pipeline
cleaned_df = (
df_raw
.copy() # Prevents modifying the unique uncooked information
.assign(
sale_date=lambda d: pd.to_datetime(d('sale_date'), errors="coerce"),
total_revenue=lambda d: d('worth') * d('amount')
)
.pipe(clean_item_codes)
.question("sale_date.notna() and worth > 0")
.rename(columns={'item_code': 'product_id'})
)
print(cleaned_df)
Ausgabe:
sale_date product_id worth amount total_revenue
0 2026-01-01 PROD_A 100.0 2 200.0
1 2026-01-02 PROD_B 250.0 1 250.0
3 2026-01-04 PROD_D 150.0 3 450.0
Durch Einschließen des Ausdrucks ( ... )Python erlaubt mehrzeilige Ketten ohne die Verwendung von Backslashes.
.assign()akzeptiert Schlüsselwortargumente, bei denen Lambdas den aktuellen Standing des DataFrame empfangen (d), sodass Sie mehrere Spalten nacheinander erstellen oder ändern können..pipe()übergibt den Zwischen-DataFrame an eine externe Funktion. Dadurch wird die wiederverwendbare Reinigungslogik von der Hauptkette getrennt..question()akzeptiert einen booleschen Ausdruck als String. Es ist sauberer als verschachtelte Klammern (df((df(a) > 0) & (df(b).notna()))) und läuft unter der Haube schneller mit dem schnellen numerischen Ausdrucks-Evaluator von NumPy, NumExpr.
Dieses Funktionsmuster vermeidet SettingWithCopyWarning weil es niemals Zwischen-Slices verändert.
# 2. Speicher- und Geschwindigkeitsoptimierung mit kategorialen und vektorisierten String-Methoden
Standardmäßig weist Pandas das Generikum zu object Datentyp für Spalten mit Textual content. Eine Objektspalte speichert Python-Zeiger auf Zeichenfolgen, die im Heapspeicher verstreut sind, und nicht auf zusammenhängende, gepackte Werte. Bei großen Datensätzen mit Zeichenfolgen mit geringer Kardinalität (Spalten mit sich wiederholenden Kategorien wie Statusflaggen, Städtenamen oder Geschlecht) ist dies standardmäßig ein offensichtlicher Speicherbedarf.
Darüber hinaus wenden Entwickler häufig benutzerdefinierte Zeichenfolgenmodifikationen an, indem sie Python-Lambda-Ausdrücke an übergeben .apply(). Dies zwingt Pandas dazu, bei langsamer Python-Interpreter-Geschwindigkeit nacheinander jede Zeile zu durchlaufen.
Wir können sowohl die RAM-Nutzung als auch die Ausführungszeit optimieren, indem wir:
- Konvertieren von Zeichenfolgenspalten mit niedriger Kardinalität in die native
classDatentyp - Langsamer Austausch
.apply()Schleifen mit optimierten vektorisierten String-Methoden über die.strZugriffsberechtigter
Lassen Sie uns die Bereinigung eines großen Datensatzes (1.000.000 Zeilen) simulieren, indem wir Textual content als Objektspalten beibehalten und Leerzeichen mit bereinigen .apply():
import pandas as pd
import numpy as np
import time
# Create a mock dataset with 1 million rows of low-cardinality string information
n_rows = 1000000
classes = (' PENDING ', ' COMPLETED ', ' FAILED ', ' SHIPPED ')
df = pd.DataFrame({
'standing': np.random.selection(classes, dimension=n_rows),
'val': np.random.rand(n_rows)
})
# Benchmark reminiscence utilization earlier than cleansing
mem_before = df('standing').memory_usage(deep=True) / (1024 ** 2)
start_time = time.time()
# Naive cleansing: sluggish Python apply loops
df('standing') = df('standing').apply(lambda x: x.strip().higher())
duration_apply = time.time() - start_time
mem_after = df('standing').memory_usage(deep=True) / (1024 ** 2)
print(f"Apply cleansing accomplished in: {duration_apply:.4f} seconds")
print(f"Standing column reminiscence utilization: {mem_after:.2f} MB (initially {mem_before:.2f} MB)")
Durch Umwandlung der Statusspalte in class Zuerst und unter Verwendung der vektorisierten .str Mit dem Accessor erzielen wir sofortige Beschleunigungen und sparen erheblich Speicher:
import pandas as pd
import numpy as np
import time
n_rows = 1000000
classes = (' PENDING ', ' COMPLETED ', ' FAILED ', ' SHIPPED ')
df = pd.DataFrame({
'standing': np.random.selection(classes, dimension=n_rows),
'val': np.random.rand(n_rows)
})
# Convert to class dtype
df('standing') = df('standing').astype('class')
# Benchmark reminiscence utilization
mem_category = df('standing').memory_usage(deep=True) / (1024 ** 2)
start_time = time.time()
# Vectorized string cleansing immediately on classes
df('standing') = df('standing').cat.rename_categories(lambda x: x.strip().higher())
duration_vectorized = time.time() - start_time
print(f"Vectorized class cleansing accomplished in: {duration_vectorized:.4f} seconds")
print(f"Class standing column reminiscence utilization: {mem_category:.2f} MB")
print(f"Speedup: {duration_apply / duration_vectorized:.2f}x sooner")
Kombinierte Ausgabe:
Apply cleansing accomplished in: 0.1213 seconds
Standing column reminiscence utilization: 53.64 MB (initially 55.55 MB)
Vectorized class cleansing accomplished in: 0.0003 seconds
Class standing column reminiscence utilization: 0.95 MB
Speedup: 407.83x sooner
Wir nennen diese Leistungsverbesserungen einen Sieg.
Wenn eine Spalte umgewandelt wird classPandas kodiert die Zeichenfolgen unter der Haube in Ganzzahlschlüssel (z PENDING -> 0, COMPLETED -> 1).
- Anstatt 1.000.000 Strings zu speichern, speichert Pandas 1.000.000 kleine Ganzzahlen und eine winzige Karte mit 4 tatsächlichen String-Kategorien. Dadurch reduziert sich der Speicherbedarf von ~56 MB auf weniger als 1 MB.
- Durch die direkte Reinigung der Etiketten
.cat.rename_categories()führt Pandas die String-Operationen nur für die 4 eindeutigen Kategorien durch, anstatt eine Schleife durch 1.000.000 Zeilen zu durchlaufen. Die Ausführungszeit sinkt auf nahezu Null.
Notiz: Wenn Sie mit Textual content mit hoher Kardinalität arbeiten (bei dem sich Werte selten wiederholen), behalten Sie es bei class spart keinen Speicher. In diesen Fällen sollten Sie es dennoch vermeiden .apply() und verwenden Sie vektorisierte String-Methoden direkt in der Objektspalte: df('standing').str.strip().str.higher()das in kompiliertem C und nicht in Python ausgeführt wird.
# 3. Gruppenbewusste Imputation und Interpolation mit groupby() Und .rework()
Der Umgang mit fehlenden Daten ist ein grundlegender Schritt bei der Datenbereinigung. In vielen Fällen führt das Ersetzen fehlender Werte durch einen globalen Durchschnitt oder eine Konstante zu einer statistischen Verzerrung. Wenn Sie beispielsweise einen fehlenden Produktpreis angeben, ist die Verwendung des globalen Durchschnittspreises aller Filialprodukte ungenau. Es ist viel präziser, die Berechnung anhand des Durchschnittspreises dieser spezifischen Produktkategorie vorzunehmen.
Der naive Ansatz besteht darin, die Produktkategorien zu durchlaufen, den Gruppenmittelwert zu berechnen, den DataFrame zu filtern, die fehlenden Werte zu füllen und die Gruppen wieder zusammenzufügen. Alternativ können Sie eine benutzerdefinierte Funktion darin verwenden groupby().apply() löst langsame Break up-Apply-Mix-Zyklen aus, die sich schlecht skalieren lassen.
Die optimale Lösung ist die Kombination groupby() mit dem .rework() Verfahren.
Hier simulieren wir die Imputation fehlender numerischer Preise (dargestellt durch NaN) mithilfe einer Schleife oder einer benutzerdefinierten Funktion, die an übergeben wird .apply():
import pandas as pd
import numpy as np
import time
# Create a mock catalog of 100,000 objects grouped by class
n_items = 100000
classes = (f"CAT_{i}" for i in vary(100))
df = pd.DataFrame({
'class': np.random.selection(classes, dimension=n_items),
'worth': np.random.uniform(10.0, 500.0, dimension=n_items)
})
# Introduce 10% lacking costs (NaN)
nan_mask = np.random.rand(n_items) < 0.1
df.loc(nan_mask, 'worth') = np.nan
df_clunky = df.copy()
start_time = time.time()
# Break up-apply-combine utilizing apply() with a customized lambda
df_clunky('worth') = df_clunky.groupby('class')('worth').apply(lambda x: x.fillna(x.imply())).reset_index(stage=0, drop=True)
duration_clunky = time.time() - start_time
print(f"Apply-based group imputation took: {duration_clunky:.4f} seconds")
Durch Hebelwirkung .rework()umgehen wir benutzerdefinierte Lambda-Schleifen und ermöglichen Pandas, die Indexausrichtung und Vektorisierung nativ zu handhaben:
import pandas as pd
import numpy as np
import time
# Use the identical setup
df_optimized = df.copy()
start_time = time.time()
# Optimized method utilizing rework
group_means = df_optimized.groupby('class')('worth').rework('imply')
df_optimized('worth') = df_optimized('worth').fillna(group_means)
duration_opt = time.time() - start_time
print(f"Remodel-based group imputation took: {duration_opt:.4f} seconds")
print(f"Speedup: {duration_clunky / duration_opt:.2f}x sooner")
Ausgabe:
Apply-based group imputation took: 0.0224 seconds
Remodel-based group imputation took: 0.0032 seconds
Speedup: 7.04x sooner
Verstehen wie .rework() „operiert“ ist der Schlüssel zum Schreiben von leistungsstarkem Pandas-Code:
- Wenn du rennst
df.groupby('class')('worth').rework('imply')Pandas berechnet den Durchschnittspreis für jede Kategorie. - Anstatt eine kleinere gruppierte Übersichtstabelle zurückzugeben,
.rework()Sendet die berechneten Werte zurück in die Größe und Ausrichtung des ursprünglichen DataFrame. Es wird eine Reihe mit genau der gleichen Länge wie der Originaldatensatz ausgegeben, wobei der Index giltienthält den Mittelwert der Gruppe dieser Zeileigehört dazu. - Wir können es dann verwenden
df('worth').fillna(group_means). Dadurch werden die fehlenden Werte durch eine saubere, vektorisierte, indexausgerichtete Zuweisung aufgefüllt.
Dieses Muster ist sehr vielseitig. Sie können damit eine Standardisierung auf Gruppenebene durchführen (z. B. Gruppenmittelwerte subtrahieren) oder fehlende Werte professional Gruppe vorwärts ergänzen, indem Sie Folgendes verwenden: df.groupby('group')('val').rework('ffill').
# Zusammenfassung
Indem Sie über einfache, naive Schleifenkonstrukte hinausgehen und idiomatische Pandas-Entwurfsmuster übernehmen, können Sie Datenvorbereitungspipelines erstellen, die sich nahtlos von lokalen Prototypen bis hin zu Produktionsumgebungen skalieren lassen.
Fassen wir noch einmal zusammen:
- Methodenverkettung ersetzt spröde, mehrzeilige crucial Mutationen durch lesbare, deklarative Verarbeitungssequenzen, die vollständig vermeiden
SettingWithCopyWarning - Kategoriales Casting und vektorisierte String-Methoden Optimieren Sie Speicherlayouts und verlagern Sie String-Transformationen auf die Ausführung mit C-Geschwindigkeit, wodurch die RAM-Nutzung bei Daten mit geringer Kardinalität um bis zu 98 % gesenkt wird
- Gruppenbewusste Imputation mit
.rework()Berechnet Statistiken auf Gruppenebene und richtet sie nativ wieder auf die ursprünglichen Indexformen aus, wodurch langsame benutzerdefinierte Gruppierungsschleifen vermieden werden
Wenn Sie diese Muster in Ihre tägliche Arbeit integrieren, werden Ihre Function-Engineering- und Datenbereinigungsprozesse schnell, sauber und äußerst wartbar.
Matthew Mayo (@mattmayo13) hat einen Grasp-Abschluss in Informatik und ein Diplom in Information Mining. Als geschäftsführender Herausgeber von KDnuggets & Statistikund Mitherausgeber bei Beherrschung des maschinellen LernensZiel von Matthew ist es, komplexe datenwissenschaftliche Konzepte zugänglich zu machen. Zu seinen beruflichen Interessen zählen die Verarbeitung natürlicher Sprache, Sprachmodelle, Algorithmen für maschinelles Lernen und die Erforschung neuer KI. Seine Mission ist es, das Wissen in der Datenwissenschaftsgemeinschaft zu demokratisieren. Matthew programmiert seit seinem sechsten Lebensjahr.
