395 Stimmen

Was ist der effizienteste Weg, um Schleife durch dataframes mit Pandas?

Ich möchte meine eigenen komplexen Operationen auf Finanzdaten in Datenrahmen in sequenzieller Weise durchführen.

Ich verwende zum Beispiel die folgende CSV-Datei von MSFT Yahoo Finanzen :

Date,Open,High,Low,Close,Volume,Adj Close
2011-10-19,27.37,27.47,27.01,27.13,42880000,27.13
2011-10-18,26.94,27.40,26.80,27.31,52487900,27.31
2011-10-17,27.11,27.42,26.85,26.98,39433400,26.98
2011-10-14,27.31,27.50,27.02,27.27,50947700,27.27

....

Ich gehe dann wie folgt vor:

#!/usr/bin/env python
from pandas import *

df = read_csv('table.csv')

for i, row in enumerate(df.values):
    date = df.index[i]
    open, high, low, close, adjclose = row
    #now perform analysis on open/close based on date, etc..

Ist das der effizienteste Weg? Angesichts der Fokus auf Geschwindigkeit in Pandas, würde ich davon ausgehen, dass es einige spezielle Funktion, um durch die Werte in einer Weise, die man auch den Index abruft (möglicherweise durch einen Generator, um speichereffizient zu sein) zu iterieren sein muss? df.iteritems iteriert leider nur spaltenweise.

422voto

Nick Crawford Punkte 4986

Die neuesten Versionen von Pandas enthalten jetzt eine eingebaute Funktion zur Iteration über Zeilen.

for index, row in df.iterrows():

    # do some logic here

Oder, wenn es schneller gehen soll, verwenden Sie itertuples()

Aber der Vorschlag von unutbu, Numpy-Funktionen zu verwenden, um die Iteration über Zeilen zu vermeiden, wird den schnellsten Code erzeugen.

168voto

unutbu Punkte 769083

Pandas basiert auf NumPy-Arrays. Der Schlüssel zur Geschwindigkeit mit NumPy-Arrays ist es, Ihre Operationen auf dem gesamten Array auf einmal durchzuführen, niemals Zeile für Zeile oder Element für Element.

Zum Beispiel, wenn close ist ein 1-d-Array, und Sie möchten die prozentuale Veränderung von Tag zu Tag,

pct_change = close[1:]/close[:-1]

Dadurch wird die gesamte Reihe der prozentualen Änderungen in einer einzigen Anweisung berechnet, anstelle von

pct_change = []
for row in close:
    pct_change.append(...)

Versuchen Sie also, die Python-Schleife zu vermeiden for i, row in enumerate(...) vollständig, und überlegen Sie, wie Sie Ihre Berechnungen mit Operationen für das gesamte Array (oder den Datenrahmen) als Ganzes und nicht Zeile für Zeile durchführen können.

136voto

Richard Wong Punkte 3228

Wie bereits erwähnt, ist das Pandas-Objekt am effizientesten, wenn es das gesamte Array auf einmal verarbeitet. Für diejenigen, die wie ich eine Schleife durch einen Pandas DataFrame ziehen müssen, um etwas auszuführen, habe ich mindestens drei Möglichkeiten gefunden, dies zu tun. Ich habe einen kurzen Test durchgeführt, um zu sehen, welche der drei Möglichkeiten am wenigsten Zeit in Anspruch nimmt.

t = pd.DataFrame({'a': range(0, 10000), 'b': range(10000, 20000)})
B = []
C = []
A = time.time()
for i,r in t.iterrows():
    C.append((r['a'], r['b']))
B.append(time.time()-A)

C = []
A = time.time()
for ir in t.itertuples():
    C.append((ir[1], ir[2]))    
B.append(time.time()-A)

C = []
A = time.time()
for r in zip(t['a'], t['b']):
    C.append((r[0], r[1]))
B.append(time.time()-A)

print B

Ergebnis:

[0.5639059543609619, 0.017839908599853516, 0.005645036697387695]

Das ist wahrscheinlich nicht die beste Methode, um den Zeitaufwand zu messen, aber für mich ist es schnell.

Hier sind einige Vor- und Nachteile IMHO:

  • .iterrows(): gibt Index- und Zeilenelemente in separaten Variablen zurück, ist aber deutlich langsamer
  • .itertuples(): schneller als .iterrows(), aber Rückgabe des Index zusammen mit den Zeilenelementen, ir[0] ist der Index
  • zip: am schnellsten, aber kein Zugriff auf den Index der Zeile

