Wie iteriere ich über Zeilen in einem DataFrame in Pandas?


1949

Ich habe eine DataFramevon Pandas:

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df

Ausgabe:

   c1   c2
0  10  100
1  11  110
2  12  120

Jetzt möchte ich über die Zeilen dieses Rahmens iterieren. Für jede Zeile möchte ich über den Namen der Spalten auf ihre Elemente (Werte in Zellen) zugreifen können. Zum Beispiel:

for row in df.rows:
   print row['c1'], row['c2']

Ist das bei Pandas möglich?

Ich habe diese ähnliche Frage gefunden . Aber es gibt mir nicht die Antwort, die ich brauche. Zum Beispiel wird dort empfohlen, Folgendes zu verwenden:

for date, row in df.T.iteritems():

oder

for row in df.iterrows():

Aber ich verstehe nicht, was das rowObjekt ist und wie ich damit arbeiten kann.


11
Das df.iteritems () iteriert über Spalten und nicht über Zeilen. Damit es über Zeilen iteriert, müssen Sie transponieren (das "T"), was bedeutet, dass Sie Zeilen und Spalten ineinander ändern (über Diagonale reflektieren). Infolgedessen iterieren Sie effektiv den ursprünglichen Datenrahmen über seine Zeilen, wenn Sie df.T.iteritems ()
Stefan Gruenwald

11
Wenn Sie neu in diesem Thread sind und Anfänger in Pandas sind, ITERIEREN SIE NICHT !! Die Iteration über Datenrahmen ist ein Anti-Pattern und etwas, das Sie nicht tun sollten, es sei denn, Sie möchten sich an viel Warten gewöhnen. Je nachdem, was Sie versuchen, gibt es möglicherweise viel bessere Alternativen . iter*Funktionen sollten in sehr seltenen Fällen verwendet werden. Auch verwandt .
CS95

18
Im Gegensatz zu den Aussagen von cs95 gibt es gute Gründe, über einen Datenrahmen zu iterieren, sodass sich neue Benutzer nicht entmutigt fühlen sollten. Ein Beispiel ist, wenn Sie Code ausführen möchten, indem Sie die Werte jeder Zeile als Eingabe verwenden. Wenn Ihr Datenrahmen relativ klein ist (z. B. weniger als 1000 Elemente), ist die Leistung kein wirkliches Problem.
Oulenz

1
@oulenz: Wenn Sie aus irgendeinem Grund angesichts der Verwendung der API für den Zweck, für den sie entwickelt wurde (Hochleistungs-Datentransformationen), fliegen möchten, dann seien Sie mein Gast. Aber zumindest nicht verwenden iterrows, es gibt bessere Möglichkeiten, über einen DataFrame zu iterieren. Sie können an diesem Punkt auch einfach über eine Liste von Listen iterieren. Wenn Sie an dem Punkt angelangt sind, an dem Sie nur noch über DataFrames iterieren, hat die Verwendung eines DataFrames überhaupt keinen Vorteil (vorausgesetzt, Sie iterieren nur darüber). Nur mein 2c.
CS95

7
Ich zweite @oulenz. Soweit ich das beurteilen kann, pandasist es die erste Wahl, eine CSV-Datei zu lesen, auch wenn der Datensatz klein ist. Es ist einfach einfacher zu programmieren, die Daten mit APIs zu manipulieren
Chris

Antworten:


2635

DataFrame.iterrows ist ein Generator, der sowohl Index als auch Zeile liefert

import pandas as pd
import numpy as np

df = pd.DataFrame([{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}])

for index, row in df.iterrows():
    print(row['c1'], row['c2'])

Output: 
   10 100
   11 110
   12 120

207
Hinweis: „Weil iterrows eine Serie für jede Zeile zurückgibt, ist es nicht bewahren dtypes über die Reihen.“ "Sie sollten niemals etwas ändern , über das Sie iterieren." Laut Pandas 0.19.1 docs
viddik13

