Python 如何加速这个嵌套循环(按日期索引?)
数据集如下所示:Python 如何加速这个嵌套循环(按日期索引?),python,datetime,Python,Datetime,数据集如下所示: 电子邮件事件\u日期事件\u类型 0 4867784685125632 2015-10-26 21:38:03.911350已交付 15352432066363392 2015-10-26 21:37:57.871980交付 2 6024938649550848 2015-10-26 21:37:57.853210购买 3 6191500064980992 2015-10-26 21:37:58.867800已交付 4 4867784685125632 2015-10-28 2
电子邮件事件\u日期事件\u类型
0 4867784685125632 2015-10-26 21:38:03.911350已交付
15352432066363392 2015-10-26 21:37:57.871980交付
2 6024938649550848 2015-10-26 21:37:57.853210购买
3 6191500064980992 2015-10-26 21:37:58.867800已交付
4 4867784685125632 2015-10-28 21:37:56.331130购买
实际上,有许多行共享一个哈希电子邮件(email)值。对于event type=delivered的每一行,我需要计算event_type=purchase共享相同电子邮件地址并在原始行中日期后5天内发生的行数
我已经找到了一种方法来实现这一点,我将此df拆分为单独的交付和购买的数据帧,然后使用嵌套循环来搜索这两个数据帧——但这确实是低效的,而且需要花费很长时间
attributed_采购=[]
计数=0
对于idx_e,delivered.iterrows()中的第_e行:
purch=0
rev=0
对于购买的.iterrows()中的idx\u p行,请执行以下操作:
如果已送达。loc[idx_e,'email']!=已购买。loc[idx_p,'电子邮件']:
通过
elif(purchased.loc[idx_p,'事件日期']>=Delived.loc[idx_e,'事件日期'])和purchased.loc[idx_p,'事件日期']在不了解更多具体要求的情况下很难实现,但有一些高级建议-
在内部循环中找到购买数据后,使用break
,这样就不会不必要地处理所有剩余的项目
在交付完成时清理采购列表,使其随着时间的推移而缩小,并且外部循环的未来迭代不会处理已经归属的项目
尽管如此,根据我的评论,我仍然认为简单的单循环方法会更有效率,要处理一封电子邮件中的多个可能重叠的购买,您只需要将它们存储为一个列表(defaultdict(list)
用于方便),并在运行时管理这些列表。这也确保了一次交付不会满足多次购买,尽管如果需要,只需将整个try
块更改为count+=bool(挂起[ehash])
导入日期时间
从集合导入defaultdict
电子邮件=((e[0],datetime.datetime.strtime(e[1],“%Y-%m-%d”),e[2])
对于e-in(
(1,'2019-01-01','delivered'),#忽略,未事先购买
(1,'2019-01-02,'purchase'),
(1、‘2019-01-03’、‘购买’,
(1,'2019-01-04','delivered'),#匹配[1],计数==1
(1、‘2019-01-05’、‘购买’,
(1,'2019-01-06','delivered'),#匹配[2],计数==2
(1,'2019-01-20','delivered')#忽略,距离上次购买时间太长
)
)
计数=0
挂起=默认dict(列表)
对于已排序(电子邮件,key=lambda e:e[1])中的(ehash、日期、状态):
#记录等待交货的采购
如果状态==“购买”:
挂起[ehash]。追加(日期)
elif状态==“已交付”:
#清除此电子邮件超过5天的任何购买
挂起的[ehash]=[p_日期对于挂起的[ehash]中的p_日期
如果p_date>date-datetime.timedelta(天=5)]
#然后是下一个最旧的(为什么要这样分割数据并首先使用嵌套循环?如果只循环最旧最新的数据,则无需任何内部循环即可满足要求。跟踪哪个purchase
尚未在dict{email:date purchased}中交付匹配的
,然后当您找到交付时,del
从该目录中删除它。如果该交付恰好在5天内,则增加计数器。如果有任何完全未交付的,它们将在目录中累积,但您始终可以每5000个循环对其执行一次清理操作,或者根据需要执行任何有意义的操作。问题在于一个电子邮件地址可能会有许多与之相关的购买,因此我不能将散列的电子邮件用作字典键。有什么想法吗?
import datetime
from collections import defaultdict
emails = ((e[0], datetime.datetime.strptime(e[1], '%Y-%m-%d'), e[2])
for e in (
(1, '2019-01-01', 'delivered'), # ignored, no prior purchase
(1, '2019-01-02', 'purchase'),
(1, '2019-01-03', 'purchase'),
(1, '2019-01-04', 'delivered'), # matches [1], count == 1
(1, '2019-01-05', 'purchase'),
(1, '2019-01-06', 'delivered'), # matches [2], count == 2
(1, '2019-01-20', 'delivered') # ignored, too long since last purchase
)
)
count = 0
pending = defaultdict(list)
for (ehash, date, status) in sorted(emails, key=lambda e: e[1]):
# record a purchase awaiting delivery
if status == 'purchase':
pending[ehash].append(date)
elif status == 'delivered':
# purge any purchases for this email > 5days old
pending[ehash] = [p_date for p_date in pending[ehash]
if p_date > date - datetime.timedelta(days=5)]
# then the next oldest (<5days) also deleted, and increments the count
try:
del pending[ehash][0]
count += 1
except IndexError:
pass # No valid purchase for this delivery
print(count)