BEARBEITEN 2020/11/10

Für was es wert ist, hier ist eine aktualisierte Benchmark mit einigen anderen Alternativen (perf mit MacBookPro 2,4 GHz Intel Core i9 8 Kerne 32 Go 2667 MHz DDR4)

import sys
import tqdm
import time
import pandas as pd

B = []
t = pd.DataFrame({'a': range(0, 10000), 'b': range(10000, 20000)})
for _ in tqdm.tqdm(range(10)):
    C = []
    A = time.time()
    for i,r in t.iterrows():
        C.append((r['a'], r['b']))
    B.append({"method": "iterrows", "time": time.time()-A})

    C = []
    A = time.time()
    for ir in t.itertuples():
        C.append((ir[1], ir[2]))
    B.append({"method": "itertuples", "time": time.time()-A})

    C = []
    A = time.time()
    for r in zip(t['a'], t['b']):
        C.append((r[0], r[1]))
    B.append({"method": "zip", "time": time.time()-A})

    C = []
    A = time.time()
    for r in zip(*t.to_dict("list").values()):
        C.append((r[0], r[1]))
    B.append({"method": "zip + to_dict('list')", "time": time.time()-A})

    C = []
    A = time.time()
    for r in t.to_dict("records"):
        C.append((r["a"], r["b"]))
    B.append({"method": "to_dict('records')", "time": time.time()-A})

    A = time.time()
    t.agg(tuple, axis=1).tolist()
    B.append({"method": "agg", "time": time.time()-A})

    A = time.time()
    t.apply(tuple, axis=1).tolist()
    B.append({"method": "apply", "time": time.time()-A})

print(f'Python {sys.version} on {sys.platform}')
print(f"Pandas version {pd.__version__}")
print(
    pd.DataFrame(B).groupby("method").agg(["mean", "std"]).xs("time", axis=1).sort_values("mean")
)

## Output

Python 3.7.9 (default, Oct 13 2020, 10:58:24) 
[Clang 12.0.0 (clang-1200.0.32.2)] on darwin
Pandas version 1.1.4
                           mean       std
method                                   
zip + to_dict('list')  0.002353  0.000168
zip                    0.003381  0.000250
itertuples             0.007659  0.000728
to_dict('records')     0.025838  0.001458
agg                    0.066391  0.007044
apply                  0.067753  0.006997
iterrows               0.647215  0.019600

75voto

Wes McKinney Punkte 94041

Sie können die Zeilen in einer Schleife durchlaufen, indem Sie transponieren und dann iteritems aufrufen:

for date, row in df.T.iteritems():
   # do some logic here

In diesem Fall bin ich mir über die Effizienz nicht sicher. Um die bestmögliche Leistung in einem iterativen Algorithmus zu erhalten, sollten Sie vielleicht versuchen, ihn in Cython Sie könnten also etwa so vorgehen:

def my_algo(ndarray[object] dates, ndarray[float64_t] open,
            ndarray[float64_t] low, ndarray[float64_t] high,
            ndarray[float64_t] close, ndarray[float64_t] volume):
    cdef:
        Py_ssize_t i, n
        float64_t foo
    n = len(dates)

    for i from 0 <= i < n:
        foo = close[i] - open[i] # will be extremely fast

Ich würde empfehlen, den Algorithmus zuerst in reinem Python zu schreiben, sicherzustellen, dass er funktioniert und zu sehen, wie schnell er ist - wenn er nicht schnell genug ist, konvertieren Sie die Dinge in Cython wie diese mit minimaler Arbeit, um etwas zu erhalten, das ungefähr so schnell ist wie handcodiertes C/C++.

62voto

Fifi Punkte 3033

Sie haben drei Möglichkeiten:

Unter indice (am einfachsten):

>>> for index in df.index:
...     print ("df[" + str(index) + "]['B']=" + str(df['B'][index]))

Mit iterrows (meist verwendet):

>>> for index, row in df.iterrows():
...     print ("df[" + str(index) + "]['B']=" + str(row['B']))

Mit itertuples (am schnellsten):

>>> for row in df.itertuples():
...     print ("df[" + str(row.Index) + "]['B']=" + str(row.B))

Drei Optionen zeigen etwas an wie:

df[0]['B']=125
df[1]['B']=415
df[2]['B']=23
df[3]['B']=456
df[4]['B']=189
df[5]['B']=456
df[6]['B']=12

Quelle: alphons.io

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X