2014-02-28 27 views
37

Pandas ist wirklich großartig, aber ich bin wirklich überrascht, wie ineffizient es ist, Werte von einem Pandas.DataFrame abzurufen. Im folgenden Spielzeugbeispiel ist sogar die Methode DataFrame.iloc mehr als 100 Mal langsamer als ein Wörterbuch.Pandas DataFrame Leistung

Die Frage: Ist die Lektion hier nur, dass Wörterbücher die bessere Möglichkeit sind, Werte nachzuschlagen? Ja, ich verstehe, dass sie genau dafür geschaffen sind. Aber ich frage mich nur, ob mir etwas an DataFrame-Lookup-Leistung fehlt.

Mir ist klar, dass diese Frage eher "musing" als "fragend" ist, aber ich werde eine Antwort akzeptieren, die Einblick oder Perspektive dazu bietet. Vielen Dank.

import timeit 

setup = ''' 
import numpy, pandas 
df = pandas.DataFrame(numpy.zeros(shape=[10, 10])) 
dictionary = df.to_dict() 
''' 

f = ['value = dictionary[5][5]', 'value = df.loc[5, 5]', 'value = df.iloc[5, 5]'] 

for func in f: 
    print func 
    print min(timeit.Timer(func, setup).repeat(3, 100000)) 

value = Wörterbuch [5] [5]

0,130625009537

Wert = df.loc [5, 5]

19,4681699276

Wert = df. Iloc [5, 5]

17.2575249672

Antwort

54

Ein Diktat ist zu einem DataFrame, wie ein Fahrrad zu einem Auto ist. Sie können 10 Fuß auf einem Fahrrad schneller treten, als Sie ein Auto starten können, es in Gang bringen, usw., usw. Aber wenn Sie eine Meile gehen müssen, gewinnt das Auto.

Für bestimmte kleine, gezielte Zwecke kann ein Diktat schneller sein. Und wenn das alles ist, was Sie brauchen, dann verwenden Sie ein Diktat, sicher! Aber wenn Sie die Kraft und den Luxus eines DataFrame brauchen/wollen, dann ist ein Diktat kein Ersatz. Es ist sinnlos, die Geschwindigkeit zu vergleichen, wenn die Datenstruktur nicht Ihren Bedürfnissen entspricht.

Jetzt zum Beispiel - um konkreter zu sein - ein Diktat ist gut für den Zugriff auf Spalten, aber es ist nicht so bequem für den Zugriff auf Zeilen.

import timeit 

setup = ''' 
import numpy, pandas 
df = pandas.DataFrame(numpy.zeros(shape=[10, 1000])) 
dictionary = df.to_dict() 
''' 

# f = ['value = dictionary[5][5]', 'value = df.loc[5, 5]', 'value = df.iloc[5, 5]'] 
f = ['value = [val[5] for col,val in dictionary.items()]', 'value = df.loc[5]', 'value = df.iloc[5]'] 

for func in f: 
    print(func) 
    print(min(timeit.Timer(func, setup).repeat(3, 100000))) 

ergibt

value = [val[5] for col,val in dictionary.iteritems()] 
25.5416321754 
value = df.loc[5] 
5.68071913719 
value = df.iloc[5] 
4.56006002426 

So ist die dict von Listen ist 5-mal langsamer in den Zeilen als df.iloc abgerufen werden. Das Geschwindigkeitsdefizit wird größer, wenn die Anzahl der Spalten zunimmt. (Die Anzahl der Spalten ist wie die Anzahl der Füße in der Fahrradanalogie. Je länger die Entfernung, desto bequemer wird das Auto ...)

Dies ist nur ein Beispiel, wenn ein Diktat von Listen weniger bequem wäre/langsamer als ein DataFrame.

Ein anderes Beispiel wäre, wenn Sie einen DatetimeIndex für die Zeilen haben und alle Zeilen zwischen bestimmten Daten auswählen möchten. Mit einem DataFrame können Sie verwenden

df.loc['2000-1-1':'2000-3-31'] 

Es gibt kein einfaches Analog für das, wenn Sie ein Diktat von Listen verwenden sollten. Und die Python-Schleifen, die Sie für die Auswahl der richtigen Zeilen verwenden müssten, wären im Vergleich zum DataFrame erneut schrecklich langsam.

+0

Antworten wie diese vielleicht zu FAQ hinzufügen, finden Sie hier: https://github.com/pydata/pandas/issues/3871 – Jeff

+0

Danke für die beiden wirklich leuchtenden Beispiele, und auch für die Analogie, die ich als Radfahrer schätze. – Owen

2

Ich stieß auf das gleiche Problem. Sie können verwenden, um zu verbessern.

"Da die Indizierung mit [] eine Vielzahl von Fällen bewältigen muss (Einzelzugriff, Slicing, boolesche Indizierung usw.), hat es einen gewissen Overhead, um herauszufinden, wonach Sie fragen Wenn Sie nur auf einen Skalarwert zugreifen möchten, verwenden Sie am schnellsten die Methoden und iat, die in allen Datenstrukturen implementiert sind. "

siehe offizieller Referenz http://pandas.pydata.org/pandas-docs/stable/indexing.html Kapitel „Schnelle skalare Wert bekommen und Einstellung“

+0

Dies ist eine gute Referenz, aber nicht so detailliert wie die obige Antwort. – BCR

12

+1 für die Verwendung von at oder iat für skalare Operationen. Beispiel Benchmark:

In [1]: import numpy, pandas 
    ...: df = pandas.DataFrame(numpy.zeros(shape=[10, 10])) 
    ...: dictionary = df.to_dict() 

In [2]: %timeit value = dictionary[5][5] 
The slowest run took 34.06 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 310 ns per loop 

In [4]: %timeit value = df.loc[5, 5] 
10000 loops, best of 3: 104 µs per loop 

In [5]: %timeit value = df.iloc[5, 5] 
10000 loops, best of 3: 98.8 µs per loop 

In [6]: %timeit value = df.iat[5, 5] 
The slowest run took 6.67 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 9.58 µs per loop 

In [7]: %timeit value = df.at[5, 5] 
The slowest run took 6.59 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 9.26 µs per loop 

Es scheint mit at (iat) ist etwa 10-mal schneller als loc (iloc).

0

Ich erlebte verschiedene Phänomene beim Zugriff auf die Datenframe Zeile. Testen Sie dieses einfache Beispiel auf Datenrahmen etwa 10.000.000 Zeilen. Wörterbuch rockt.

def testRow(go): 
    go_dict = go.to_dict() 
    times = 100000 
    ot= time.time() 
    for i in range(times): 
     go.iloc[100,:] 
    nt = time.time() 
    print('for iloc {}'.format(nt-ot)) 
    ot= time.time() 
    for i in range(times): 
     go.loc[100,2] 
    nt = time.time() 
    print('for loc {}'.format(nt-ot)) 
    ot= time.time() 
    for i in range(times): 
     [val[100] for col,val in go_dict.iteritems()] 
    nt = time.time() 
    print('for dict {}'.format(nt-ot)) 
0

Ich denke, die schnellste Weg, um eine Zelle für den Zugriff ist

df.get_value(row,column) 
df.set_value(row,column,value) 

Beide sind schneller als (ich glaube)

df.iat(...) 
df.at(...)