Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何加速这个嵌套循环(按日期索引?)_Python_Datetime - Fatal编程技术网

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)