Pertanyaan Panda: Pilih nilai dari kolom tertentu dari DataFrame berdasarkan baris


Dengan adanya DataFrame dengan beberapa kolom, bagaimana kita memilih nilai dari kolom tertentu secara berturut-turut untuk membuat Seri baru?

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"]

Tujuan: [10, 2, 3, 400]

Salah satu metode yang berhasil adalah menggunakan pernyataan yang berlaku.

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

Sayangnya, ini bukan operasi vectorized dan membutuhkan waktu yang lama pada dataset yang besar. Setiap ide akan dihargai.


9
2017-12-27 19:22


asal


Jawaban:


Pendekatan Pandas:

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

10
2017-12-27 19:33



Cara NumPy

Berikut adalah cara NumPy dengan menggunakan vektor 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])

Jika nama kolom dari df tidak diurutkan, kita perlu menggunakan sorter argumen dengan np.searchsorted. Kode untuk mengekstrak col_idx untuk generik seperti itu df akan menjadi :

# 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)]

Begitu, col_idx akan diperoleh seperti begitu -

col_idx = column_index(df, columns_to_select)

Optimasi lebih lanjut

Profil itu mengungkapkan bahwa hambatan itu sedang memproses string np.searchsorted, Kelemahan NumPy biasa karena tidak begitu hebat dengan string. Jadi, untuk mengatasi itu dan menggunakan skenario kasus khusus dari nama kolom menjadi huruf tunggal, kita bisa dengan cepat mengkonversinya menjadi angka dan kemudian memberi mereka searchsorted untuk pemrosesan lebih cepat.

Jadi, versi yang dioptimalkan untuk mendapatkan ID kolom berdasarkan integer, untuk kasus di mana nama kolom adalah huruf tunggal dan diurutkan, akan -

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)

Ini, memberi kami versi modifikasi dari solusi, seperti halnya -

a = df.values
col_idx = column_index_singlechar_sorted(df, columns_to_select)
out = pd.Series(a[np.arange(len(col_idx)), col_idx])

Timing -

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

Mengingat bahwa df.lookup memecahkan untuk kasus umum, itu mungkin pilihan yang lebih baik, tetapi optimisasi lain yang mungkin seperti yang ditunjukkan dalam posting ini bisa berguna juga!


8
2017-12-27 19:26