web-dev-qa-db-ja.com

Pandas DataFrameは、現在の値を決定するために複雑な「if」条件に前の行の値を使用します

次のループを行うより速い方法があるかどうか知りたいですか?多分これを実現するために適用またはローリング適用機能を使用します。基本的に、現在のセル値を決定するために前の行の値にアクセスする必要があります。

df.ix[0] = (np.abs(df.ix[0]) >= So) * np.sign(df.ix[0])
for i in range(1, len(df)):
    for col in list(df.columns.values):
        if ((df[col].ix[i] > 1.25) & (df[col].ix[i-1] == 0)) | :
            df[col].ix[i] = 1
        Elif ((df[col].ix[i] < -1.25) & (df[col].ix[i-1] == 0)):
            df[col].ix[i] = -1
        Elif ((df[col].ix[i] <= -0.75) & (df[col].ix[i-1] < 0)) | ((df[col].ix[i] >= 0.5) & (df[col].ix[i-1] > 0)):
            df[col].ix[i] = df[col].ix[i-1]
        else:
            df[col].ix[i] = 0

ご覧のとおり、関数ではデータフレームを更新しています。更新された前の行にアクセスする必要があるため、shiftを使用しても機能しません。

例:入力:

A      B     C
1.3  -1.5   0.7
1.1  -1.4   0.6
1.0  -1.3   0.5
0.4   1.4   0.4

出力:

 A      B     C
1     -1      0
1     -1      0
1     -1      0
0      1      0
13
user5025141

。shift() 関数を使用して、前のまたは次の値にアクセスできます:

col列の以前の値:

df['col'].shift()

col列の次の値:

df['col'].shift(-1)

例:

In [38]: df
Out[38]:
   a  b  c
0  1  0  5
1  9  9  2
2  2  2  8
3  6  3  0
4  6  1  7

In [39]: df['prev_a'] = df['a'].shift()

In [40]: df
Out[40]:
   a  b  c  prev_a
0  1  0  5     NaN
1  9  9  2     1.0
2  2  2  8     9.0
3  6  3  0     2.0
4  6  1  7     6.0

In [43]: df['next_a'] = df['a'].shift(-1)

In [44]: df
Out[44]:
   a  b  c  prev_a  next_a
0  1  0  5     NaN     9.0
1  9  9  2     1.0     2.0
2  2  2  8     9.0     6.0
3  6  3  0     2.0     6.0
4  6  1  7     6.0     NaN
25
MaxU

シフトとローリングはそれを成し遂げないので、これに対するネイティブのpandasソリューションもないことに驚いています。標準のpandas構文を使用してこれを実行する方法を考案しましたが、ループよりもパフォーマンスが良いかどうかはわかりません...

import pandas as pd

df = pd.DataFrame({'a':[0,1,2], 'b':[0,10,20]})

new_col = 'c'

def apply_func_decorator(func):
    prev_row = {}
    def wrapper(curr_row, **kwargs):
        val = func(curr_row, prev_row)
        prev_row.update(curr_row)
        prev_row[new_col] = val
        return val
    return wrapper

@apply_func_decorator
def running_total(curr_row, prev_row):
    return curr_row['a'] + curr_row['b'] + prev_row.get('c', 0)

df[new_col] = df.apply(running_total, axis=1)

print(df)
# Output will be:
#    a   b   c
# 0  0   0   0
# 1  1  10  11
# 2  2  20  33

免責事項:pandas 0.16を使用しましたが、わずかな変更を加えるだけで、これは最新バージョンでも機能します。

他にも同様の質問があり、私もそれらにこの解決策を投稿しました:

4
CoreDump

@maxUにはシフトがあれば正しく、次のようなデータフレームを直接比較することもできると思います。

df_prev = df.shift(-1)
df_out = pd.DataFrame(index=df.index,columns=df.columns)

df_out[(df>1.25) & (df_prev == 0)] = 1
df_out[(df<-1.25) & (df_prev == 0)] = 1
df_out[(df<-.75) & (df_prev <0)] = df_prev
df_out[(df>.5) & (df_prev >0)] = df_prev

構文がオフになっている可能性がありますが、テストデータを提供すると、これでうまくいくと思います。

ループする必要がまったくなくなります。

編集-以下のコメントに基づいて更新

DF自体をループしないように最善を尽くします。列ごとに行ってリストに送信し、更新を行ってから、もう一度インポートすることをお勧めします。この:

df.ix[0] = (np.abs(df.ix[0]) >= 1.25) * np.sign(df.ix[0]) 
for col in df.columns.tolist():
    currData = df[col].tolist()
    for currRow in range(1,len(currData)):
        if  currData[currRow]> 1.25 and currData[currRow-1]== 0:
            currData[currRow] = 1
        Elif currData[currRow] < -1.25 and currData[currRow-1]== 0:
            currData[currRow] = -1
        Elif currData[currRow] <=-.75 and currData[currRow-1]< 0:
            currData[currRow] = currData[currRow-1]
        Elif currData[currRow]>= .5 and currData[currRow-1]> 0:
            currData[currRow] = currData[currRow-1]
        else:
            currData[currRow] = 0
    df[col] = currData
1
flyingmeatball