So beschleunigen Sie Pandas-Code – Vektorisierung
Wenn wir unsere Deep-Studying-Modelle anhand eines Datensatzes trainieren möchten, müssen wir unseren Code optimieren, um diese Daten schnell zu analysieren. Wir möchten unsere Datentabellen so schnell wie möglich lesen und dabei unseren Code auf optimierte Weise schreiben. Selbst die kleinste Leistungssteigerung verbessert die Leistung über Zehntausende von Datenpunkten exponentiell. In diesem Weblog definieren wir Pandas und geben ein Beispiel, wie Sie Ihren Python-Code vektorisieren können, um die Datensatzanalyse mithilfe von Pandas zu optimieren und Ihren Code über 300-mal schneller zu machen.
Was ist Pandas für Python?
Pandas ist eine wichtige und beliebte Open-Supply-Bibliothek zur Datenmanipulation und Datenanalyse für die Programmiersprache Python. Pandas wird häufig in verschiedenen Bereichen wie Finanzen, Wirtschaft, Sozialwissenschaften und Ingenieurwesen verwendet. Es eignet sich intestine für die Datenbereinigung, -aufbereitung und -analyse in Information-Science- und Machine-Studying-Aufgaben.
Es bietet leistungsstarke Datenstrukturen (wie DataFrame und Sequence) und Datenbearbeitungstools für die Arbeit mit strukturierten Daten, einschließlich Lesen und Schreiben von Daten in verschiedenen Formaten (z. B. CSV, Excel, JSON) sowie Filtern, Bereinigen und Transformieren von Daten. Darüber hinaus unterstützt es Zeitreihendaten und bietet leistungsstarke Datenaggregations- und Visualisierungsfunktionen durch die Integration mit anderen beliebten Bibliotheken wie NumPy und Matplotlib.
Unser Datensatz und Downside
Die Daten
In diesem Beispiel erstellen wir einen zufälligen Datensatz in einem Jupyter-Notizbuch Wir verwenden NumPy, um unseren Pandas-Datenrahmen mit beliebigen Werten und Zeichenfolgen zu füllen. In diesem Datensatz benennen wir 10.000 Personen unterschiedlichen Alters, geben die Dauer ihrer Arbeitszeit und den Prozentsatz ihrer produktiven Arbeitszeit an. Außerdem wird ihnen eine zufällige Lieblingsspeise sowie ein zufälliges schlechtes Karma-Ereignis zugewiesen.
Wir importieren zunächst unsere Frameworks und generieren einen zufälligen Code, bevor wir beginnen:
import pandas as pd
import numpy as np
Als Nächstes erstellen wir unseren Datensatz, indem wir einige zufällige Daten erstellen. Ihr Code wird höchstwahrscheinlich auf tatsächlichen Daten basieren, aber für unseren Anwendungsfall erstellen wir einige beliebige Daten.
def get_data(dimension = 10_000):
df = pd.DataFrame()
df('age') = np.random.randint(0, 100, dimension)
df('time_at_work') = np.random.randint(0,8,dimension)
df('percentage_productive') = np.random.rand(dimension)
df('favorite_treat') = np.random.selection(('ice_cream', 'boba', 'cookie'), dimension)
df('bad_karma') = np.random.selection(('stub_toe', 'wifi_malfunction', 'extra_traffic'))
return df
Die Parameter und Regeln
- Wenn die Arbeitszeit einer Individual mindestens 2 Stunden beträgt UND der Produktivitätsanteil über 50 % liegt, geben wir die „Lieblingsleckerei“ zurück.
- Andernfalls geben wir ihnen „schlechtes Karma“.
- Wenn sie über 65 Jahre alt sind, geben wir ihnen ein „Lieblingsleckerli“ zurück, da wir unsere Senioren glücklich machen möchten.
def reward_calc(row):
if row('age') >= 65:
return row ('favorite_treat')
if (row('time_at_work') >= 2) & (row('percentage_productive') >= 0.5):
return row ('favorite_treat')
return row('bad_karma')
Da wir nun über unseren Datensatz und die Parameter für die gewünschte Rückgabe verfügen, können wir fortfahren und die schnellste Möglichkeit zur Ausführung dieser Artwork von Analyse erkunden.
Welcher Pandas-Code ist am schnellsten: Looping, Apply oder Vektorisierung?
Um unsere Funktionen zeitlich zu messen, verwenden wir ein Jupyter-Pocket book, um es mit der magischen Funktion %%timeit relativ einfach zu machen. Es gibt andere Möglichkeiten, eine Funktion in Python zeitlich zu messen, aber zu Demonstrationszwecken reicht unser Jupyter-Pocket book aus. Wir werden einen Demolauf auf demselben Datensatz mit 3 Möglichkeiten zum Berechnen und Auswerten unseres Issues durchführen, indem wir Looping/Iterating, Apply und Vectorization verwenden.
Schleife/Iteriere
Schleifen und Iterieren ist die einfachste Methode, um dieselbe Berechnung Zeile für Zeile durchzuführen. Wir rufen den Datenrahmen auf und iterieren Zeilen mit einer neuen Zelle namens „reward“ und führen die Berechnung aus, um die neue Zelle auszufüllen. reward
nach unseren zuvor definierten reward_calc
Codeblock. Dies ist die grundlegendste und wahrscheinlich die erste Methode, die man beim Codieren lernt, das For-Schleifen ähnelt.
%%timeit
df = get_data()
for index, row in df.iterrows():
df.loc(index, 'reward') = reward_calc(row)
Das ist das Ergebnis:
3.66 s ± 119 ms per loop (imply ± std. dev. of seven runs, 1 loop every)
Unerfahrene Datenwissenschaftler halten ein paar Sekunden vielleicht für keine große Sache. Aber 3,66 Sekunden sind ziemlich lang, um eine einfache Funktion durch einen Datensatz laufen zu lassen. Sehen wir uns an, was die apply
Funktion kann für uns hinsichtlich der Geschwindigkeit tun.
Anwenden
Der apply
Funktion macht effektiv dasselbe wie die Schleife. Sie erstellt eine neue Spalte mit dem Titel „Belohnung“ und wendet die Berechnungsfunktion jede Zeile an, wie definiert durch axis=1
. Der apply
Funktion ist eine schnellere Möglichkeit, eine Schleife für Ihren Datensatz auszuführen.
%%timeit
df = get_data()
df('reward') = df.apply(reward_calc, axis=1)
Die Laufzeit beträgt:
404 ms ± 18.2 ms per loop (imply ± std. dev. of seven runs, 1 loop every)
Wow, so viel schneller! Ungefähr 9x schneller, eine enorme Verbesserung gegenüber einer Schleife. Die Apply-Funktion ist jetzt völlig problemlos zu verwenden und wird in bestimmten Szenarien anwendbar sein, aber für unseren Anwendungsfall wollen wir sehen, ob wir sie noch weiter beschleunigen können.
Vektorisierung
Unsere letzte und endgültige Möglichkeit, diesen Datensatz auszuwerten, ist die Vektorisierung. Wir rufen unseren Datensatz auf und wenden die Standardbelohnung an: bad_karma
auf den gesamten Datenrahmen. Dann prüfen wir nur diejenigen, die unsere Parameter mit boolescher Indizierung erfüllen. Stellen Sie es sich so vor, als würden Sie für jede Zeile einen Wahr/Falsch-Wert festlegen. Wenn eine oder alle Zeilen in unserer Berechnung Falsch zurückgeben, dann reward
Zeile bleibt bad_karma
. Wenn alle Zeilen wahr sind, definieren wir den Datenrahmen für die reward
Zeile als favorite_treat
.
%%timeit
df = get_data()
df('reward') = df('bad_karma')
df.loc(((df('percentage_productive') >= 0.5) &
(df('time_at_work') >= 2)) |
(df('age') >= 65), 'reward') = df('favorite_treat')
Die zum Ausführen dieser Funktion auf unserem Datensatz benötigte Zeit ist wie folgt:
10.4 ms ± 76.2 µs per loop (imply ± std. dev. of seven runs, 100 loops every)
Das ist extrem schnell. 40x schneller als das Apply und ungefähr 360x schneller als Looping…
Warum die Vektorisierung in Pandas über 300-mal schneller ist
Der Grund, warum die Vektorisierung so viel schneller ist als Schleifen/Iterieren und Anwenden, liegt darin, dass nicht jedes Mal die gesamte Zeile berechnet wird, sondern die Parameter auf den gesamten Datensatz als Ganzes angewendet werden. Bei der Vektorisierung werden Operationen auf ganze Datenarrays gleichzeitig angewendet, anstatt jedes Component des Arrays einzeln zu bearbeiten. Dies ermöglicht eine viel effizientere Nutzung von Speicher- und CPU-Ressourcen.
Wenn Sie Schleifen oder Anwenden verwenden, um Berechnungen für einen Pandas-Datenrahmen durchzuführen, wird der Vorgang sequenziell ausgeführt. Dies führt zu wiederholten Zugriffen auf Speicher, Berechnungen und aktualisierte Werte, was langsam und ressourcenintensiv sein kann.
Vektorisierte Operationen hingegen werden in Cython (Python in C oder C++) implementiert und nutzen die Vektorverarbeitungsfunktionen der CPU, die mehrere Operationen gleichzeitig ausführen können, wodurch die Leistung durch die gleichzeitige Berechnung mehrerer Parameter weiter gesteigert wird. Vektorisierte Operationen vermeiden auch den Overhead des ständigen Speicherzugriffs, der die Krücke von Loop und Apply ist.
So vektorisieren Sie Ihren Pandas-Code
- Verwenden Sie integrierte Pandas- und NumPy-Funktionen, die C wie implementiert haben Summe(), bedeuten()oder max().
- Verwenden Sie vektorisierte Operationen, die auf ganze DataFrames und Serien angewendet werden können, einschließlich mathematischer Operationen, Vergleiche und Logik, um eine Boolesche Maske zum Auswählen mehrerer Zeilen aus Ihrem Datensatz zu erstellen.
- Du kannst den … benutzen .Werte Attribut oder das
.to_numpy()
um das zugrunde liegende NumPy-Array zurückzugeben und vektorisierte Berechnungen direkt auf dem Array durchzuführen. - Verwenden Sie vektorisierte Zeichenfolgenoperationen, um sie auf Ihren Datensatz anzuwenden, wie beispielsweise
.str.incorporates()
,.str.exchange()
Und.str.break up()
.
Versuchen Sie beim Schreiben von Funktionen in Pandas DataFrames, Ihre Berechnungen so weit wie möglich zu vektorisieren. Da die Datensätze immer größer und Ihre Berechnungen immer komplexer werden, summieren sich die Zeiteinsparungen exponentiell, wenn Sie Vektorisierung verwenden. Es ist zu beachten, dass nicht alle Operationen vektorisiert werden können und es manchmal notwendig ist, Schleifen zu verwenden oder Funktionen anzuwenden. Wo immer es jedoch möglich ist, können vektorisierte Operationen die Leistung erheblich verbessern und Ihren Code effizienter machen.
Kevin Vu verwaltet Weblog von Exxact Corp und arbeitet mit vielen seiner talentierten Autoren zusammen, die über verschiedene Aspekte des Deep Studying schreiben.