2016-05-02 36 views
7

Tôi có một khung dữ liệu gấu trúc và tôi muốn tính toán giá trị trung bình của một cột (sau một mệnh đề nhóm). Tuy nhiên, tôi muốn loại trừ NaN.pandas groupby và rolling_apply bỏ qua NaNs

Ví dụ: nếu nhóm trả về [2, NaN, 1], kết quả sẽ là 1,5 trong khi hiện tại trả về NaN.

Tôi đã thử những điều sau đây nhưng nó dường như không làm việc:

df.groupby(by=['var1'])['value'].apply(pd.rolling_apply, 3, lambda x: np.mean([i for i in x if i is not np.nan and i!='NaN'])) 

Nếu tôi thậm chí cố gắng này:

df.groupby(by=['var1'])['value'].apply(pd.rolling_apply, 3, lambda x: 1) 

Tôi nhận được NaN trong đầu ra nên nó phải là một cái gì đó để làm với cách gấu trúc hoạt động trong nền.

Bất kỳ ý tưởng nào?

EDIT: Đây là một mẫu mã với những gì tôi đang cố gắng để làm:

import pandas as pd 
import numpy as np 

df = pd.DataFrame({'var1' : ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b'], 'value' : [1, 2, 3, np.nan, 2, 3, 4, 1] }) 
print df.groupby(by=['var1'])['value'].apply(pd.rolling_apply, 2, lambda x: np.mean([i for i in x if i is not np.nan and i!='NaN'])) 

Kết quả là:

0 NaN 
1 NaN 
2 2.0 
3 NaN 
4 2.5 
5 NaN 
6 3.0 
7 2.0 

trong khi tôi muốn có những điều sau đây:

0 NaN 
1 NaN 
2 2.0 
3 2.0 
4 2.5 
5 3.0 
6 3.0 
7 2.0 
+1

Vui lòng cung cấp một bộ mã có thể tái sản xuất nhỏ để tôi có thể chơi với thông tin tương tự mà bạn có mà không phải tự mình tạo. – piRSquared

+0

@piRSquared Tôi vừa thêm một mẫu mã. Cảm ơn – Stergios

Trả lời

1

Kết quả này có phù hợp với mong đợi của bạn không? Tôi hơi thay đổi giải pháp của bạn với tham số min_periods và bộ lọc phù hợp cho nan.

In [164]: df.groupby(by=['var1'])['value'].apply(pd.rolling_apply, 2, lambda x: np.mean([i for i in x if not np.isnan(i)]), min_periods=1) 
Out[164]: 
0 1.0 
1 2.0 
2 2.0 
3 2.0 
4 2.5 
5 3.0 
6 3.0 
7 2.0 
dtype: float64 
+0

Đây là cách sử dụng thông minh của 'min_period'! – IanS

1

Đây là một thực hiện thay thế mà không cần danh sách hiểu, nhưng nó cũng không để cư entry đầu tiên của đầu ra với np.nan

means = df.groupby('var1')['value'].apply(
    lambda gp: gp.rolling(2, min_periods=1).apply(np.nanmean)) 
8

Như mọi khi trong gấu trúc, gắn bó với phương pháp vectorized (tức là tránh apply) là điều cần thiết cho hiệu suất và khả năng mở rộng.

Thao tác bạn muốn thực hiện hơi khó sử dụng khi thao tác lăn trên đối tượng nhóm không được nhận thức NaN (phiên bản 0.18.1). Như vậy, chúng ta sẽ cần một vài dòng mã ngắn:

g1 = df.groupby(['var1'])['value']    # group values 
g2 = df.fillna(0).groupby(['var1'])['value'] # fillna, then group values 

s = g2.rolling(2).sum()/g1.rolling(2).count() # the actual computation 

s.reset_index(level=0, drop=True).sort_index() # drop/sort index 

Ý tưởng là để tổng hợp các giá trị trong cửa sổ (sử dụng sum), đếm các giá trị NaN (sử dụng count) và sau đó chia để tìm ra nghĩa là. Mã này cung cấp cho các đầu ra sau đây phù hợp với đầu ra mong muốn của bạn:

0 NaN 
1 NaN 
2 2.0 
3 2.0 
4 2.5 
5 3.0 
6 3.0 
7 2.0 
Name: value, dtype: float64 

nghiệm này trên một DataFrame lớn hơn (khoảng 100.000 hàng), thời gian chạy là dưới 100ms, đáng kể nhanh hơn bất kỳ phương pháp áp dụng dựa trên tôi đã cố gắng.

Có thể đáng thử nghiệm các cách tiếp cận khác nhau trên dữ liệu thực tế của bạn vì thời gian có thể bị ảnh hưởng bởi các yếu tố khác như số nhóm. Nó khá chắc chắn rằng tính toán vectorized sẽ giành chiến thắng, mặc dù.


Cách tiếp cận được hiển thị ở trên hoạt động tốt cho các phép tính đơn giản, chẳng hạn như giá trị trung bình.Nó sẽ làm việc cho các phép tính phức tạp hơn (chẳng hạn như độ lệch tiêu chuẩn cán), mặc dù việc thực hiện có liên quan nhiều hơn.

Ý tưởng chung là xem xét từng thói quen đơn giản nhanh chóng trong gấu trúc (ví dụ: sum) và sau đó điền bất kỳ giá trị null nào có phần tử nhận dạng (ví dụ: 0). Sau đó, bạn có thể sử dụng groubpy và thực hiện thao tác lăn (ví dụ: .rolling(2).sum()). Đầu ra sau đó được kết hợp với (các) đầu ra của các hoạt động khác.

Ví dụ: để triển khai nhóm theo sai số cuộn NaN nhận biết (độ lệch chuẩn là căn bậc hai), chúng tôi phải tìm "giá trị trung bình của ô vuông trừ bình phương của giá trị trung bình". Dưới đây là bản phác thảo về những gì có thể trông giống như sau:

def rolling_nanvar(df, window): 
    """ 
    Group df by 'var1' values and then calculate rolling variance, 
    adjusting for the number of NaN values in the window. 

    Note: user may wish to edit this function to control degrees of 
    freedom (n), depending on their overall aim. 
    """ 
    g1 = df.groupby(['var1'])['value'] 
    g2 = df.fillna(0).groupby(['var1'])['value'] 
    # fill missing values with 0, square values and groupby 
    g3 = df['value'].fillna(0).pow(2).groupby(df['var1']) 

    n = g1.rolling(window).count() 

    mean_of_squares = g3.rolling(window).sum()/n 
    square_of_mean = (g2.rolling(window).sum()/n)**2 
    variance = mean_of_squares - square_of_mean 
    return variance.reset_index(level=0, drop=True).sort_index() 

Lưu ý rằng chức năng này có thể không ổn định về số (bình phương có thể dẫn đến tràn). gấu trúc sử dụng Welford's algorithm nội bộ để giảm thiểu vấn đề này.

Dù sao, chức năng này, mặc dù nó sử dụng nhiều thao tác, vẫn rất nhanh. Dưới đây là một sự so sánh với các phương pháp ngắn gọn hơn được áp dụng dựa trên đề nghị của Yakym Pirozhenko:

>>> df2 = pd.concat([df]*10000, ignore_index=True) # 80000 rows 
>>> %timeit df2.groupby('var1')['value'].apply(\ 
     lambda gp: gp.rolling(7, min_periods=1).apply(np.nanvar)) 
1 loops, best of 3: 11 s per loop 

>>> %timeit rolling_nanvar(df2, 7) 
10 loops, best of 3: 110 ms per loop 

bản vẽ Gia là 100 lần nhanh hơn trong trường hợp này. Tất nhiên, tùy thuộc vào lượng dữ liệu bạn có, bạn có thể muốn sử dụng apply vì nó cho phép bạn tính tổng quát/ngắn gọn với chi phí hiệu suất.

+0

Lưu ý rằng điều này sử dụng phương pháp 'rolling', chỉ có sẵn trong gấu trúc 18, trong khi OP sử dụng' pd.rolling_apply', vì vậy nhiều khả năng có gấu trúc 17 hoặc thấp hơn được cài đặt. – IanS

+0

@ajcr Điều này giải quyết vấn đề hiện tại của tôi nhưng cần viết lại hoàn toàn nếu tôi muốn áp dụng các hàm khác với giá trị trung bình (ví dụ: độ lệch tiêu chuẩn cuộn). Có cách nào để làm cho nó hoạt động cho các chức năng khác không? – Stergios

+0

@Stergios: Tôi sẽ cung cấp cho vấn đề một số suy nghĩ và thêm một số hướng dẫn/đề xuất cho câu trả lời này sau ngày hôm nay/ngày mai. Chắc chắn rằng việc sử dụng 'apply' đôi khi là lựa chọn thuận tiện nhất và cung cấp giải pháp chung nhất (không phải tất cả các chức năng đều dễ bắt chước với các thói quen gấu trúc tăng tốc). –

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