gấu trúc: bộ lọc phức tạp trên các hàng của DataFrame


85

Tôi muốn lọc các hàng theo một hàm của mỗi hàng, ví dụ:

def f(row):
  return sin(row['velocity'])/np.prod(['masses']) > 5

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, f)]

Hoặc đối với một ví dụ khác phức tạp hơn,

def g(row):
  if row['col1'].method1() == 1:
    val = row['col1'].method2() / row['col1'].method3(row['col3'], row['col4'])
  else:
    val = row['col2'].method5(row['col6'])
  return np.sin(val)

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, g)]

Làm thế nào tôi có thể làm như vậy?

Câu trả lời:


121

Bạn có thể làm điều này bằng cách sử dụng DataFrame.apply, áp dụng một hàm dọc theo một trục nhất định,

In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [4]: df
Out[4]: 
          a         b         c
0 -0.001968 -1.877945 -1.515674
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

In [6]: df[df.apply(lambda x: x['b'] > x['c'], axis=1)]
Out[6]: 
          a         b         c
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

15
Không cần thiết applytrong tình huống này. Chỉ mục boolean thông thường sẽ hoạt động tốt. df[df['b] > df['c']]. Có rất ít tình huống thật sự yêu cầu applyvà thậm chí vài cần nó vớiaxis=1
Ted Petrou

@TedPetrou Điều gì xảy ra nếu bạn không chắc chắn rằng mọi phần tử trong khung dữ liệu của bạn là đúng loại. Chỉ mục boolean thông thường có hỗ trợ xử lý ngoại lệ không?
D. Ror.

13

Giả sử tôi có một DataFrame như sau:

In [39]: df
Out[39]: 
      mass1     mass2  velocity
0  1.461711 -0.404452  0.722502
1 -2.169377  1.131037  0.232047
2  0.009450 -0.868753  0.598470
3  0.602463  0.299249  0.474564
4 -0.675339 -0.816702  0.799289

Tôi có thể sử dụng sin và DataFrame.prod để tạo mặt nạ boolean:

In [40]: mask = (np.sin(df.velocity) / df.ix[:, 0:2].prod(axis=1)) > 0

In [41]: mask
Out[41]: 
0    False
1    False
2    False
3     True
4     True

Sau đó, sử dụng mặt nạ để chọn từ DataFrame:

In [42]: df[mask]
Out[42]: 
      mass1     mass2  velocity
3  0.602463  0.299249  0.474564
4 -0.675339 -0.816702  0.799289

2
thực ra, đây có lẽ là một ví dụ tồi: np.sintự động phát sóng tới tất cả các phần tử. Điều gì sẽ xảy ra nếu tôi thay thế nó bằng một chức năng kém thông minh hơn chỉ có thể xử lý một đầu vào tại một thời điểm?
duckworthd


5

Tôi bình luận về câu trả lời của duckworthd , nhưng nó không hoạt động hoàn hảo. Nó bị treo khi khung dữ liệu trống:

df = pandas.DataFrame(columns=['a', 'b', 'c'])
df[df.apply(lambda x: x['b'] > x['c'], axis=1)]

Kết quả đầu ra:

ValueError: Must pass DataFrame with boolean values only

Đối với tôi, nó trông giống như một lỗi ở gấu trúc, vì {} chắc chắn là một tập hợp các giá trị boolean hợp lệ. Để có giải pháp, hãy tham khảo câu trả lời của Roy Hyunjin Han .


3

Cách tiếp cận tốt nhất mà tôi đã tìm thấy là, thay vì sử dụng reduce=Trueđể tránh lỗi cho df trống (vì đối số này không được dùng nữa), chỉ cần kiểm tra kích thước df> 0 trước khi áp dụng bộ lọc:

def my_filter(row):
    if row.columnA == something:
        return True

    return False

if len(df.index) > 0:
    df[df.apply(my_filter, axis=1)]

0

Bạn có thể sử dụng thuộc loctính cho khung dữ liệu của bạn.

Theo tài liệu , loccó thể có một callable functionđối số như.

In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [4]: df
Out[4]: 
          a         b         c
0 -0.001968 -1.877945 -1.515674
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

# define lambda function
In [5]: myfilter = lambda x: x['b'] > x['c']

# use my lambda in loc
In [6]: df1 = df.loc[fif]

nếu bạn muốn kết hợp chức năng bộ lọc của mình fifvới các tiêu chí bộ lọc khác

df1 = df.loc[fif].loc[(df.b >= 0.5)]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.