2015-03-06 9 views
8

Ich versuche herauszufinden, wann verschiedene Auswahlmethoden in Pandas DataFrame verwendet werden. Insbesondere suche ich nach skalaren Werten. Ich höre oft ix wird allgemein empfohlen. Aber in pandas documentation es wird empfohlen, at und iat für schnelle Skalarwert zugreifenden zu verwenden:Pandas skalaren Wert bekommen und einstellen: ix oder iat?

Since indexing with [] must handle a lot of cases (single-label access, slicing, boolean indexing, etc.), it has a bit of overhead in order to figure out what you’re asking for. If you only want to access a scalar value, the fastest way is to use the bei and iat methods, which are implemented on all of the data structures.

Also, ich würde iat annehmen sollte schneller sein für immer und einzelne Zellen zu setzen. Nach einigen Tests haben wir jedoch festgestellt, dass ix beim Lesen von Zellen vergleichbar oder schneller sein würde, während iat beim Zuweisen von Werten zu Zellen viel schneller ist.

Ist dieses Verhalten überall dokumentiert? Ist es immer so und warum passiert das? Muss es etwas mit der Rückgabe oder Kopie tun? Ich würde mich freuen, wenn jemand diese Frage beleuchten und erklären könnte, was empfohlen wird, um Zellwerte zu bekommen und festzulegen und warum.

Hier sind einige Tests mit Pandas (Version 0.15.2).

Nur um sicherzustellen, dass dieses Verhalten kein Fehler dieser Version ist, habe ich es auch auf 0.11.0 getestet. Ich gebe die Ergebnisse nicht, aber der Trend ist genau der gleiche - ix being much faster for getting, and iat for setting individual cells.

import pandas as pd 
import numpy as np 

df = pd.DataFrame(np.random.rand(1000,2),columns = ['A','B']) 
idx = 0 

timeit for i in range(1000): df.ix[i,'A'] = 1 
timeit for i in range(1000): df.iat[i,idx] = 2 

>> 10 loops, best of 3: 92.6 ms per loop 
>> 10 loops, best of 3: 21.7 ms per loop 

timeit for i in range(1000): tmp = df.ix[i,'A'] 
timeit for i in range(1000): tmp = df.iat[i,idx] 

>> 100 loops, best of 3: 5.31 ms per loop 
>> 10 loops, best of 3: 19.4 ms per loop 
+0

Ich denke, die get_value() und set_value() ist schneller – HYRY

+0

Interessant, um die relativen Unterschiede zu beachten. Mit Pandas 0.14.1 erhalte ich die folgenden Ergebnisse: 1) 10 Schleifen, das Beste aus 3: 108 ms pro Schleife 2) 100 Schleifen, das Beste aus 3: 12,1 ms pro Schleife 3) 100 Schleifen, das Beste aus 3: 5,55 ms pro Schleife 4) 100 Schleifen, best of 3: 9,43 ms pro Schleife – Alexander

+0

Sieht aus wie iat zum Erhalten hat sich in den letzten Versionen erheblich verbessert. Mit Pandas 0.19.1 sehe ich immer noch ix schneller als iat für die letzten 2 Timings, aber gerade noch. Die obigen Ergebnisse sind 5,31 vs 19,4 (in Version 0.15.2), aber ich sehe 6.01 vs 7.16 (in Version 0.19.1). – JohnE

Antwort

11

Pandas hat einige ziemlich interessante Dinge mit dem indexing classes. Ich glaube nicht, dass ich in der Lage bin, einen einfachen Weg zu beschreiben, den ich verwenden könnte, aber ich kann einige Einblicke in die Implementierung geben.

DataFrame#ix ist ein _IXIndexer, die nicht über ein eigenes __getitem__ oder __setitem__ erklären. Diese beiden Methoden sind wichtig, da sie steuern, wie auf Werte mit Pandas zugegriffen wird. Da _IXIndexer diese Methoden nicht deklariert, werden die super class _NDFrameIndexer stattdessen verwendet.

Weitere Graben auf der _NDFrameIndexer 's __getitem__ zeigt, dass es relativ einfach ist und in einigen Fällen die Logik umschließt, die in get_value gefunden wird. Dann ist __getitem__ für einige Szenarien so schnell wie get_value.

_NDFrameIndexer 's __setitem__ ist eine andere Geschichte. Zunächst sieht es einfach aus, aber die zweite Methode, die es aufruft, ist _setitem_with_indexer, die für die meisten Szenarien eine beträchtliche Menge Arbeit leistet.

Diese Daten lassen darauf schließen, dass die Werte Anrufe erhalten mit ix werden von get_value im besten Fall begrenzt und ruft Werte setzen ix wäre einen Kern Committer nehmen zu erklären. Jetzt

für DataFrame#iat welches ein _iAtIndexer die auch nicht seine eigene __getitem__ oder __setitem__ dafür fallen in seine Superklasse nicht erklären zurück _ScalarAccessIndexer ‚s Umsetzung.