3
@ viddik13 das ist eine tolle Notiz danke. Aus diesem Grund bin ich auf einen Fall gestoßen, in dem numerische Werte wie 431341610650wo gelesen wurden als 4.31E+11. Gibt es einen Weg, um die d-Typen zu erhalten?
Aziz Alto

26
@AzizAlto verwenden itertuples, wie unten erklärt. Siehe auch pandas.pydata.org/pandas-docs/stable/generated/…
Axel

101
Verwenden Sie keine Iterrows. Itertuples ist schneller und behält den Datentyp bei. Weitere Informationen
James L.

11
Aus der Dokumentation : "Das Durchlaufen von Pandas-Objekten ist im Allgemeinen langsam. In vielen Fällen ist ein manuelles Durchlaufen der Zeilen nicht erforderlich [...]". Ihre Antwort ist richtig (im Kontext der Frage), erwähnt dies jedoch nirgendwo, so dass es keine sehr gute ist.
cs95

458

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 ):

  1. Vektorisierung
  2. Cython-Routinen
  3. Listenverständnisse (Vanille- forSchleife)
  4. DataFrame.apply(): i) Reduktionen, die in Cython durchgeführt werden können, ii) Iteration im Pythonraum
  5. DataFrame.itertuples() und iteritems()
  6. DataFrame.iterrows()

iterrowsund 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.


Schneller als Looping: Vektorisierung , Cython

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 .


Next Best Thing: Listenverständnisse *

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.

  1. 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.
  2. 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 zipPingen 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.

Geben Sie hier die Bildbeschreibung ein

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.


24
Dies ist die einzige Antwort, die sich auf die idiomatischen Techniken konzentriert, die man bei Pandas anwenden sollte, was sie zur besten Antwort auf diese Frage macht. Das Lernen, die richtige Antwort mit dem richtigen Code zu erhalten (anstelle der richtigen Antwort mit dem falschen Code - dh ineffizient, nicht skalierbar, zu passend für bestimmte Daten), ist ein großer Teil des Lernens von Pandas (und Daten im Allgemeinen).
LinkBerest

3
Ich denke, Sie sind der for-Schleife gegenüber unfair, da sie in meinen Tests nur ein bisschen langsamer sind als das Listenverständnis. Der Trick besteht darin, zip(df['A'], df['B'])statt zu schleifen df.iterrows().
Unvergängliche Nacht

2
@ImperishableNight Überhaupt nicht; In diesem Beitrag geht es nicht darum, die Iteration im Allgemeinen anzuprangern, sondern darum, die Verwendung von iterrows()Iterationen spezifisch anzuprangern und sie implizit anzuprangern, wenn und wann bessere Alternativen existieren. forSchleifen alleine sind in Ordnung, aber Listenverständnisse sind besser, wenn Sie iterativ elementweise Transformationen durchführen.
CS95

1
@sdbbs gibt es, verwenden Sie sort_values, um Ihre Daten zu sortieren, und rufen Sie dann to_string () für das Ergebnis auf.
CS95

1
Unter Listenverständnis erfordert das Beispiel "Durchlaufen mehrerer Spalten" eine Einschränkung: DataFrame.valuesKonvertiert jede Spalte in einen gemeinsamen Datentyp. DataFrame.to_numpy()macht das auch. Glücklicherweise können wir zipmit einer beliebigen Anzahl von Spalten verwenden.
David Wasserman

397

Überlegen Sie zunächst, ob Sie wirklich über Zeilen in einem DataFrame iterieren müssen. In dieser Antwort finden Sie Alternativen.

Wenn Sie immer noch über Zeilen iterieren müssen, können Sie die folgenden Methoden verwenden. Beachten Sie einige wichtige Vorsichtsmaßnahmen, die in keiner der anderen Antworten erwähnt werden.

itertuples() soll schneller sein als iterrows()

