2015-08-12 15 views
6

Tôi có một DataFrame pandas Python trong đó mỗi phần tử là một float hoặc NaN. Đối với mỗi hàng, tôi sẽ cần phải tìm cột chứa số thứ n của hàng. Tức là, tôi cần lấy cột giữ phần tử thứ n của hàng không phải là NaN. Tôi biết rằng cột thứ n như vậy luôn luôn tồn tại.Đối với mỗi hàng, cách nhanh nhất để tìm cột giữ phần tử thứ n không phải là NaN?

Vì vậy, nếu n là 4 và một dataframe gấu trúc được gọi là myDF là như sau:

 10 20 30 40 50 60 70 80 90 100 

'A' 4.5 5.5 2.5 NaN NaN 2.9 NaN NaN 1.1 1.8 
'B' 4.7 4.1 NaN NaN NaN 2.0 1.2 NaN NaN NaN 
'C' NaN NaN NaN NaN NaN 1.9 9.2 NaN 4.4 2.1 
'D' 1.1 2.2 3.5 3.4 4.5 NaN NaN NaN 1.9 5.5 

tôi muốn để có được:

'A' 60 
'B' 70 
'C' 100 
'D' 40 

tôi có thể làm:

import pandas as pd 
import math 

n = some arbitrary int 
for row in myDF.indexes: 
    num_not_NaN = 0 
    for c in myDF.columns:  
     if math.isnan(myDF[c][row]) == False: 
      num_not_NaN +=1 
     if num_not_NaN==n: 
      print row, c 
      break 

Tôi chắc rằng điều này rất chậm và không phải là rất Pythonic. Có cách tiếp cận nào sẽ nhanh hơn nếu tôi đang xử lý một DataFrame rất lớn và các giá trị lớn của n?

Trả lời

5

Nếu tốc độ là mục tiêu của bạn, đó là một tốt ý tưởng sử dụng các phương pháp vectơ của Pandas bất cứ khi nào bạn có thể:

Các câu trả lời khác là tốt và có thể là một chút rõ ràng hơn về cú pháp. Xét về tốc độ, không có nhiều sự khác biệt giữa chúng cho ví dụ nhỏ của bạn. Tuy nhiên, đối với một DataFrame hơi lớn hơn, phương pháp vector hóa đã nhanh hơn khoảng 60 lần:

>>> df2 = pd.concat([df]*1000) # 4000 row DataFrame 
>>> %timeit df2.apply(lambda row: get_nth(row, n), axis=1) 
1 loops, best of 3: 749 ms per loop 

>>> %timeit df2.T.apply(lambda x: x.dropna()[n-1:].index[0]) 
1 loops, best of 3: 673 ms per loop 

>>> %timeit (df2.notnull().cumsum(1) == 4).idxmax(axis=1) 
100 loops, best of 3: 10.5 ms per loop 
+0

Cảm ơn rất nhiều. Điều này sẽ tăng tốc độ cho tôi rất nhiều. –

2

Bạn có thể tạo một hàm và sau đó chuyển nó đến hàm lambda.

Hàm sẽ lọc chuỗi cho giá trị rỗng và sau đó trả về giá trị chỉ mục của mục n (hoặc Không nếu độ dài chỉ mục nhỏ hơn n).

Nhu cầu chức năng lambdaaxis=1 để đảm bảo chức năng được áp dụng cho mỗi hàng của DataFrame.

def get_nth(series, n): 
    s = series[series.notnull()] 
    if len(s) >= n: 
     return s.index[n - 1] 

>>> n = 4 
>>> df.apply(lambda row: get_nth(row, n), axis=1) 
A  60 
B  70 
C 100 
D  40 
dtype: object 
2

Bạn có thể transpose df và áp dụng một lambda mà giọt các hàng NaN, lát từ giá trị thứ 4 trở đi và trả về chỉ số hợp lệ đầu tiên:

In [72]: 
n=4 

df.T.apply(lambda x: x.dropna()[n-1:].index[0]) 
Out[72]: 
'A'  60 
'B'  70 
'C' 100 
'D'  40 
dtype: object 
Các vấn đề liên quan