在基于两个不同列的日期获取交叉连接表的唯一记录时,如何提高逻辑的速度/内存?



我之前问过如何根据两个不同列的日期获取交叉连接表的唯一记录?上一个问题,上下文和答案

虽然提供的答案有效:

import numpy as np
# df - your DataFrame
group = df.groupby(['P_CLIENT_ID', 'P_DATE_ENCOUNTER'])
def foo(df):
result = df.loc[df.P_DATE_ENCOUNTER>df.R_DATE_TESTED, ['R_DATE_TESTED', 'R_RESULT']].tail(1).reset_index()
if not result.empty:
return result
else:
return pd.DataFrame([[np.nan, np.nan, np.nan]], columns=['RECORD_ID','R_DATE_TESTED', 'R_RESULT'])

group.apply(foo)

我的实际数据帧有+- 150万行,这个.apply需要两个小时的时间,并在笔记本电脑上使用大量内存,最终崩溃。是否有一个内存效率应用相同的逻辑?我想也许用dense-ranknp.where?

你可以使用所有矢量化的Pandas操作(Pandas内置函数)来加速这个过程,而不是使用.apply()和自定义函数,Pandas无法优化和运行缓慢。

我们首先用Series.where()替换R_DATE_TESTEDR_RESULTNaN的值。然后,使用groupby()+last()获取所需的聚合条目,如下所示:

  1. 如果r_date_testing <将R_DATE_TESTEDR_RESULT的值替换为NaNR_DATE_ENCOUNTER不为真,通过Series.where():>
df['R_DATE_TESTED'] = df['R_DATE_TESTED'].where(df['R_DATE_TESTED'] < df['P_DATE_ENCOUNTER'])
df['R_RESULT'] = df['R_RESULT'].where(df['R_DATE_TESTED'] < df['P_DATE_ENCOUNTER'])
  1. 使用groupby()+last()获取最近的每个组中的(和非nan)条目:
df.groupby(['P_CLIENT_ID', 'P_DATE_ENCOUNTER'], as_index=False).last()

输出:

P_CLIENT_ID P_DATE_ENCOUNTER  RECORD_ID  R_CLIENT_ID R_DATE_TESTED  R_RESULT
0        25835       2016-12-21     302956      25835.0          None       NaN
1        25835       2017-02-21     302963      25835.0          None       NaN
2        25835       2017-04-25     302970      25835.0    2017-03-07      20.0
3        25835       2017-06-21     302977      25835.0    2017-03-07      20.0
4        25835       2017-09-04     302984      25835.0    2017-08-03      20.0
5        25835       2018-01-08     302991      25835.0    2017-08-03      20.0
6        25835       2018-04-03     302998      25835.0    2018-03-23      20.0
7        25835       2018-07-25     303005      25835.0    2018-03-23      20.0

执行时间基准测试

1。示例数据大小:56行

旧的解决方案:

%%timeit
group = df.groupby(['P_CLIENT_ID', 'P_DATE_ENCOUNTER'])
def foo(df):
result = df.loc[df.P_DATE_ENCOUNTER>df.R_DATE_TESTED, ['R_DATE_TESTED', 'R_RESULT']].tail(1).reset_index()
if not result.empty:
return result
else:
return pd.DataFrame([[np.nan, np.nan, np.nan]], columns=['RECORD_ID','R_DATE_TESTED', 'R_RESULT'])

group.apply(foo)

16.5 ms ± 94.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

新解决方案:

%%timeit
df['R_DATE_TESTED'] = df['R_DATE_TESTED'].where(df['R_DATE_TESTED'] < df['P_DATE_ENCOUNTER'])
df['R_RESULT'] = df['R_RESULT'].where(df['R_DATE_TESTED'] < df['P_DATE_ENCOUNTER'])
df.groupby(['P_CLIENT_ID', 'P_DATE_ENCOUNTER'], as_index=False).last()
4.24 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

对于56行的样本数据大小,新的解决方案快了近4倍(从16.5ms减少到4.24ms)

2。扩大数据大小:168万行

df2 = pd.concat([df] * 30000, ignore_index=True)
df2.shape
(1680000, 6)       # 1.68 million rows

新解决方案:

%%timeit
df2['R_DATE_TESTED'] = df2['R_DATE_TESTED'].where(df2['R_DATE_TESTED'] < df2['P_DATE_ENCOUNTER'])
df2['R_RESULT'] = df2['R_RESULT'].where(df2['R_DATE_TESTED'] < df2['P_DATE_ENCOUNTER'])
df2.groupby(['P_CLIENT_ID', 'P_DATE_ENCOUNTER'], as_index=False).last()
473 ms ± 7.37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

新方案完成处理时间小于1秒。

由于扩大的数据集大多是重复的数据,因此运行时间可能与实际数据的情况不成线性比例。无论如何,这表明新解决方案的运行时间对于大型数据集来说仍然是合理的。

最新更新