Beachten Sie jedoch laut den Dokumenten (Pandas 0.24.2 im Moment):

  • iterrows: stimmt dtypemöglicherweise nicht von Zeile zu Zeile überein

    Da iterrows eine Serie für jede Zeile zurückgibt, ist es nicht beibehalten dtypes über die Reihen (dtypes werden über mehrere Spalten für Datenrahmen erhalten). Um dtypes beim Durchlaufen der Zeilen beizubehalten, ist es besser, itertuples () zu verwenden, das namedtuples der Werte zurückgibt und im Allgemeinen viel schneller als iterrows () ist.

  • iterrows: Ändern Sie keine Zeilen

    Sie sollten niemals etwas ändern , über das Sie iterieren. Dies funktioniert nicht garantiert in allen Fällen. Abhängig von den Datentypen gibt der Iterator eine Kopie und keine Ansicht zurück, und das Schreiben darauf hat keine Auswirkung.

    Verwenden Sie stattdessen DataFrame.apply () :

    new_df = df.apply(lambda x: x * 2)
  • itertuples:

    Die Spaltennamen werden in Positionsnamen umbenannt, wenn sie ungültige Python-Bezeichner sind, wiederholt werden oder mit einem Unterstrich beginnen. Bei einer großen Anzahl von Spalten (> 255) werden reguläre Tupel zurückgegeben.

Weitere Informationen finden Sie in den Pandas-Dokumenten zur Iteration .


4
Nur eine kleine Frage von jemandem, der diesen Thread so lange nach seiner Fertigstellung liest: Wie ist df.apply () im Vergleich zu itertuples in Bezug auf Effizienz?
Raul Guarini

4
Hinweis: Sie können auch sagen for row in df[['c1','c2']].itertuples(index=True, name=None):, dass nur bestimmte Spalten in den Zeileniterator aufgenommen werden sollen.
Brian Burns

12
Stattdessen getattr(row, "c1")können Sie nur verwenden row.c1.
Viraptor

1
Ich bin zu etwa 90% sicher, dass Sie bei Verwendung von getattr(row, "c1")anstelle von row.c1Leistungsvorteilen verlieren itertuplesund stattdessen iterrows verwenden sollten, wenn Sie tatsächlich über eine Zeichenfolge zur Eigenschaft gelangen müssen.
Noctiphobia

3
Ich bin auf diese Frage gestoßen, weil ich, obwohl ich wusste, dass es Split- Apply -Combine gibt, wirklich über einen DataFrame iterieren musste (wie in der Frage angegeben). Nicht jeder hat den Luxus, sich mit numbaund zu verbessern cython(dieselben Dokumente sagen, dass es sich immer lohnt, zuerst in Python zu optimieren). Ich habe diese Antwort geschrieben, um anderen zu helfen, (manchmal frustrierende) Probleme zu vermeiden, da keine der anderen Antworten diese Vorbehalte erwähnt. Jemanden irrezuführen oder zu sagen, "das ist das Richtige", war nie meine Absicht. Ich habe die Antwort verbessert.
viddik13

201

Sie sollten verwenden df.iterrows(). Das zeilenweise Durchlaufen ist jedoch nicht besonders effizient, da SeriesObjekte erstellt werden müssen.


12
Ist dies schneller als die Konvertierung des DataFrame in ein Numpy-Array (über .values) und die direkte Bearbeitung des Arrays? Ich habe das gleiche Problem, konvertierte aber schließlich in ein Numpy-Array und verwendete dann Cython.
Vgoklani

12
@vgoklani Wenn das zeilenweise Durchlaufen ineffizient ist und Sie ein Numpy-Array ohne Objekt haben, ist die Verwendung des rohen Numpy-Arrays mit ziemlicher Sicherheit schneller, insbesondere bei Arrays mit vielen Zeilen. Sie sollten es vermeiden, über Zeilen zu iterieren, es sei denn, Sie müssen unbedingt
Phillip Cloud

