[英]Pandas groupby resample poor performance
我在與 groupby 結合使用 resample 函數時遇到了問題。 我正在做的操作目前在 5000 行的數據樣本上需要 8 秒以上的時間,這對於我的要求來說是完全不合理的。
Pastebin 以數據為字典: https : //pastebin.com/RPNdhXsy
我有一個季度間隔的日期數據,我想按列分組,然后每月重新采樣組內的日期。
Input:
isin report_date val
SE001 2018-12-31 1
SE001 2018-09-30 2
SE001 2018-06-31 3
US001 2018-10-31 4
US001 2018-07-31 5
Output:
isin report_date val
SE001 2018-12-31 1
2018-11-30 NaN
2018-10-31 NaN
2018-09-30 2
2018-08-31 NaN
2018-07-31 NaN
2018-06-30 3
US001 2018-10-30 4
2018-09-31 NaN
2018-08-31 NaN
2018-07-31 5
我曾經有過這樣的操作:
df.groupby('isin').resample('M', on="report_date").first()[::-1]
由於似乎asfreq()
性能比在resample
使用on=
略好,因此我目前執行以下操作。 不過還是很慢。 我反轉,因為resample
似乎非可選地對日期進行降序排序。
df.set_index('report_date').groupby('isin').resample('M').asfreq()[::-1]
如前所述,5000 行和大約 16 列這需要 15 秒才能運行,因為我需要在兩個單獨的數據幀上進行。 使用 pastebin 中的示例數據(500 行),操作需要 0.7 秒,這對我來說太長了,因為我的最終數據將有 80 萬行。
當前方式
setindex --- 0.001055002212524414 seconds ---
groupby --- 0.00033092498779296875 seconds ---
resample --- 0.004662036895751953 seconds ---
asfreq --- 0.8990700244903564 seconds ---
[::-1] --- 0.0013098716735839844 seconds ---
= 0.9056s
老辦法
groupby --- 0.0005779266357421875 seconds ---
resample --- 0.0044629573822021484 seconds ---
first --- 1.6829369068145752 seconds ---
[::-1] --- 0.001600027084350586 seconds ---
= 1.6894s
由此看來,從pandas.core.resample.DatetimeIndexResamplerGroupby
轉換為 df 似乎需要很長時間。 怎么辦?
df.set_index('report_date').groupby('isin').apply(lambda x: x.reindex(pd.date_range(x.index.min(), x.index.max(), freq='M'), fill_value=0))[::-1]
這需要 0.28 秒,這是一個巨大的改進。 不過還是不太好。
我怎樣才能加快速度? 有沒有另一種方法可以做同樣的事情?
我將 25k 行測試數據集的執行時間從 850 毫秒縮短到 320 毫秒。 我將 reindex 邏輯包裝在一個函數中,以簡化計時:
def orig_pipeline(df):
return (df
.set_index('report_date')
.groupby('isin')
.apply(lambda x: x.reindex(pd.date_range(x.index.min(),
x.index.max(),
freq='M'),
fill_value=0))
[::-1])
然后,我創建了新函數來使日期算術和重新索引更快:
def create_params(df):
return (df.groupby('isin')['report_date']
.agg(['min', 'max']).sort_index().reset_index())
def create_multiindex(df, params):
all_dates = pd.date_range(start='1999-12-31', end='2020-12-31', freq='M')
midx = (
(row.isin, d)
for row in params.itertuples()
for d in all_dates[(row.min <= all_dates) & (all_dates <= row.max)])
return pd.MultiIndex.from_tuples(midx, names=['isin', 'report_date'])
def apply_mulitindex(df, midx):
return df.set_index(['isin', 'report_date']).reindex(midx)
def new_pipeline(df):
params = create_params(df)
midx = create_multiindex(df, params)
return apply_mulitindex(df, midx)
新舊管道給出相同的結果(除了可能的排序順序):
v1 = orig_pipeline(df).drop(columns='isin').sort_index()
v2 = new_pipeline(df).sort_index().fillna(0)
assert(v1 == v2).all().all()
計時結果:
%%timeit
v1 = orig_pipeline(df_big)
854 ms ± 2.72 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit
v2 = new_pipeline(df_big)
322 ms ± 5.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
我想舉例說明我試圖找出哪種解決方案產生最高性能的實驗,它表明 @jsmart 是最好的。
我的數據集如下(對不起,我無法粘貼漂亮的表格):
我的目標是為每個(orgacom、客戶)結合按工作日重新采樣的指標。
解決方案1:groupby / apply asfreq
%%time
sol1 = (
to_process.groupby(['orgacom', 'client'], observed=True, )
.apply(lambda x: x.asfreq('B', fill_value=np.nan))
)
CPU 時間:用戶 4 分 6 秒,系統:2.91 秒,總計:4 分 9 秒掛牆時間:4 分 9 秒
解決方案 2:groupby / apply reindex(從 @jokab EDIT2 開始)
%%time
sol2 = (
to_process.groupby(['orgacom', 'client'], observed=True, )
.apply(lambda x: x.reindex(pd.date_range(x.index.min(), x.index.max(), freq='B'), fill_value=np.nan))
)
CPU 時間:用戶 4 分 13 秒,系統:2.16 秒,總計:4 分 15 秒掛牆時間:4 分 15 秒
解決方案 3:重新編碼重新采樣(從@jsmart 回答開始)
def create_params(df):
return (df.reset_index().groupby(['orgacom', 'client'], observed=True, )['date']
.agg(['min', 'max']).sort_index().reset_index())
def create_multiindex(df, params):
all_dates = pd.date_range(start='2016-12-31', end='2020-12-31', freq='B')
midx = (
(row.orgacom, row.client, d)
for row in params.itertuples()
for d in all_dates[(row.min <= all_dates) & (all_dates <= row.max)])
return pd.MultiIndex.from_tuples(midx, names=['orgacom', 'client', 'date'])
def apply_mulitindex(df, midx):
return df.set_index(['orgacom', 'client', 'date']).reindex(midx)
def new_pipeline(df):
params = create_params(df)
midx = create_multiindex(df, params)
return apply_mulitindex(df, midx)
%%time
sol3 = new_pipeline(to_process.reset_index())
CPU 時間:用戶 1 分 46 秒,系統:4.93 秒,總計:1 分 51 秒掛牆時間:1 分 51 秒
解決方案 4:groupby / resample asfreq(從@jokab 第一個解決方案開始)
%%time
sol4 = to_process.groupby(['orgacom', 'client']).resample('B').asfreq()
CPU 時間:用戶 4 分 22 秒,系統:8.01 秒,總計:4 分 30 秒掛牆時間:4 分 30 秒
我還注意到在 groupby 上重新采樣可能很慢。 就我而言,我使用數據整形來加速,
df.set_index(['isin', 'report_date'])['val'].unstack(0).resample('M')
還有另一種方法可以做到這一點。 使用 itertools.groupby() 和列表理解
import time
from itertools import groupby
print(time.time())
data = (
('SE001', '2018-12-31', 1),
('SE001', '2018-09-30', 2),
('SE001', '2018-06-31', 3),
('US001', '2018-10-31', 4),
('US001', '2018-07-31', 5),
)
aggr = [(key, sum([g[2] for g in grp])) for key, grp in groupby(sorted(data), key=lambda x: x[0])]
print(aggr)
print(time.time())
# 100,000 records
# 2.5 seconds
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.