以下のデータフレームがあると仮定します
Date, A
2014-11-21 11:00:00, 1
2014-11-21 11:03:00, 4
2014-11-21 11:04:00, 1
2014-11-21 11:05:00, 2
2014-11-21 11:07:00, 4
2014-11-21 11:08:00, 1
2014-11-21 11:12:00, 1
2014-11-21 11:13:00, 2
最初の列は日時オブジェクトで、2番目の列は整数です。私が欲しいのは、各行の最後の5分間の列「A」の合計を計算することです。
行2014-11-21 11:12:00, 1
の例として、列 'A'の合計は2(1 + 1)になり、行2014-11-21 11:05:00, 2
の列 'A'の合計は7(2 + 1)になります。 +4)。重要なことは、時間枠(5分)の過去の行数が各行で同じではないことです(時系列が不規則であるため)。
パンダでrolling_sumメソッドを使用して列「A」の最後の5分間の合計を取得するにはどうすればよいですか?前もって感謝します。
一般に、日付が完全に任意の場合、行に対してPython _for-loop
_を使用するか、 _df.apply
_ を使用する)を強制されると思います。 =、(内部では、Pythonループも使用します。)
ただし、上記のように日付が共通の頻度を共有している場合は、_df.apply
_を使用するよりもはるかに高速なトリックがあります。共通の頻度(この場合は1分)に従って時系列を展開します。 --NaNにゼロを入力してから、_rolling_sum
_を呼び出します。
_In [279]: pd.rolling_sum(df.set_index(['Date']).asfreq('1T').fillna(0), window=5, min_periods=1).reindex(df['Date'])
Out[279]:
A
Date
2014-11-21 11:00:00 1
2014-11-21 11:03:00 5
2014-11-21 11:04:00 6
2014-11-21 11:05:00 7
2014-11-21 11:07:00 11
2014-11-21 11:08:00 8
2014-11-21 11:12:00 2
2014-11-21 11:13:00 3
_
もちろん、十分に小さい粒度を受け入れる場合は、どの時系列にも共通の頻度がありますが、必要なサイズのdf.asfreq(...)
を使用すると、このトリックが実用的でない場合があります。
_df.apply
_を使用したより一般的なアプローチの例を次に示します。 searchsorted
の呼び出しは、_df['Date']
_がソートされた順序であることに依存していることに注意してください。
_import numpy as np
import pandas as pd
df = pd.read_csv('data', parse_dates=[0], sep=',\s*')
start_dates = df['Date'] - pd.Timedelta(minutes=5)
df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right')
df['end_index'] = np.arange(len(df))
def sum_window(row):
return df['A'].iloc[row['start_index']:row['end_index']+1].sum()
df['rolling_sum'] = df.apply(sum_window, axis=1)
print(df[['Date', 'A', 'rolling_sum']])
_
収量
_ Date A rolling_sum
0 2014-11-21 11:00:00 1 1
1 2014-11-21 11:03:00 4 5
2 2014-11-21 11:04:00 1 6
3 2014-11-21 11:05:00 2 7
4 2014-11-21 11:07:00 4 11
5 2014-11-21 11:08:00 1 8
6 2014-11-21 11:12:00 1 2
7 2014-11-21 11:13:00 2 3
_
_df.asfreq
_トリックと_df.apply
_の呼び出しを比較するベンチマークは次のとおりです。
_import numpy as np
import pandas as pd
df = pd.read_csv('data', parse_dates=[0], sep=',\s*')
def big_df(df):
df = df.copy()
for i in range(7):
dates = df['Date'] + pd.Timedelta(df.iloc[-1]['Date']-df.iloc[0]['Date']) + pd.Timedelta('1 minute')
df2 = pd.DataFrame({'Date': dates, 'A': df['A']})
df = pd.concat([df, df2])
df = df.reset_index(drop=True)
return df
def using_apply():
start_dates = df['Date'] - pd.Timedelta(minutes=5)
df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right')
df['end_index'] = np.arange(len(df))
def sum_window(row):
return df['A'].iloc[row['start_index']:row['end_index']+1].sum()
df['rolling_sum'] = df.apply(sum_window, axis=1)
return df[['Date', 'rolling_sum']]
def using_asfreq():
result = (pd.rolling_sum(
df.set_index(['Date']).asfreq('1T').fillna(0),
window=5, min_periods=1).reindex(df['Date']))
return result
_
_In [364]: df = big_df(df)
In [367]: %timeit using_asfreq()
1000 loops, best of 3: 1.21 ms per loop
In [368]: %timeit using_apply()
1 loops, best of 3: 208 ms per loop
_