7
Ich habe den Zeitverbrauch für df.iterrows (), df.itertuples () und zip (df ['a'], df ['b']) ein wenig getestet und das Ergebnis in der Antwort eines anderen veröffentlicht Frage: stackoverflow.com/a/34311080/2142098
Richard Wong

154

Dies iterrows()ist zwar eine gute Option, itertuples()kann aber manchmal viel schneller sein:

df = pd.DataFrame({'a': randn(1000), 'b': randn(1000),'N': randint(100, 1000, (1000)), 'x': 'x'})

%timeit [row.a * 2 for idx, row in df.iterrows()]
# => 10 loops, best of 3: 50.3 ms per loop

%timeit [row[1] * 2 for row in df.itertuples()]
# => 1000 loops, best of 3: 541 µs per loop

5
Ein Großteil des Zeitunterschieds in Ihren beiden Beispielen scheint darauf zurückzuführen zu sein, dass Sie anscheinend die beschriftungsbasierte Indizierung für den Befehl .iterrows () und die ganzzahlige Indizierung für den Befehl .itertuples () verwenden.
Alex

2
Bei einem auf Finanzdaten basierenden Datenrahmen (Zeitstempel und 4x Float) sind Itertuples 19,57-mal schneller als Iterrows auf meinem Computer. Nur for a,b,c in izip(df["a"],df["b"],df["c"]:ist fast gleich schnell.
Harbun

7
Können Sie erklären, warum es schneller ist?
Abe Miessler

4
@AbeMiessler fasst iterrows()jede Datenzeile in eine Reihe ein, während itertuples()dies nicht der Fall ist.
Miradulo

3
Beachten Sie, dass die Reihenfolge der Spalten tatsächlich unbestimmt ist, da dfsie aus einem Wörterbuch erstellt wurde und sich daher row[1]auf eine der Spalten beziehen kann. Wie sich herausstellt, sind die Zeiten für die Ganzzahl im Vergleich zu den Float-Spalten ungefähr gleich.
Brian Burns

88

Sie können auch df.apply()Zeilen durchlaufen und auf mehrere Spalten für eine Funktion zugreifen.

docs: DataFrame.apply ()

def valuation_formula(x, y):
    return x * y * 0.5

df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)

Bezieht sich der df ['Preis'] auf einen Spaltennamen im Datenrahmen? Ich versuche, ein Wörterbuch mit eindeutigen Werten aus mehreren Spalten in einer CSV-Datei zu erstellen. Ich habe Ihre Logik verwendet, um ein Wörterbuch mit eindeutigen Schlüsseln und Werten zu erstellen, und habe eine Fehlermeldung erhalten, die TypeError angibt: ("'Serien'-Objekte sind veränderbar, daher können sie nicht gehasht werden", u'occed at index 0')
SRS

Code: df ['Workclass'] = df.apply (Lambda-Zeile: dic_update (Zeile), Achse = 1) Zeilenende- ID = 0 Zeilenende def dic_update (Zeile): Wenn Zeile nicht in dic: dic [Zeile] = id id = id + 1
SRS

Egal, ich habe es verstanden. Die Funktionsaufrufzeile wurde in df_new = df ['Workclass'] geändert. Apply (dasselbe)
SRS

2
Die Standardeinstellung der Achse auf 0 zu setzen ist das Schlimmste
zthomas.nc

9
Beachten Sie, dass applynicht über Zeilen "iteriert" wird, sondern eine Funktion zeilenweise angewendet wird. Der obige Code würde nicht funktionieren , wenn Sie wirklich tun müssen Iterationen und Indizes, zum Beispiel , wenn die Werte in den verschiedenen Reihen zu vergleichen (in diesem Fall können Sie tun nichts anderes als Iterieren).
Gented

82

Sie können die Funktion df.iloc wie folgt verwenden:

for i in range(0, len(df)):
    print df.iloc[i]['c1'], df.iloc[i]['c2']

