programing

적용에서 이전 값도 계산 될 때 Pandas에서 dataframe.apply의 이전 행 값을 사용하는 방법이 있습니까?

itsource 2021. 1. 15. 08:12
반응형

적용에서 이전 값도 계산 될 때 Pandas에서 dataframe.apply의 이전 행 값을 사용하는 방법이 있습니까?


다음 데이터 프레임이 있습니다.

 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

필요 :

 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대해 유도된다 2015-01-31취하여 valueD.

그럼 난 사용해야 value의를 C위한 2015-01-31과 곱셈에 의해 valueA2015-02-01추가합니다 B.

나는 시도 apply했고를 shift사용하면 if else키 오류가 발생합니다.


먼저 파생 된 값을 만듭니다.

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

그런 다음 나머지 행을 반복하고 계산 된 값을 채 웁니다.

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

숫자 열이 주어지면 :

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

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

numpy 배열에 재귀 함수를 적용하는 것이 현재 답변보다 빠릅니다.

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

산출

      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

numba

벡터화 할 수없는 재귀 계산의 경우 numbaJIT 컴파일을 사용하고 하위 수준 개체와 함께 작동하는은 종종 성능이 크게 향상됩니다. 일반 for루프를 정의 하고 데코레이터 @njit또는 (이전 버전의 경우) 사용하기 만하면됩니다 @jit(nopython=True).

합리적인 크기의 데이터 프레임의 경우 일반 for루프에 비해 ~ 30 배의 성능 향상을 제공합니다 .

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

이 질문을 한 지 오래되었지만 누군가에게 도움이되기를 바라면서 답변을 게시하겠습니다.

면책 조항 : 이 솔루션이 표준 이 아니라는 것을 알고 있지만 잘 작동한다고 생각합니다.

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

그래서 기본적으로 우리는 applyfrom pandas와 이전에 계산 된 값을 추적하는 전역 변수의 도움을 사용합니다.


for루프를 사용한 시간 비교 :

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 per loop (mean ± std. dev. of 7 runs, 1 loop each)

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 per loop (mean ± std. dev. of 7 runs, 1 loop each)

So 0.57 times faster on average.


I have found the for-based solution too slow.

df['C'] = 0
df.loc[df.index[1:], 'C'] = df.loc[df.index[:-1], 'D'].to_numpy()
df['C'] = df['D'] * df['A'] + df['B']

Hundreds of times faster. It matters when you have 100+k rows.

ReferenceURL : https://stackoverflow.com/questions/34855859/is-there-a-way-in-pandas-to-use-previous-row-value-in-dataframe-apply-when-previ

반응형