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.
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
@piRSquared Tôi vừa thêm một mẫu mã. Cảm ơn – Stergios