1
Ich weiß, dass man dies zugunsten von Iterrows oder Itertuples vermeiden sollte, aber es wäre interessant zu wissen, warum. Irgendwelche Gedanken?
Rocarvaj

12
Dies ist die einzige mir bekannte gültige Technik, wenn Sie die Datentypen beibehalten und Spalten nach Namen referenzieren möchten. itertuplesErhält Datentypen, entfernt jedoch alle Namen, die ihm nicht gefallen. iterrowsmacht das Gegenteil.
Ken Williams

6
Verbrachte Stunden damit, durch die Eigenheiten der Pandas-Datenstrukturen zu waten, um etwas Einfaches UND Ausdrucksvolles zu tun. Dies führt zu lesbarem Code.
Sean Anderson

Obwohl for i in range(df.shape[0])dieser Ansatz möglicherweise etwas beschleunigt wird, ist er für meine Anwendung immer noch etwa 3,5-mal langsamer als der oben beschriebene iterrows () -Ansatz.
Kim Miller

Bei großen Datafrmes scheint dies besser zu sein, da my_iter = df.itertuples()der Speicher doppelt so viel Speicherplatz benötigt und viel Zeit zum Kopieren benötigt wird. Gleiches gilt für iterrows().
Bastiaan

33

Ich suchte nach dem Iterieren von Zeilen UND Spalten und endete hier so:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)

18

Sie können Ihren eigenen Iterator schreiben, der implementiert namedtuple

from collections import namedtuple

def myiter(d, cols=None):
    if cols is None:
        v = d.values.tolist()
        cols = d.columns.values.tolist()
    else:
        j = [d.columns.get_loc(c) for c in cols]
        v = d.values[:, j].tolist()

    n = namedtuple('MyTuple', cols)

    for line in iter(v):
        yield n(*line)

Dies ist direkt vergleichbar mit pd.DataFrame.itertuples. Ich möchte dieselbe Aufgabe effizienter ausführen.


Für den angegebenen Datenrahmen mit meiner Funktion:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

Oder mit pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

Ein umfassender Test
Wir testen, ob alle Spalten verfügbar sind und die Spalten untergeordnet werden.

def iterfullA(d):
    return list(myiter(d))

def iterfullB(d):
    return list(d.itertuples(index=False))

def itersubA(d):
    return list(myiter(d, ['col3', 'col4', 'col5', 'col6', 'col7']))

def itersubB(d):
    return list(d[['col3', 'col4', 'col5', 'col6', 'col7']].itertuples(index=False))

res = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    columns='iterfullA iterfullB itersubA itersubB'.split(),
    dtype=float
)

for i in res.index:
    d = pd.DataFrame(np.random.randint(10, size=(i, 10))).add_prefix('col')
    for j in res.columns:
        stmt = '{}(d)'.format(j)
        setp = 'from __main__ import d, {}'.format(j)
        res.at[i, j] = timeit(stmt, setp, number=100)

res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein


2
Für Leute, die den Code nicht lesen möchten: Blaue Linie ist intertuples, orange Linie ist eine Liste eines Iterators durch einen Ertragsblock. interrowswird nicht verglichen.
James L.

18

Wie kann ich effizient iterieren?

Wenn Sie einen Pandas-Datenrahmen wirklich iterieren müssen, sollten Sie wahrscheinlich die Verwendung von iterrows () vermeiden . Es gibt verschiedene Methoden und das Übliche iterrows()ist alles andere als das Beste. itertuples () kann 100-mal schneller sein.

Zusamenfassend:

  • Verwenden Sie in der Regel df.itertuples(name=None). Insbesondere, wenn Sie eine feste Anzahl von Spalten und weniger als 255 Spalten haben. Siehe Punkt (3)
  • Andernfalls verwenden Sie, df.itertuples()außer wenn Ihre Spalten Sonderzeichen wie Leerzeichen oder '-' enthalten. Siehe Punkt (2)
  • Die Verwendung ist itertuples()auch im letzten Beispiel möglich, wenn Ihr Datenrahmen seltsame Spalten enthält. Siehe Punkt (4)
  • Nur verwenden, iterrows()wenn Sie die vorherigen Lösungen nicht können. Siehe Punkt (1)