_ScalarAccessIndexer hat eine simple __getitem__ Implementierung, aber es erfordert eine Schleife, um den Schlüssel in das richtige Format zu konvertieren. Die zusätzliche Schleife fügt vor dem Aufruf von get_value zusätzliche Verarbeitungszeit hinzu.

_ScalarAccessIndexer hat auch eine ziemlich simple __setitem__ Implementierung, die den Schlüssel konvertiert die Parameter set_value erfordert, bevor Sie den Wert festlegen.

Diese Information legt nahe, dass Aufrufe zum Abrufen von Werten unter Verwendung von iat durch get_value sowie for loop begrenzt sind. Einstellwerte mit iat werden hauptsächlich durch Aufrufe an set_value begrenzt. Werte mit iat zu erhalten, hat also einen gewissen Overhead, während das Setzen von diesen einen geringeren Overhead hat.

TL; DR

Ich glaube, Sie die richtige Accessor verwenden für einen Int64Index Index für die Dokumentation basiert, aber ich glaube nicht, das bedeutet, dass es die am schnellsten ist. Die beste Leistung kann unter Verwendung von get_value und set_value direkt gefunden werden, aber sie erfordern eine zusätzliche Tiefe an Wissen, wie Pandas DataFrames implementiert werden.

Hinweise

Es ist erwähnenswert, dass die Dokumentation auf Pandas erwähnt, dass get_value und set_value sind veraltet, die ich glaube, gemeint war iget_value anstatt zu sein.

Beispiele

Um den Unterschied in der Leistung mit wenigen Indexer zu zeigen (einschließlich get_value und set_value direkt aufrufen) Ich habe dieses Skript:

example.py:

import timeit 


def print_index_speed(stmnt_name, stmnt): 
    """ 
    Repeatedly run the statement provided then repeat the process and take the 
    minimum execution time. 
    """ 
    setup = """ 
import pandas as pd 
import numpy as np 

df = pd.DataFrame(np.random.rand(1000,2),columns = ['A','B']) 
idx = 0 
    """ 

    minimum_execution_time = min(
     timeit.Timer(stmnt, setup=setup).repeat(5, 10)) 

    print("{stmnt_name}: {time}".format(
     stmnt_name=stmnt_name, 
     time=round(minimum_execution_time, 5))) 

print_index_speed("set ix", "for i in range(1000): df.ix[i, 'A'] = 1") 
print_index_speed("set at", "for i in range(1000): df.at[i, 'A'] = 2") 
print_index_speed("set iat", "for i in range(1000): df.iat[i, idx] = 3") 
print_index_speed("set loc", "for i in range(1000): df.loc[i, 'A'] = 4") 
print_index_speed("set iloc", "for i in range(1000): df.iloc[i, idx] = 5") 
print_index_speed(
    "set_value scalar", 
    "for i in range(1000): df.set_value(i, idx, 6, True)") 
print_index_speed(
    "set_value label", 
    "for i in range(1000): df.set_value(i, 'A', 7, False)") 

print_index_speed("get ix", "for i in range(1000): tmp = df.ix[i, 'A']") 
print_index_speed("get at", "for i in range(1000): tmp = df.at[i, 'A']") 
print_index_speed("get iat", "for i in range(1000): tmp = df.iat[i, idx]") 
print_index_speed("get loc", "for i in range(1000): tmp = df.loc[i, 'A']") 
print_index_speed("get iloc", "for i in range(1000): tmp = df.iloc[i, idx]") 
print_index_speed(
    "get_value scalar", 
    "for i in range(1000): tmp = df.get_value(i, idx, True)") 
print_index_speed(
    "get_value label", 
    "for i in range(1000): tmp = df.get_value(i, 'A', False)") 

Ausgang :

set ix: 0.9918 
set at: 0.06801 
set iat: 0.08606 
set loc: 1.04173 
set iloc: 1.0021 
set_value: 0.0452 
**set_value**: 0.03516 
get ix: 0.04827 
get at: 0.06889 
get iat: 0.07813 
get loc: 0.8966 
get iloc: 0.87484 
get_value: 0.04994 
**get_value**: 0.03111 
+0

Vielen Dank für das Einarbeiten in die Implementierungsdetails! Sehr interessante Info! Ja, scheint wie set_value und get_value sind die besten Performer. Sie haben Recht, Dokumentation ist ein wenig verwirrend, ob sie abgeschrieben werden oder nicht. In der aktuellen Version der Pandas-Version (0.15.1) wird nicht explizit darauf hingewiesen, dass sie veraltet ist, und auch nicht in diesem Dokument zu allen API-Änderungen: http://pandas.pydata.org/pandas-docs/dev/whatsnew.html#api -changes Es erwähnt nur ausdrücklich über iget_value. – ojy

Verwandte Themen