NumPy sposób
Oto wektorowy sposób NumPy użyciu 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])
Jeśli nazwy kolumn df
nie są sortowane, musimy użyć sorter
argumentu z np.searchsorted
. Kod wyodrębnić col_idx
do takiej ogólnej df
byłoby:
# 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)]
Więc col_idx
można by uzyskać jak tak -
col_idx = column_index(df, columns_to_select)
Dalsza optymalizacja
Profilowanie to ujawniło, że wąskim gardłem przetwarzał ciągi znaków o numerze np.searchsorted
, co jest typową słabością NumPy, ponieważ nie jest tak dobre w przypadku łańcuchów. Aby przezwyciężyć to i wykorzystując scenariusz szczególnego przypadku nazw kolumn składających się z pojedynczych liter, moglibyśmy szybko przekonwertować te wartości na liczby, a następnie przekazać je do searchsorted
w celu znacznie szybszego przetwarzania.
Zatem zoptymalizowana wersja coraz identyfikatory kolumn całkowitych oparte na przypadku, gdy nazwy kolumn są pojedyncze litery i sortowane, byłoby -
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)
To daje nam zmodyfikowaną wersję rozwiązania jak tak -
Szybkość wczytywania -
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
Zważywszy, że df.lookup
rozwiązuje dla ogólnego przypadku, to prawdopodobnie lepszy wybór, ale inne możliwe optymalizacje, jak pokazano w tym poście, mogą być również przydatne!
jeden s za sobą. ;-) – Wen
@Wen, tak, znam to uczucie - przepraszam :) – MaxU
@MaxU To jest dokładnie to, czego szukałem. Dziękuję Ci! –