Verschiedene Methoden zum Durchlaufen von Zeilen in einem Pandas-Datenrahmen:

Generieren Sie einen zufälligen Datenrahmen mit einer Million Zeilen und 4 Spalten:

    df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
    print(df)

1) Das Übliche iterrows()ist praktisch, aber verdammt langsam:

start_time = time.clock()
result = 0
for _, row in df.iterrows():
    result += max(row['B'], row['C'])

total_elapsed_time = round(time.clock() - start_time, 2)
print("1. Iterrows done in {} seconds, result = {}".format(total_elapsed_time, result))

2) Die Standardeinstellung itertuples()ist bereits viel schneller, funktioniert jedoch nicht mit Spaltennamen wie My Col-Name is very Strange(Sie sollten diese Methode vermeiden, wenn Ihre Spalten wiederholt werden oder wenn ein Spaltenname nicht einfach in einen Python-Variablennamen konvertiert werden kann):

start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row.B, row.C)

total_elapsed_time = round(time.clock() - start_time, 2)
print("2. Named Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

3) Die Standardeinstellung itertuples()mit name = None ist noch schneller, aber nicht sehr praktisch, da Sie eine Variable pro Spalte definieren müssen.

start_time = time.clock()
result = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
    result += max(col2, col3)

total_elapsed_time = round(time.clock() - start_time, 2)
print("3. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

4) Schließlich ist der Name itertuples()langsamer als der vorherige Punkt, aber Sie müssen keine Variable pro Spalte definieren und es funktioniert mit Spaltennamen wie My Col-Name is very Strange.

start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row[df.columns.get_loc('B')], row[df.columns.get_loc('C')])

total_elapsed_time = round(time.clock() - start_time, 2)
print("4. Polyvalent Itertuples working even with special characters in the column name done in {} seconds, result = {}".format(total_elapsed_time, result))

Ausgabe:

         A   B   C   D
0       41  63  42  23
1       54   9  24  65
2       15  34  10   9
3       39  94  82  97
4        4  88  79  54
...     ..  ..  ..  ..
999995  48  27   4  25
999996  16  51  34  28
999997   1  39  61  14
999998  66  51  27  70
999999  51  53  47  99

[1000000 rows x 4 columns]

1. Iterrows done in 104.96 seconds, result = 66151519
2. Named Itertuples done in 1.26 seconds, result = 66151519
3. Itertuples done in 0.94 seconds, result = 66151519
4. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519

Dieser Artikel ist ein sehr interessanter Vergleich zwischen Iterrows und Itertuples


14

Um alle Zeilen in a zu schleifen dataframe, können Sie Folgendes verwenden:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]

1
Dies ist eine verkettete Indizierung. Ich empfehle dies nicht.
CS95

@ cs95 Was würdest du stattdessen empfehlen?
CONvid19

Wenn dies funktionieren soll, rufen Sie df.columns.get_loc auf, um die ganzzahlige Indexposition der Datumsspalte (außerhalb der Schleife) abzurufen, und verwenden Sie dann einen einzelnen iloc-Indexierungsaufruf innerhalb.
CS95

14
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]

1
Wie ist die Leistung dieser Option bei Verwendung in einem großen Datenrahmen (z. B. Millionen von Zeilen)?
Bazyli Debowski

Ehrlich gesagt, ich weiß es nicht genau, ich denke, dass im Vergleich zur besten Antwort die verstrichene Zeit ungefähr gleich sein wird, da beide Fälle "für" -Konstruktionen verwenden. In einigen Fällen kann der Speicher jedoch unterschiedlich sein.
Grag2015

