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


94

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

 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키 오류가 발생합니다.


왜 dataframes의 마지막 행은 열 다릅니다 있습니다 AB?
Anton Protopopov

@Anton은 이제 올바른 사과합니다.
ctrl-alt-delete

열에서 다음 행의 값이란 A과 열은 D?
jezrael

7
이것은 좋은 질문입니다. 벡터화 된 솔루션에 대한 유사한 요구가 있습니다. pandas apply()가 사용자의 함수가 계산의 일부로 이전 행에서 하나 이상의 값에 액세스 할 수있는 버전을 제공 하거나 적어도 다음 반복에서 '자신에게'전달되는 값을 반환하면 좋을 것 입니다. 이렇게하면 for 루프에 비해 효율성이 향상되지 않습니까?
Bill

@Bill, 방금 추가 한이 답변에 관심 있을 수 있으며 numba종종 여기에서 좋은 옵션입니다.
jpp

답변:


64

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

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

41
pandas에 루프없이이 작업을 수행하는 기능이 있습니까?
ctrl-alt-delete

1
입력이 이전 단계의 결과에 의존하는 계산의 반복적 인 특성으로 인해 벡터화가 복잡해집니다. apply루프와 동일한 계산을 수행하는 함수와 함께 사용할 수 있지만 배후에서는 루프가 될 수도 있습니다. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Stefan

이 루프를 사용하고 병합 된 데이터 프레임에서 계산하고 Nan을 찾으면 Nan이있는 행에서만 작동합니다. 오류가 발생하지 않습니다. fillNa를 시도하면 AttributeError : 'numpy.float64'개체에 'fillna'속성이 없습니다. Nan으로 행을 건너 뛰거나 값을 0으로 설정하는 방법이 있습니까?
ctrl-alt-delete

이외의 열에서 누락 된 값을 의미 C합니까?
Stefan

예, 귀하의 솔루션은 괜찮습니다. 루프 전에 데이터 프레임에서 Nans를 채웠는지 확인합니다.
ctrl-alt-delete

41

숫자 열이 주어지면 :

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

9
이 상황에서는 이전 행의 값을 처음에 알 수 없기 때문에 도움이되지 않습니다. 매 반복마다 계산 된 다음 다음 반복에서 사용되어야합니다.
Bill

6
나는 이전 행의 값을 알고있는 경우를 찾고 이것을 우연히 발견했기 때문에 여전히이 답변에 감사드립니다. 감사합니다 @kztd
Kevin Pauli

28

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

1
그것은 경이 롭다! 이전 값에서 값을 세는 기능을 가속화했습니다. 감사!
Artem Malikov

20

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

3
이 답변은 비슷한 계산으로 나에게 완벽하게 작동합니다. 나는 cumsum과 shift의 조합을 사용해 보았지만이 솔루션은 훨씬 더 잘 작동합니다. 감사.
Simon

이것은 나에게도 완벽하게 작동합니다. 감사합니다. 나는 많은 형태의 반복, 반복, 적용 등으로 어려움을 겪고 있었고 이것은 이해하기 쉽고 성능이 좋아 보입니다.
차임

9

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

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

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 초 ± 114ms (7 회 실행의 평균 ± 표준 편차, 각 1 루프)

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 초 ± 64.4ms (7 회 실행의 평균 ± 표준 편차, 각 1 회 루프)

따라서 평균적으로 0.57 배 더 빠릅니다.


0

일반적으로 명시 적 루프를 피하는 핵심은 rowindex-1 == rowindex에서 데이터 프레임의 두 인스턴스를 결합 (병합)하는 것입니다.

그런 다음 df.apply () 함수를 수행 할 수있는 r 및 r-1 행을 포함하는 빅 데이터 프레임을 갖게됩니다.

그러나 대규모 데이터 세트를 생성하는 오버 헤드는 병렬 처리의 이점을 상쇄 할 수 있습니다.

HTH 마틴

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.