2013-05-05 3 views
9

Ich muss oft Pandas Dataframe df von df[df['col_name']=='string_value'] zu filtern, und ich möchte die Zeilenauswahl Operation beschleunigen, gibt es einen schnellen Weg, das zu tun?Wie Pandas Zeilenfilterung durch String-Matching zu beschleunigen?

Zum Beispiel

In [1]: df = mul_df(3000,2000,3).reset_index() 

In [2]: timeit df[df['STK_ID']=='A0003'] 
1 loops, best of 3: 1.52 s per loop 

1.52s verkürzen werden kann?

Hinweis:

mul_df() ist Funktion Multi-Level-Datenrahmen zu erstellen:

>>> mul_df(4,2,3) 
       COL000 COL001 COL002 
STK_ID RPT_Date       
A0000 B000  0.6399 0.0062 1.0022 
     B001  -0.2881 -2.0604 1.2481 
A0001 B000  0.7070 -0.9539 -0.5268 
     B001  0.8860 -0.5367 -2.4492 
A0002 B000  -2.4738 0.9529 -0.9789 
     B001  0.1392 -1.0931 -0.2077 
A0003 B000  -1.1377 0.5455 -0.2290 
     B001  1.0083 0.2746 -0.3934 

Unten ist der Code von mul_df():

import itertools 
import numpy as np 
import pandas as pd 

def mul_df(level1_rownum, level2_rownum, col_num, data_ty='float32'): 
    ''' create multilevel dataframe, for example: mul_df(4,2,6)''' 

    index_name = ['STK_ID','RPT_Date'] 
    col_name = ['COL'+str(x).zfill(3) for x in range(col_num)] 

    first_level_dt = [['A'+str(x).zfill(4)]*level2_rownum for x in range(level1_rownum)] 
    first_level_dt = list(itertools.chain(*first_level_dt)) #flatten the list 
    second_level_dt = ['B'+str(x).zfill(3) for x in range(level2_rownum)]*level1_rownum 

    dt = pd.DataFrame(np.random.randn(level1_rownum*level2_rownum, col_num), columns=col_name, dtype = data_ty) 
    dt[index_name[0]] = first_level_dt 
    dt[index_name[1]] = second_level_dt 

    rst = dt.set_index(index_name, drop=True, inplace=False) 
    return rst 
+3

Warum nicht als Index beibehalten (oder wenn es sich nicht um einen Index handelt, als Index festlegen)? Die Auswahl auf dem Index ist viel schneller ('df.ix ['A0003']'): für mich 194 us vs 646 ms. – joris

+0

Das ist nur ein Beispiel. Oft müssen wir nach Spalte anstatt nach Index wählen; es als einen Index zu setzen ist eine Option, aber es führt zu einem anderen Problem (Index existiert bereits, ändern Datenstruktur usw.) (Wenn wir Datenframe als Datenbanktabelle behandeln, ist es sehr üblich: SELECT * FROM Tabellenname WHERE Col_Name = 'String_Wert ', nicht wahr?) – bigbug

Antwort

12

Ich wollte schon seit langem binäre Suchindizes zu DataFrame-Objekten hinzufügen. Sie können den DIY-Ansatz nehmen durch die Spalte zu sortieren und diese selbst zu tun:

In [11]: df = df.sort('STK_ID') # skip this if you're sure it's sorted 

In [12]: df['STK_ID'].searchsorted('A0003', 'left') 
Out[12]: 6000 

In [13]: df['STK_ID'].searchsorted('A0003', 'right') 
Out[13]: 8000 

In [14]: timeit df[6000:8000] 
10000 loops, best of 3: 134 µs per loop 

Dies ist schnell, weil sie Ansichten immer abruft und keine Daten kopieren.

+0

Iuitive Lösung, danke. Hoffentlich kann es in Pandas Zeile Auswahl Sytax wie "df [df ['col_name'] == 'string_value']" – bigbug

+1

Ich wünschte, dies wurde irgendwo in den Dokumenten erwähnt, würde viel Zeit gespeichert ... – user2808117

+0

Die 'sort' Methode ist in Pandas 19.2 und höher veraltet. Verwenden Sie stattdessen die Methode 'sort_values'. – Jeff

3

Etwas überraschend, mit dem .values Arbeits Array anstelle der Series ist viel schneller für mich:

>>> time df = mul_df(3000, 2000, 3).reset_index() 
CPU times: user 5.96 s, sys: 0.81 s, total: 6.78 s 
Wall time: 6.78 s 
>>> timeit df[df["STK_ID"] == "A0003"] 
1 loops, best of 3: 841 ms per loop 
>>> timeit df[df["STK_ID"].values == "A0003"] 
1 loops, best of 3: 210 ms per loop 
3

Je nachdem, was Sie bei der Auswahl tun, wollten danach, und wenn Sie mehrere Auswahlen dieser Art machen müssen, die groupby Funktionalität kann auch die Dinge schneller (zumindest mit dem Beispiel).

Auch wenn Sie nur die Zeilen für einen string_value wählen müssen, ist es ein bisschen schneller (aber nicht viel):

In [11]: %timeit df[df['STK_ID']=='A0003'] 
1 loops, best of 3: 626 ms per loop 

In [12]: %timeit df.groupby("STK_ID").get_group("A0003") 
1 loops, best of 3: 459 ms per loop 

aber nachfolgende Aufrufe der GroupBy Objekt wird sehr schnell sein (zB wähle die Zeilen anderer sting_values):

In [25]: grouped = df.groupby("STK_ID") 

In [26]: %timeit grouped.get_group("A0003") 
1 loops, best of 3: 333 us per loop 
Verwandte Themen