4
Dies ist eine verkettete Indizierung. Verwenden Sie dies nicht!
CS95

7

Manchmal ist ein nützliches Muster:

# Borrowing @KutalmisB df example
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
# The to_dict call results in a list of dicts
# where each row_dict is a dictionary with k:v pairs of columns:value for that row
for row_dict in df.to_dict(orient='records'):
    print(row_dict)

Was in ... resultiert:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}

6

Um alle Zeilen in a zu schleifen dataframeund die Werte jeder Zeile bequem zu verwenden , namedtupleskann in ndarrays konvertiert werden . Zum Beispiel:

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])

Durch die Zeilen iterieren:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

Ergebnisse in:

[ 1.   0.1]
[ 2.   0.2]

Bitte beachten Sie, dass , wenn index=True, der Index als das erste Element des Tupels hinzugefügt wird , die für einige Anwendungen unerwünscht sein kann.


5

Es gibt eine Möglichkeit, Wurfzeilen zu iterieren, während im Gegenzug ein DataFrame und keine Serie abgerufen wird. Ich sehe niemanden, der erwähnt, dass Sie den Index als Liste für die Zeile übergeben können, die als DataFrame zurückgegeben werden soll:

for i in range(len(df)):
    row = df.iloc[[i]]

Beachten Sie die Verwendung von doppelten Klammern. Dies gibt einen DataFrame mit einer einzelnen Zeile zurück.


Dies war sehr hilfreich, um nach dem Sortieren die n-te größte Zeile in einem Datenrahmen zu erhalten. Vielen Dank!
Jason Harrison

3

Zum Anzeigen und Ändern von Werten würde ich verwenden iterrows(). In einer for-Schleife und mithilfe des Tupel-Entpackens (siehe Beispiel :) verwende i, rowich das, rowum nur den Wert anzuzeigen und imit der locMethode zu verwenden, wenn ich Werte ändern möchte. Wie in den vorherigen Antworten angegeben, sollten Sie hier nichts ändern, über das Sie iterieren.

for i, row in df.iterrows():
    df_column_A = df.loc[i, 'A']
    if df_column_A == 'Old_Value':
        df_column_A = 'New_value'  

Hier ist das rowin der Schleife eine Kopie dieser Zeile und keine Ansicht davon. Daher sollten Sie NICHT so etwas schreiben row['A'] = 'New_Value', da dies den DataFrame nicht ändert. Sie können jedoch den DataFrame verwenden iund locangeben, um die Arbeit auszuführen.


2

Ich weiß, dass ich zu spät zur antwortenden Partei komme, aber ich wollte nur die obige Antwort von @ cs95 ergänzen, die meiner Meinung nach die akzeptierte Antwort sein sollte. In seiner Antwort zeigt er, dass die Vektorisierung von Pandas andere Pandas-Methoden zur Berechnung von Datenframes bei weitem übertrifft.

Ich wollte hinzufügen, dass wenn Sie den Datenrahmen zuerst in ein Numpy-Array konvertieren und dann die Vektorisierung verwenden, dieser sogar noch schneller ist als die Pandas-Datenrahmenvektorisierung (und dies schließt die Zeit ein, ihn wieder in eine Datenrahmenserie umzuwandeln).

Wenn Sie dem Benchmark-Code von @ cs95 die folgenden Funktionen hinzufügen, wird dies ziemlich deutlich:

def np_vectorization(df):
    np_arr = df.to_numpy()
    return pd.Series(np_arr[:,0] + np_arr[:,1], index=df.index)

def just_np_vectorization(df):
    np_arr = df.to_numpy()
    return np_arr[:,0] + np_arr[:,1]

Geben Sie hier die Bildbeschreibung ein


1

Sie können auch eine numpyIndizierung durchführen, um die Geschwindigkeit noch weiter zu steigern. Es iteriert nicht wirklich, funktioniert aber für bestimmte Anwendungen viel besser als die Iteration.

