Có cách nào trong Pandas để sử dụng giá trị hàng trước đó trong dataframe.apply khi giá trị trước đó cũng được tính trong ứng dụng không?


95

Tôi có khung dữ liệu sau:

 Index_Date    A    B    C    D
 ===============================
 2015-01-31    10   10   Nan  10
 2015-02-01     2    3   Nan  22 
 2015-02-02    10   60   Nan  280
 2015-02-03    10   100   Nan  250

Yêu cầu:

 Index_Date    A    B    C    D
 ===============================
 2015-01-31    10   10   10   10
 2015-02-01     2    3   23   22
 2015-02-02    10   60   290  280
 2015-02-03    10   100  3000 250

Column Cđược bắt nguồn 2015-01-31từ việc lấy valuecủa D.

Sau đó, tôi cần sử dụng valuecủa Ccho 2015-01-31và nhân với valuecủa Atrên 2015-02-01và thêm B.

Tôi đã thử applyshiftsử dụng một if elsebởi điều này gây ra lỗi chính.


Tại sao các hàng cuối cùng của bạn trong khung dữ liệu lại khác nhau đối với các cột AB?
Anton Protopopov

@Anton xin lỗi là đúng.
ctrl-alt-delete

Giá trị của hàng tiếp theo trong cột Avà cột là Dgì?
jezrael

7
Đây là một câu hỏi hay. Tôi cũng có nhu cầu tương tự về một giải pháp vector hóa. Sẽ thật tuyệt nếu gấu trúc cung cấp phiên bản trong apply()đó hàm của người dùng có thể truy cập một hoặc nhiều giá trị từ hàng trước đó như một phần của phép tính hoặc ít nhất trả về một giá trị sau đó được chuyển 'cho chính nó' trong lần lặp tiếp theo. Điều này sẽ không cho phép một số tăng hiệu quả so với vòng lặp for?
Bill

@Bill, Bạn có thể quan tâm đến câu trả lời mà tôi vừa thêm này, numbathường là một lựa chọn tốt ở đây.
jpp

Câu trả lời:


65

Đầu tiên, tạo giá trị bắt nguồn:

df.loc[0, 'C'] = df.loc[0, 'D']

Sau đó, lặp qua các hàng còn lại và điền các giá trị đã tính:

for i in range(1, len(df)):
    df.loc[i, 'C'] = df.loc[i-1, 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']


  Index_Date   A   B    C    D
0 2015-01-31  10  10   10   10
1 2015-02-01   2   3   23   22
2 2015-02-02  10  60  290  280

41
Có một chức năng trong gấu trúc để làm điều này mà không cần vòng lặp?
ctrl-alt-delete

1
Tính chất lặp đi lặp lại của phép tính trong đó đầu vào phụ thuộc vào kết quả của các bước trước đó làm phức tạp hóa vectơ hóa. Có lẽ bạn có thể sử dụng applyvới một hàm thực hiện phép tính tương tự như vòng lặp, nhưng đằng sau nó cũng sẽ là một vòng lặp. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Stefan

Nếu tôi sử dụng vòng lặp này và tính toán trên khung dữ liệu đã hợp nhất và nó tìm thấy Nan thì nó hoạt động nhưng chỉ với hàng có Nan. Không có lỗi nào được đưa ra, Nếu tôi thử điềnNa, tôi nhận được AttributeError: Đối tượng 'numpy.float64' không có thuộc tính 'fillna' Có cách nào để bỏ qua hàng có Nan hoặc đặt giá trị bằng 0 không?
ctrl-alt-delete

Ý bạn là thiếu giá trị trong các cột khác C?
Stefan

Có giải pháp của bạn là tốt. Tôi chỉ đảm bảo rằng tôi điền các Nans trong dataframe trước vòng lặp.
ctrl-alt-delete

41

Cho một cột số:

lst = []
cols = ['A']
for a in range(100, 105):
    lst.append([a])
df = pd.DataFrame(lst, columns=cols, index=range(5))
df

    A
0   100
1   101
2   102
3   103
4   104

Bạn có thể tham chiếu hàng trước với shift:

df['Change'] = df.A - df.A.shift(1)
df

    A   Change
0   100 NaN
1   101 1.0
2   102 1.0
3   103 1.0
4   104 1.0

9
Điều này sẽ không hữu ích trong trường hợp này vì giá trị từ hàng trước đó không được biết ở đầu. Nó phải được tính toán từng lần lặp và sau đó được sử dụng trong lần lặp tiếp theo.
Bill

6
Tôi vẫn biết ơn câu trả lời này vì tôi đã tình cờ bắt gặp điều này, đang tìm kiếm một trường hợp mà tôi biết giá trị từ hàng trước. Vì vậy, cảm ơn @kztd
Kevin Pauli

28

numba

Đối với các phép tính đệ quy không thể vecto được, numbasử dụng biên dịch JIT và hoạt động với các đối tượng cấp thấp hơn, thường mang lại những cải tiến hiệu suất lớn. Bạn chỉ cần xác định một forvòng lặp thông thường và sử dụng trình trang trí @njithoặc (đối với các phiên bản cũ hơn) @jit(nopython=True):

Đối với khung dữ liệu có kích thước hợp lý, điều này giúp cải thiện hiệu suất ~ 30 lần so với forvòng lặp thông thường :

from numba import jit

@jit(nopython=True)
def calculator_nb(a, b, d):
    res = np.empty(d.shape)
    res[0] = d[0]
    for i in range(1, res.shape[0]):
        res[i] = res[i-1] * a[i] + b[i]
    return res

df['C'] = calculator_nb(*df[list('ABD')].values.T)

n = 10**5
df = pd.concat([df]*n, ignore_index=True)

# benchmarking on Python 3.6.0, Pandas 0.19.2, NumPy 1.11.3, Numba 0.30.1
# calculator() is same as calculator_nb() but without @jit decorator
%timeit calculator_nb(*df[list('ABD')].values.T)  # 14.1 ms per loop
%timeit calculator(*df[list('ABD')].values.T)     # 444 ms per loop

1
Nó là tuyệt vời! Tôi đã tăng tốc hàm của mình, hàm này đếm các giá trị từ các giá trị trước đó. Cảm ơn!
Artem Malikov

21

Việc áp dụng hàm đệ quy trên mảng numpy sẽ nhanh hơn câu trả lời hiện tại.

df = pd.DataFrame(np.repeat(np.arange(2, 6),3).reshape(4,3), columns=['A', 'B', 'D'])
new = [df.D.values[0]]
for i in range(1, len(df.index)):
    new.append(new[i-1]*df.A.values[i]+df.B.values[i])
df['C'] = new

Đầu ra

      A  B  D    C
   0  1  1  1    1
   1  2  2  2    4
   2  3  3  3   15
   3  4  4  4   64
   4  5  5  5  325

3
Câu trả lời này hoạt động hoàn hảo đối với tôi với một phép tính tương tự. Tôi đã thử sử dụng kết hợp cumsum và shift nhưng giải pháp này hoạt động tốt hơn nhiều. Cảm ơn.
Simon

Điều này cũng hoàn hảo cho tôi, cảm ơn. Tôi đã phải vật lộn với nhiều dạng ngứa, ngứa, bôi thuốc, v.v. và điều này có vẻ dễ hiểu và dễ thực hiện.
chaim

9

Mặc dù đã được một thời gian kể từ khi câu hỏi này được hỏi, tôi sẽ đăng câu trả lời của mình hy vọng nó sẽ giúp ích cho ai đó.

Tuyên bố từ chối trách nhiệm: Tôi biết giải pháp này không chuẩn , nhưng tôi nghĩ nó hoạt động tốt.

import pandas as pd
import numpy as np

data = np.array([[10, 2, 10, 10],
                 [10, 3, 60, 100],
                 [np.nan] * 4,
                 [10, 22, 280, 250]]).T
idx = pd.date_range('20150131', end='20150203')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df
               A    B     C    D
 =================================
 2015-01-31    10   10    NaN  10
 2015-02-01    2    3     NaN  22 
 2015-02-02    10   60    NaN  280
 2015-02-03    10   100   NaN  250

def calculate(mul, add):
    global value
    value = value * mul + add
    return value

value = df.loc['2015-01-31', 'D']
df.loc['2015-01-31', 'C'] = value
df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)
df
               A    B     C     D
 =================================
 2015-01-31    10   10    10    10
 2015-02-01    2    3     23    22 
 2015-02-02    10   60    290   280
 2015-02-03    10   100   3000  250

Vì vậy, về cơ bản chúng tôi sử dụng a applyfrom pandas và sự trợ giúp của một biến toàn cục giúp theo dõi giá trị được tính toán trước đó.


So sánh thời gian với một forvòng lặp:

data = np.random.random(size=(1000, 4))
idx = pd.date_range('20150131', end='20171026')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df.C = np.nan

df.loc['2015-01-31', 'C'] = df.loc['2015-01-31', 'D']

%%timeit
for i in df.loc['2015-02-01':].index.date:
    df.loc[i, 'C'] = df.loc[(i - pd.DateOffset(days=1)).date(), 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']

3,2 s ± 114 ms mỗi vòng (trung bình ± std. Dev. Của 7 lần chạy, mỗi lần 1 vòng)

data = np.random.random(size=(1000, 4))
idx = pd.date_range('20150131', end='20171026')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df.C = np.nan

def calculate(mul, add):
    global value
    value = value * mul + add
    return value

value = df.loc['2015-01-31', 'D']
df.loc['2015-01-31', 'C'] = value

%%timeit
df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)

1,82 s ± 64,4 ms mỗi vòng (trung bình ± std. Dev. Của 7 lần chạy, mỗi vòng 1 lần)

Vì vậy, nhanh hơn trung bình 0,57 lần.


0

Nói chung, chìa khóa để tránh một vòng lặp rõ ràng là nối (hợp nhất) 2 phiên bản của khung dữ liệu trên rowindex-1 == rowindex.

Sau đó, bạn sẽ có một khung dữ liệu lớn chứa các hàng r và r-1, từ đó bạn có thể thực hiện một hàm df.apply ().

Tuy nhiên, chi phí tạo tập dữ liệu lớn có thể bù đắp lợi ích của việc xử lý song song ...

HTH Martin

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.