这大约是你的 1.7 倍method_1
稍微整洁一点:
df_expand = pd.DataFrame.from_records(
(
(d, r.type, r.value)
for r in df.itertuples()
for d in pd.date_range(start=r.start, end=r.end, freq='D')
),
columns=['day', 'type', 'row']
)
通过创建自己的日期范围而不是调用,您可以将速度提高约 7 倍pd.date_range()
:
one_day = dt.timedelta(1)
df_expand = pd.DataFrame.from_records(
(
(r.start + i * one_day, r.type, r.value)
for r in df.itertuples()
for i in range(int((r.end-r.start)/one_day)+1)
),
columns=['day', 'type', 'row']
)
或者使用 numpy 可以将速度提高 24 倍arange
生成日期的函数:
one_day = dt.timedelta(1)
df_expand = pd.DataFrame.from_records(
(
(d, r.type, r.value)
for r in df.itertuples()
for d in np.arange(r.start.date(), r.end.date()+one_day, dtype='datetime64[D]')
),
columns=['day', 'type', 'row']
)
我忍不住再添加一个,速度比上一个快两倍多一点。不幸的是,它很难阅读。这根据读数跨越的天数(“dur”)对读数进行分组,然后使用矢量化 numpy 操作在单个批次中扩展每个组。
def expand_group(g):
dur = g.dur.iloc[0] # how many days for each reading in this group?
return pd.DataFrame({
'day': (g.start.values[:,None] + np.timedelta64(1, 'D') * np.arange(dur)).ravel(),
'type': np.repeat(g.type.values, dur),
'value': np.repeat(g.value.values, dur),
})
# take all readings with the same duration and process them together using vectorized code
df_expand = (
df.assign(dur=(df['end']-df['start']).dt.days + 1)
.groupby('dur').apply(expand_group)
.reset_index('dur', drop=True)
)
Update:为了回应您的评论,下面是矢量化方法的简化版本,它更快、更容易阅读。而不是使用groupby
步骤,这使得单个矩阵与最长的读数一样宽,然后过滤掉不需要的条目。除非您的读数的最大持续时间比平均值长得多,否则这应该非常有效。使用测试数据帧(所有读数持续 4 天),这比实际数据快约 15 倍groupby
解决方案,大约快 700 倍method_1
.
dur = (df['end']-df['start']).max().days + 1
df_expand = pd.DataFrame({
'day': (
df['start'].values[:,None] + np.timedelta64(1, 'D') * np.arange(dur)
).ravel(),
'type': np.repeat(df['type'].values, dur),
'value': np.repeat(df['value'].values, dur),
'end': np.repeat(df['end'].values, dur),
})
df_expand = df_expand.loc[df_expand['day']<=df_expand['end'], 'day':'value']