NumPy Weg
Hier ist eine vektorisiert NumPy Weise mit advanced indexing
-
# Extract array data
In [10]: a = df.values
# Get integer based column IDs
In [11]: col_idx = np.searchsorted(df.columns, columns_to_select)
# Use NumPy's advanced indexing to extract relevant elem per row
In [12]: a[np.arange(len(col_idx)), col_idx]
Out[12]: array([ 10, 2, 3, 400])
Wenn Spaltennamen von df
nicht sortiert sind, wir sorter
Argument mit np.searchsorted
verwenden müssen. Der Code col_idx
für eine solche allgemeine df
zu extrahieren wäre:
# https://stackoverflow.com/a/38489403/ @Divakar
def column_index(df, query_cols):
cols = df.columns.values
sidx = np.argsort(cols)
return sidx[np.searchsorted(cols,query_cols,sorter=sidx)]
So col_idx
wie so erhalten würde -
Profilierungs enthüllt
col_idx = column_index(df, columns_to_select)
Eine weitere Optimierung, dass der Engpass war die Bearbeitung von Strings mit np.searchsorted
, die übliche NumPy-Schwäche, mit Streichern nicht so gut zu sein. Um dies zu umgehen und das Sonderfall-Szenario zu verwenden, bei dem Spaltennamen aus einzelnen Buchstaben bestehen, könnten wir diese schnell in Zahlen umwandeln und sie dann zur schnelleren Verarbeitung an searchsorted
weiterleiten.
Somit ist eine optimierte Version der Integer-basierte Spalten-IDs erhalten, für den Fall, dass die Spaltennamen einzelne Buchstaben und sortiert sind, wäre -
def column_index_singlechar_sorted(df, query_cols):
c0 = np.fromstring(''.join(df.columns), dtype=np.uint8)
c1 = np.fromstring(''.join(query_cols), dtype=np.uint8)
return np.searchsorted(c0, c1)
Dies gibt uns eine modifizierte Version der Lösung wie so -
Timings -
In [149]: # Setup df with 26 uppercase column letters and many rows
...: import string
...: df = pd.DataFrame(np.random.randint(0,9,(1000000,26)))
...: s = list(string.uppercase[:df.shape[1]])
...: df.columns = s
...: idx = np.random.randint(0,df.shape[1],len(df))
...: columns_to_select = np.take(s, idx).tolist()
# With df.lookup from @MaxU's soln
In [150]: %timeit pd.Series(df.lookup(df.index, columns_to_select))
10 loops, best of 3: 76.7 ms per loop
# With proposed one from this soln
In [151]: %%timeit
...: a = df.values
...: col_idx = column_index_singlechar_sorted(df, columns_to_select)
...: out = pd.Series(a[np.arange(len(col_idx)), col_idx])
10 loops, best of 3: 59 ms per loop
Da df.lookup
löst für einen generischen Fall, das ist wahrscheinlich eine bessere Wahl, aber die anderen möglichen Optimierungen, wie in diesem Beitrag gezeigt, könnte auch nützlich sein!
eine Sekunde hinter sich. ;-) – Wen
@Wen, yeah, ich kenne dieses Gefühl - sorry :) – MaxU
@MaxU Das ist genau das, was ich gesucht habe. Vielen Dank! –