以前の値も適用で計算されるときに、パンダでdataframe.applyの前の行の値を使用する方法はありますか?


96

私は次のデータフレームを持っています:

 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

次に、valueof Cforを使用2015-01-31し、valueof Aon2015-02-01を掛けて、を追加する必要がありますB

私はを試みましたがapply、これをshift使用するとif elseキーエラーが発生します。


データフレームの最後の行が列Aと 異なるのはなぜBですか?
アントンプロト

@Antonは今正しいことをお詫びします。
ctrl-alt-delete

Aと列の次の行の値は何Dですか?
jezrael 2016年

7
これは良い質問です。ベクトル化されたソリューションについても同様のニーズがあります。パンダapply()が、ユーザーの関数が計算の一部として前の行の1つ以上の値にアクセスできるバージョンを提供するか、少なくとも次の反復で「それ自体に」渡される値を返すことができると便利です。これにより、forループと比較して効率がいくらか向上しませんか?
ビル

@ビル、あなたは私がちょうど追加したこの答えに興味があるかもしれません、numbaしばしばここで良いオプションです。
JPP

回答:


68

まず、派生値を作成します。

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

42
パンダにループなしでこれを行う機能はありますか?
ctrl-alt-delete

1
入力が前のステップの結果に依存する計算の反復性は、ベクトル化を複雑にします。applyループと同じ計算を行う関数で使用することもできますが、舞台裏ではこれもループになります。pandas.pydata.org/pandas-docs/version/0.17.1/generated/…–
Stefan

このループを使用してマージされたデータフレームで計算し、Nanが見つかった場合、それは機能しますが、Nanのある行に対してのみ機能します。エラーはスローされません。fillNaを試行するとAttributeErrorが発生します: 'numpy.float64'オブジェクトに属性 'fillna'がありませんNanの行をスキップしたり、値をゼロに設定したりする方法はありますか?
ctrl-alt-delete

以外の列に値がないことを意味しますCか?
ステファン

はい、あなたの解決策は大丈夫です。ループの前に、データフレームにNansを入力するようにします。
ctrl-alt-delete

43

数字の列が与えられた:

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

10
前の行の値が最初はわからないため、これはこの状況では役に立ちません。反復ごとに計算してから、次の反復で使用する必要があります。
ビル

6
前の行の値がわかっているケースを探して、これに遭遇したので、私はまだこの答えに感謝しています。おかげで@kztdだから
ケビン・パウリ

28

numba

ベクトル化できない再帰計算の場合numba、JITコンパイルを使用し、低レベルのオブジェクトで機能するため、多くの場合、パフォーマンスが大幅に向上します。通常の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
それは素晴らしいです!以前の値から値をカウントする関数を高速化しました。ありがとう!
アルテムマリコフ

23

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を組み合わせて使用​​してみましたが、このソリューションの方がはるかに効果的です。ありがとう。
サイモン

これは私にもぴったりです、ありがとう。私はさまざまな形の繰り返し、繰り返し、適用などに苦労していましたが、これは理解しやすく、パフォーマンスも簡単なようです。
CHAIM

10

この質問をしてからしばらく経ちますが、誰かの助けになることを願って回答を投稿します。

免責事項:このソリューションが標準ではないことは知っていますが、うまく機能すると思います。

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

したがって、基本的には、applyパンダのfromと、前に計算された値を追跡するグローバル変数の助けを借ります。


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秒±114ミリ秒(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.4ミリ秒(7回の実行の平均±標準偏差、各1ループ)

したがって、平均で0.57倍高速です。


0

一般に、明示的なループを回避するための鍵は、rowindex-1 == rowindexのデータフレームの2つのインスタンスを結合(マージ)することです。

次に、rとr-1の行を含む大きなデータフレームが作成され、そこからdf.apply()関数を実行できます。

ただし、大規模なデータセットを作成するオーバーヘッドは、並列処理の利点を相殺する可能性があります...

HTHマーティン

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.