Рассмотрим решение SQL, поскольку ваша логика преобразуется в следующий запрос с коррелированным подзапросом агрегирования (что, по общему признанию, также является дорогостоящим типом запроса, поскольку агрегаты выполняются для каждой внешней строки запроса, аналогично пандам apply
loop).
SELECT l.*,
(SELECT AVG([APF_AMOUNT]) FROM recharge_df r
WHERE r.[APF_DATE] >= date(l.[APF_DATE], '-90 day')
AND r.[APF_DATE] < l.[APF_DATE]
AND r.[APF_MSISDN] = l.[APF_MSISDN]) AS mean_recharge_90
FROM loan_df l
В пандах вы можете использовать модуль pandasql
, который запускает экземпляр в памяти для SQLite:
from pandasql import sqldf
pysqldf = lambda q: sqldf(q, globals())
sql = """SELECT l.*,
(SELECT AVG([APF_AMOUNT]) FROM recharge_df r
WHERE r.[APF_DATE] >= date(l.[APF_DATE], '-90 day')
AND r.[APF_DATE] < l.[APF_DATE]
AND r.[APF_MSISDN] = l.[APF_MSISDN]) AS mean_recharge_90
FROM loan_df l"""
output_df = pysqldf(q)
Ниже приведена расширенная версия, котораяработает под капотом pandasql
, взаимодействуя с SQLAlchemy и вызовами импорта / экспорта панд: read_sql
и to_sql
.
from sqlalchemy import create_engine
# IN-MEMORY DATABASE (NO PATH SPECIFIED)
engine = create_engine('sqlite://')
# EXPORT DATAFRAMES
recharge_df.to_sql("recharge_tbl", con=engine, if_exists='replace')
loan_df.to_sql("loan_tbl", con=engine, if_exists='replace')
sql = """SELECT l.*,
(SELECT AVG([APF_AMOUNT]) FROM recharge_tbl r
WHERE r.[APF_DATE] >= date(l.[APF_DATE], '-90 day')
AND r.[APF_DATE] < l.[APF_DATE]
AND r.[APF_MSISDN] = l.[APF_MSISDN]) AS mean_recharge_90
FROM loan_tbl l"""
# IMPORT QUERY RESULT
output_df = pd.read_sql(strSQL, engine)
# IN-MEMORY DATABASE DESTROYED
engine.dispose()