2017-12-27 138 views
9

Cho một DataFrame có nhiều cột, làm cách nào để chúng tôi chọn giá trị từ các cột cụ thể theo hàng để tạo một Chuỗi mới?Pandas: Chọn giá trị từ các cột cụ thể của một DataFrame theo hàng

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

Mục tiêu: [10, 2, 3, 400]

Một phương pháp mà làm việc là sử dụng một tuyên bố áp dụng.

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

Thật không may, đây không phải là hoạt động được vector hóa và mất nhiều thời gian trên một tập dữ liệu lớn. Có những câu chuyện mới trên trang chủ.

Trả lời

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

một giây phía sau bạn. ;-) – Wen

+0

@Wen, vâng, tôi biết cảm giác này - xin lỗi :) – MaxU

+0

@MaxU Đây chính xác là những gì tôi đang tìm kiếm. Cảm ơn bạn! –

8

NumPy cách

Dưới đây là một NumPy cách vectorized sử dụng 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]) 

Nếu tên cột của df không sắp xếp, chúng ta cần phải sử dụng sorter tranh cãi với np.searchsorted. Mã này để trích xuất col_idx cho ví dụ một generic df sẽ là:

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

Vì vậy, col_idx sẽ thu được như vậy -

col_idx = column_index(df, columns_to_select) 

Tiếp tục tối ưu hóa

Profiling nó tiết lộ rằng nút cổ chai đã xử lý các chuỗi với np.searchsorted, điểm yếu NumPy thông thường không quá lớn với các chuỗi. Vì vậy, để khắc phục điều đó và sử dụng trường hợp đặc biệt của các tên cột là các chữ cái đơn, chúng ta có thể nhanh chóng chuyển đổi chúng thành các chữ số và sau đó chuyển chúng thành searchsorted để xử lý nhanh hơn nhiều.

Do đó, một phiên bản được tối ưu hóa để nhận các ID cột số nguyên dựa, cho trường hợp các tên cột là chữ duy nhất và được sắp xếp, sẽ là -

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) 

này, cho chúng ta một phiên bản sửa đổi của giải pháp , như vậy -

Thời gian -

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 

cho rằng df.lookup giải quyết cho một trường hợp chung chung, đó có thể là một lựa chọn tốt hơn, nhưng tối ưu hóa khác có thể được hiển thị trong bài đăng này cũng có thể hữu ích!

Các vấn đề liên quan