subset = row['c1'][0:5]
all = row['c1'][:]

Möglicherweise möchten Sie es auch in ein Array umwandeln. Diese Indizes / Auswahlen sollten sich bereits wie Numpy-Arrays verhalten, aber ich bin auf Probleme gestoßen und musste sie umwandeln

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) #resize every image in an hdf5 file

1

Es gibt so viele Möglichkeiten, die Zeilen im Pandas-Datenrahmen zu durchlaufen. Ein sehr einfacher und intuitiver Weg ist:

df=pd.DataFrame({'A':[1,2,3], 'B':[4,5,6],'C':[7,8,9]})
print(df)
for i in range(df.shape[0]):
    # For printing the second column
    print(df.iloc[i,1])
    # For printing more than one columns
    print(df.iloc[i,[0,2]])

0

In diesem Beispiel wird iloc verwendet, um jede Ziffer im Datenrahmen zu isolieren.

import pandas as pd

 a = [1, 2, 3, 4]
 b = [5, 6, 7, 8]

 mjr = pd.DataFrame({'a':a, 'b':b})

 size = mjr.shape

 for i in range(size[0]):
     for j in range(size[1]):
         print(mjr.iloc[i, j])

0

Einige Bibliotheken (z. B. eine von mir verwendete Java-Interop-Bibliothek) erfordern die gleichzeitige Übergabe von Werten, z. B. beim Streaming von Daten. Um die Streaming-Natur zu replizieren, "streame" ich meine Datenrahmenwerte nacheinander. Ich habe Folgendes geschrieben, was von Zeit zu Zeit nützlich ist.

class DataFrameReader:
  def __init__(self, df):
    self._df = df
    self._row = None
    self._columns = df.columns.tolist()
    self.reset()
    self.row_index = 0

  def __getattr__(self, key):
    return self.__getitem__(key)

  def read(self) -> bool:
    self._row = next(self._iterator, None)
    self.row_index += 1
    return self._row is not None

  def columns(self):
    return self._columns

  def reset(self) -> None:
    self._iterator = self._df.itertuples()

  def get_index(self):
    return self._row[0]

  def index(self):
    return self._row[0]

  def to_dict(self, columns: List[str] = None):
    return self.row(columns=columns)

  def tolist(self, cols) -> List[object]:
    return [self.__getitem__(c) for c in cols]

  def row(self, columns: List[str] = None) -> Dict[str, object]:
    cols = set(self._columns if columns is None else columns)
    return {c : self.__getitem__(c) for c in self._columns if c in cols}

  def __getitem__(self, key) -> object:
    # the df index of the row is at index 0
    try:
        if type(key) is list:
            ix = [self._columns.index(key) + 1 for k in key]
        else:
            ix = self._columns.index(key) + 1
        return self._row[ix]
    except BaseException as e:
        return None

  def __next__(self) -> 'DataFrameReader':
    if self.read():
        return self
    else:
        raise StopIteration

  def __iter__(self) -> 'DataFrameReader':
    return self

Welches kann verwendet werden:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

Und behält die Werte- / Namenszuordnung für die Zeilen bei, die iteriert werden. Offensichtlich ist es viel langsamer als die Verwendung von apply und Cython wie oben angegeben, ist aber unter bestimmten Umständen erforderlich.


0

Zusamenfassend

  • Verwenden Sie nach Möglichkeit die Vektorisierung
  • Wenn die Operation nicht vektorisiert werden kann, verwenden Sie Listenverständnisse
  • Wenn Sie ein einzelnes Objekt benötigen, das die gesamte Zeile darstellt, verwenden Sie itertuples
  • Wenn das oben genannte zu langsam ist, versuchen Sie es mit swifter.apply
  • Wenn es immer noch zu langsam ist, versuchen Sie es mit der Cython-Routine

Details in diesem Video

Benchmark Benchmark der Iteration über Zeilen in einem Pandas DataFrame

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.