如何根据两个不同列的日期获取交叉连接table的唯一记录时的逻辑speed/memory?
How improve speed/memory of logic when obtaining the unique record of a cross joined table based on the dates of two different columns?
我之前问过如何根据两个不同列的日期获取交叉连接 table 的唯一记录?这里:
尽管提供的答案有效:
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-rank
或 np.where
?
您可以通过使用所有向量化 Pandas 操作(Pandas 内置函数)来加速该过程,这些操作已被 Pandas 优化为快速 运行将 .apply()
与 Pandas 无法优化且 运行 缓慢的自定义函数一起使用。
我们首先将R_DATE_TESTED
和R_RESULT
的值替换为Series.where()
的NaN
。然后,使用groupby()
+ last()
得到需要的聚合条目,如下:
- 如果 R_DATE_TESTED < R_DATE_ENCOUNTER 不成立,则将
R_DATE_TESTED
和 R_RESULT
的值替换为 NaN
,使用 by 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'])
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.5 毫秒减少到 4.24 毫秒)
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 秒即可完成处理。
由于放大的数据集大部分是重复数据,运行时间可能与真实数据的情况不成线性比例。无论如何,这表明 运行 新解决方案的时间对于大型数据集仍然是合理的。
我之前问过如何根据两个不同列的日期获取交叉连接 table 的唯一记录?这里:
尽管提供的答案有效:
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-rank
或 np.where
?
您可以通过使用所有向量化 Pandas 操作(Pandas 内置函数)来加速该过程,这些操作已被 Pandas 优化为快速 运行将 .apply()
与 Pandas 无法优化且 运行 缓慢的自定义函数一起使用。
我们首先将R_DATE_TESTED
和R_RESULT
的值替换为Series.where()
的NaN
。然后,使用groupby()
+ last()
得到需要的聚合条目,如下:
- 如果 R_DATE_TESTED < R_DATE_ENCOUNTER 不成立,则将
R_DATE_TESTED
和R_RESULT
的值替换为NaN
,使用 bySeries.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'])
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.5 毫秒减少到 4.24 毫秒)
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 秒即可完成处理。
由于放大的数据集大部分是重复数据,运行时间可能与真实数据的情况不成线性比例。无论如何,这表明 运行 新解决方案的时间对于大型数据集仍然是合理的。