2017-12-27 19 views
9

Bei einem DataFrame mit mehreren Spalten, wie wählen wir Werte aus bestimmten Spalten nach Zeilen, um eine neue Serie zu erstellen? Pandas: Wählen Sie Werte aus bestimmten Spalten eines DataFrame nach Zeile

df = pd.DataFrame({"A":[1,2,3,4], 
        "B":[10,20,30,40], 
        "C":[100,200,300,400]}) 
columns_to_select = ["B", "A", "A", "C"] 

Ziel: [10, 2, 3, 400]

Eine Methode, die funktioniert, ist eine Anwendung Anweisung zu verwenden.

df["cols"] = columns_to_select 
df.apply(lambda x: x[x.cols], axis=1) 

Leider handelt es sich hierbei nicht um eine vektorisierte Operation und es dauert sehr lange auf einem großen Dataset. Irgendwelche Ideen würden geschätzt werden.

Antwort

10

Pandas approach:

In [22]: df['new'] = df.lookup(df.index, columns_to_select) 

In [23]: df 
Out[23]: 
    A B C new 
0 1 10 100 10 
1 2 20 200 2 
2 3 30 300 3 
3 4 40 400 400 
+1

eine Sekunde hinter sich. ;-) – Wen

+0

@Wen, yeah, ich kenne dieses Gefühl - sorry :) – MaxU

+0

@MaxU Das ist genau das, was ich gesucht habe. Vielen Dank! –

8

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!

Verwandte Themen