我无法将重采样功能与groupby结合使用。目前,我正在对5000行的数据样本进行8+秒的操作,这完全不符合我的要求。
以数据为字典的垃圾箱: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()
中使用on=
相比,resample
的性能似乎要好一些,所以我现在执行以下操作。虽然它仍然很慢。
我倒退,因为resample
似乎非强制性地将日期降序。
df.set_index('report_date').groupby('isin').resample('M').asfreq()[::-1]
如前所述,具有5000行和大约16列,这需要15秒的时间来运行,因为我需要在两个单独的数据帧上执行此操作。 使用样本数据在pastebin(500行)中,该操作花费了我0.7s的时间,这对我来说太长了,因为我的最终数据将有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.28s,这是一个很大的改进。仍然不是很好。
如何加快速度?还有另一种方法可以做同样的事情吗?
答案 0 :(得分:2)
我想举例说明我尝试找出哪种解决方案产生最大性能的实验,它表明@jsmart是最好的。
我的数据集如下所示(抱歉,我无法粘贴漂亮的表格的屏幕截图):
我的目标是让每个人(orgacom,客户)按工作日对指标进行重新采样。
解决方案1:按分组/按顺序申请
%%time
sol1 = (
to_process.groupby(['orgacom', 'client'], observed=True, )
.apply(lambda x: x.asfreq('B', fill_value=np.nan))
)
CPU时间:用户4min 6s,sys:2.91 s,总计:4min 9s 上场时间:4分9秒
解决方案2:groupby /应用重新索引(自@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时间:用户4min 13s,sys:2.16 s,总计:4min 15s 挂墙时间: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时间:用户1min 46s,sys:4.93 s,总计:1min 51s 挂墙时间:1分51秒
解决方案4:groupby / asfreq重新采样(从@jokab第一种解决方案开始)
%%time
sol4 = to_process.groupby(['orgacom', 'client']).resample('B').asfreq()
CPU时间:用户4min 22s,sys:8.01 s,总计:4min 30s 挂墙时间:4分30秒
答案 1 :(得分:1)
我将25k行测试数据集的执行时间从850毫秒缩短到了320毫秒。我将重新索引逻辑包装在一个函数中,以使计时更容易:
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)
答案 2 :(得分:0)
我还注意到在groupby上重采样可能很慢。就我而言,我使用数据重塑来加快速度,
df.set_index(['isin', 'report_date'])['val'].unstack(0).resample('M')
答案 3 :(得分:0)
还有另一种方法。使用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