Wie iteriere ich über Zeilen in einem DataFrame in Pandas?
Antwort: NICHT * !
Die Iteration bei Pandas ist ein Anti-Muster und sollte nur durchgeführt werden, wenn Sie alle anderen Optionen ausgeschöpft haben. Sie sollten keine Funktion mit " iter
" im Namen für mehr als ein paar tausend Zeilen verwenden, da Sie sich sonst an viel Warten gewöhnen müssen .
Möchten Sie einen DataFrame drucken? Verwenden Sie DataFrame.to_string()
.
Möchten Sie etwas berechnen? Suchen Sie in diesem Fall nach Methoden in dieser Reihenfolge (Liste von hier geändert ):
- Vektorisierung
- Cython-Routinen
- Listenverständnisse (Vanille-
for
Schleife)
DataFrame.apply()
: i) Reduktionen, die in Cython durchgeführt werden können, ii) Iteration im Pythonraum
DataFrame.itertuples()
und iteritems()
DataFrame.iterrows()
iterrows
und itertuples
(beide erhalten viele Stimmen bei der Beantwortung dieser Frage) sollten in sehr seltenen Fällen verwendet werden, z. B. zum Generieren von Zeilenobjekten / Namensetupeln für die sequentielle Verarbeitung, was wirklich das einzige ist, wofür diese Funktionen nützlich sind.
Appell an die Behörde
Auf der Dokumentseite zur Iteration befindet sich ein großes rotes Warnfeld mit der Aufschrift:
Das Iterieren durch Pandas-Objekte ist im Allgemeinen langsam. In vielen Fällen ist es nicht erforderlich, manuell [...] über die Zeilen zu iterieren.
* Es ist eigentlich etwas komplizierter als "nicht". df.iterrows()
ist die richtige Antwort auf diese Frage, aber "vectorize your ops" ist die bessere. Ich werde zugeben, dass es Umstände gibt, unter denen eine Iteration nicht vermieden werden kann (zum Beispiel einige Operationen, bei denen das Ergebnis von dem für die vorherige Zeile berechneten Wert abhängt). Es bedarf jedoch einiger Vertrautheit mit der Bibliothek, um zu wissen, wann. Wenn Sie nicht sicher sind, ob Sie eine iterative Lösung benötigen, tun Sie dies wahrscheinlich nicht. PS: Um mehr über meine Gründe für das Schreiben dieser Antwort zu erfahren, gehen Sie ganz nach unten.
Eine gute Anzahl grundlegender Operationen und Berechnungen wird von Pandas "vektorisiert" (entweder über NumPy oder über zythonisierte Funktionen). Dies umfasst Arithmetik, Vergleiche, (die meisten) Reduzierungen, Umformen (z. B. Schwenken), Verknüpfungen und Groupby-Operationen. In der Dokumentation zu Essential Basic Functionality finden Sie eine geeignete vektorisierte Methode für Ihr Problem.
Wenn keine vorhanden ist, können Sie Ihre eigenen mit benutzerdefinierten Cython-Erweiterungen schreiben .
Listenverständnisse sollten Ihre nächste Anlaufstelle sein, wenn 1) keine vektorisierte Lösung verfügbar ist, 2) die Leistung wichtig, aber nicht wichtig genug ist, um den Aufwand für die Cythonisierung Ihres Codes zu bewältigen, und 3) Sie versuchen, eine elementweise Transformation durchzuführen auf Ihrem Code. Es gibt zahlreiche Hinweise darauf, dass das Listenverständnis für viele gängige Pandas-Aufgaben ausreichend schnell (und manchmal sogar schneller) ist.
Die Formel ist einfach,
# iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# iterating over multiple columns - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
Wenn Sie Ihre Geschäftslogik in eine Funktion einkapseln können, können Sie ein Listenverständnis verwenden, das sie aufruft. Durch die Einfachheit und Geschwindigkeit von Raw Python können Sie beliebig komplexe Dinge zum Laufen bringen.
Vorsichtsmaßnahmen Das
Verständnis der Liste setzt voraus, dass Ihre Daten einfach zu verarbeiten sind. Dies bedeutet, dass Ihre Datentypen konsistent sind und Sie keine NaNs haben. Dies kann jedoch nicht immer garantiert werden.
- Die erste ist offensichtlicher, aber wenn Sie sich mit NaNs befassen, bevorzugen Sie eingebaute Pandas-Methoden, falls vorhanden (weil sie eine viel bessere Logik für die Behandlung von Eckfällen haben), oder stellen Sie sicher, dass Ihre Geschäftslogik eine geeignete NaN-Handhabungslogik enthält.
- Wenn Sie mit gemischten Datentypen arbeiten, sollten Sie iterieren,
zip(df['A'], df['B'], ...)
anstatt df[['A', 'B']].to_numpy()
diese implizit auf den am häufigsten verwendeten Typ zu übertragen. Wenn beispielsweise A numerisch und B eine Zeichenfolge ist, to_numpy()
wird das gesamte Array in eine Zeichenfolge umgewandelt, was möglicherweise nicht Ihren Wünschen entspricht. Glücklicherweise ist das zip
Pingen Ihrer Spalten die einfachste Problemumgehung.
* YMMV für die im genannten Gründen Caveats Abschnitt oben.
Ein offensichtliches Beispiel
Lassen Sie uns den Unterschied anhand eines einfachen Beispiels für das Hinzufügen von zwei Pandas-Spalten demonstrieren A + B
. Dies ist eine vektorisierbare Operation, so dass es leicht ist, die Leistung der oben diskutierten Methoden gegenüberzustellen.
Benchmarking-Code als Referenz.
Ich sollte jedoch erwähnen, dass es nicht immer so geschnitten und trocken ist. Manchmal lautet die Antwort auf "Was ist die beste Methode für eine Operation?" "Es hängt von Ihren Daten ab". Mein Rat ist, verschiedene Ansätze für Ihre Daten zu testen, bevor Sie sich für einen entscheiden.
Weiterführende Literatur
* Pandas-String-Methoden werden in dem Sinne "vektorisiert", dass sie in der Reihe angegeben sind, aber für jedes Element gelten. Die zugrunde liegenden Mechanismen sind immer noch iterativ, da Zeichenfolgenoperationen von Natur aus schwer zu vektorisieren sind.
Warum ich diese Antwort geschrieben habe
Ein häufiger Trend, den ich bei neuen Benutzern bemerke, besteht darin, Fragen an das Formular "Wie kann ich über meine df iterieren, um X auszuführen?" Zu stellen. Anzeigen von Code, der aufgerufen wird, iterrows()
während etwas in einer for-Schleife ausgeführt wird. Hier ist warum. Ein neuer Benutzer in der Bibliothek, der nicht mit dem Konzept der Vektorisierung vertraut ist, wird sich wahrscheinlich den Code vorstellen, der sein Problem löst, indem er über seine Daten iteriert, um etwas zu tun. Da sie nicht wissen, wie sie über einen DataFrame iterieren sollen, googeln sie ihn zuerst und landen hier bei dieser Frage. Sie sehen dann die akzeptierte Antwort, die ihnen sagt, wie es geht, und sie schließen die Augen und führen diesen Code aus, ohne vorher zu fragen, ob Iteration nicht das Richtige ist.
Ziel dieser Antwort ist es, neuen Benutzern zu vermitteln, dass Iteration nicht unbedingt die Lösung für jedes Problem ist und dass es bessere, schnellere und idiomatischere Lösungen geben kann und dass es sich lohnt, Zeit in ihre Erforschung zu investieren. Ich versuche nicht, einen Krieg zwischen Iteration und Vektorisierung zu beginnen, aber ich möchte, dass neue Benutzer informiert werden, wenn sie Lösungen für ihre Probleme mit dieser Bibliothek entwickeln.