2014-12-22 9 views
8

Versuchen Sie, diese für sich selbst:Warum ist DataFrame.loc [[1]] 1.800x langsamer als df.ix [[1]] und 3.500x als df.loc [1]?

import pandas as pd 
s=pd.Series(xrange(5000000)) 
%timeit s.loc[[0]] # You need pandas 0.15.1 or newer for it to be that slow 
1 loops, best of 3: 445 ms per loop 

aktualisieren: die a legitimate bug in pandas ist, die wahrscheinlich in 0.15.1 im August 2014 oder so eingeführt wurde. Workarounds: Warte auf eine neue Version, während du eine alte Version von Pandas verwendest; Holen Sie sich einen topaktuellen Entwickler. Version von GitHub; manuell eine einzeilige Änderung in Ihrer Version pandas; vorübergehend .ix anstelle von .loc verwenden.

Ich habe einen Datenrahmen mit 4,8 Millionen Zeilen und eine einzelne Zeile auswählen .iloc[[ id ]] Verwendung (mit einem Einzelelement-Liste) dauert 489 ms, fast eine halbe Sekunde, 1,800x-mal langsamer als die identische .ix[[ id ]] und 3.500x mal langsamer als.iloc[id] (die ID als Wert übergeben, nicht als Liste). Um fair zu sein, .loc[list] dauert etwa die gleiche Zeit unabhängig von der Länge der Liste, aber ich möchte nicht verbringen 489 ms darauf, vor allem, wenn .ix ist tausendmal schneller, und produziert identische Ergebnis. Es war mein Verständnis, dass .ix langsamer sein sollte, oder?

Ich benutze Pandas 0.15.1. Das ausgezeichnete Tutorial auf Indexing and Selecting Data schlägt vor, dass .ix ist irgendwie allgemeiner und vermutlich langsamer als .loc und .iloc. Konkret heißt das:

Wenn jedoch eine Achse integer ist, wird NUR Label-basierter Zugriff und nicht positionsabhängiger Zugriff unterstützt. In solchen Fällen ist es normalerweise besser, explizit zu sein und .iloc oder .loc zu verwenden.

Hier eine ipython Sitzung mit dem Benchmarks:

print 'The dataframe has %d entries, indexed by integers that are less than %d' % (len(df), max(df.index)+1) 
    print 'df.index begins with ', df.index[:20] 
    print 'The index is sorted:', df.index.tolist()==sorted(df.index.tolist()) 

    # First extract one element directly. Expected result, no issues here. 
    id=5965356 
    print 'Extract one element with id %d' % id 
    %timeit df.loc[id] 
    %timeit df.ix[id] 
    print hash(str(df.loc[id])) == hash(str(df.ix[id])) # check we get the same result 

    # Now extract this one element as a list. 
    %timeit df.loc[[id]] # SO SLOW. 489 ms vs 270 microseconds for .ix, or 139 microseconds for .loc[id] 
    %timeit df.ix[[id]] 
    print hash(str(df.loc[[id]])) == hash(str(df.ix[[id]])) # this one should be True 
    # Let's double-check that in this case .ix is the same as .loc, not .iloc, 
    # as this would explain the difference. 
    try: 
     print hash(str(df.iloc[[id]])) == hash(str(df.ix[[id]])) 
    except: 
     print 'Indeed, %d is not even a valid iloc[] value, as there are only %d rows' % (id, len(df)) 

    # Finally, for the sake of completeness, let's take a look at iloc 
    %timeit df.iloc[3456789] # this is still 100+ times faster than the next version 
    %timeit df.iloc[[3456789]] 

Ausgang:

The dataframe has 4826616 entries, indexed by integers that are less than 6177817 
df.index begins with Int64Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], dtype='int64') 
The index is sorted: True 
Extract one element with id 5965356 
10000 loops, best of 3: 139 µs per loop 
10000 loops, best of 3: 141 µs per loop 
True 
1 loops, best of 3: 489 ms per loop 
1000 loops, best of 3: 270 µs per loop 
True 
Indeed, 5965356 is not even a valid iloc[] value, as there are only 4826616 rows 
10000 loops, best of 3: 98.9 µs per loop 
100 loops, best of 3: 12 ms per loop 
+0

Hinweis numpy geschaltet, dass '[[id]]' und '[id] mit' nicht gleichwertig sind. '[id]' gibt eine Serie zurück, aber '[[id]]' gibt einen einreihigen DataFrame zurück. – BrenBarn

+0

@BrenBarn, ja, das erklärt den Unterschied für die '.ix': 141 μs vs. 270 μs. Aber warum ist '.loc [[id]]' so langsam? – osa

Antwort

6

Sieht aus wie das Problem 0.14 in Pandas nicht vorhanden war. Ich profilierte es mit line_profiler, und ich denke, ich weiß, was passiert ist. Seit Pandas 0.15.1 wird jetzt ein KeyError ausgelöst, wenn ein bestimmter Index nicht vorhanden ist. Wenn Sie die .loc[list]-Syntax verwenden, wird eine umfassende Suche nach einem Index entlang der gesamten Achse durchgeführt, selbst wenn er gefunden wurde. Das heißt, zuerst gibt es keine vorzeitige Beendigung, falls ein Element gefunden wird, und zweitens ist die Suche in diesem Fall Brute-Force.

File: .../anaconda/lib/python2.7/site-packages/pandas/core/indexing.py,

1278              # require at least 1 element in the index 
    1279   1   241 241.0  0.1    idx = _ensure_index(key) 
    1280   1  391040 391040.0  99.9    if len(idx) and not idx.isin(ax).any(): 
    1281           
    1282               raise KeyError("None of [%s] are in the [%s]" % 
4

Pandas Indizierung war verrückt langsam, ich Indizierung

df=pd.DataFrame(some_content) 
# takes forever!! 
for iPer in np.arange(-df.shape[0],0,1): 
    x = df.iloc[iPer,:].values 
    y = df.iloc[-1,:].values 
# fast!   
vals = np.matrix(df.values) 
for iPer in np.arange(-vals.shape[0],0,1): 
    x = vals[iPer,:] 
    y = vals[-1,:] 
Verwandte Themen