熊猫,如何避免使用iterrow(如何基于另一个数据框的值将值分配给数据框的新列)

时间:2019-10-23 09:15:31

标签: python pandas merge iteration left-join

我有三个不同的实体:机会帐户活动

我需要以一种特殊的方式将它们结合起来。让我解释一下它们之间的关系:

  • 机会 N-1个帐户
  • 帐户-1-N 活动

另外,值得注意的是

  • 机会具有以下字段:{ opp_id ; opp_date ; acc_id}
  • 活动具有以下字段:{ act_id ; act_date ; acc_id }

我想实现的是,在商机日期之前X天完成的活动数量插入到商机中。

我目前正在这样做:

a_new_df = pd.DataFrame(columns=['acc_id',"opp_id", "opp_date", "act_90", "act_180"])

for index, opp_row in Opportunity.iterrows():
    account = opp_row["acc_id"]
    opportunity = opp_row["opp_id"]
    opp_date = opp_row["opp_date"]
    act_90, act_180 = 0, 0
    for index, act_row in activities_step_7.iterrows():
        if acc == act_row["acc_id"]:
            days = (pd.to_datetime(opp_date) - pd.to_datetime(act_row["act_date"])).days
            if days<=90:
                act_90+=1
            elif days<=180:
                act_180+=1
    events_df = events_df.append({
        "acc_id": account,
        "opp_id":  opportunity,
        "opp_date" : dat,
        "act_90" :  act_90,
        "act_180" : act_180,    
      }, ignore_index=True)

最后,我在机会和这个新df 之间进行了merge()的操作。当然,该操作需要永远的时间。但是,我不知道如何改进它。

主要问题是我需要在“机会”中插入一些统计数据,这些统计信息需要同时包含“机会”和“活动”中的数据,但是我无法事先将它们合并,因为对于每个机会,我都需要考虑多个活动(而您不能这样做)如果您有重复项,则单击左联接)

有什么主意吗?非常感谢!


编辑1

如果这是我的机会表:

    opp_date    acc_id  opp_id
0   05.08.2019  acc1    opp1
1   25.03.2019  acc2    opp2
2   27.08.2019  acc1    opp3
3   02.09.2019  acc1    opp4
4   22.07.2019  acc3    opp5

这是我的活动表:

    acc_id  act_date
0   acc1    25.07.2019
1   acc1    26.07.2019
2   acc1    31.07.2019 
3   acc1    28.07.2019
4   acc1    02.09.2019 
5   acc1    02.09.2019 
6   acc1    31.07.2019 
7   acc1    02.09.2019 
8   acc1    24.07.2019 
9   acc1    25.07.2019 
10  acc2    31.03.2019 
11  acc3    31.07.2019 
12  acc2    24.03.2019 
13  acc3    13.05.2019 
14  acc3    05.02.2019
15  acc3    30.05.2016 
16  acc3    30.11.2017 
17  acc3    11.04.2016 
18  acc3    19.01.2018 
19  acc3    19.01.2018 
20  acc2    24.03.2019 
21  acc1    04.08.2019
22  acc1    20.10.2019

则预期输出为:

    opp_date        acc_id  opp_id      act_90  act_180
0   05.08.2019      acc1    opp1        4       4   
1   25.03.2019      acc2    opp2        0       0   
2   27.08.2019      acc1    opp3        7       8   
3   02.09.2019      acc1    opp4        0       0   
4   22.07.2019      acc3    opp5        2       2   

1 个答案:

答案 0 :(得分:1)

您可以使用一些pandas内置函数来代替for循环。此结果与您在问题中发布的“预期输出”有所不同,但我认为它符合您的描述。

让我们调用您的第一个数据帧df1和第二个数据帧df2
我们可以通过将其写为一个函数并对其进行apply-而不是在行中进行迭代来计算满足您条件的活动数量:

def count_activities(row, act_df, days):
    return (act_df['act_date'].between(row['opp_date'] -pd.Timedelta(days=days), row['opp_date']) 
            & (act_df['acc_id']==row['acc_id'])).sum()

由于我们在上面的函数中进行了计数,因此加入不是问题:

def add_count_activities_column(opp_df, act_df, days):
    return opp_df.join(opp_df.apply(lambda row: count_activities(row,act_df,days), axis=1).rename('act_{}'.format(days)))

结果:

df3 = add_count_activities_column(df1, df2, 90)
df3 = add_count_activities_column(df3, df2, 180)

我的df3

    opp_date    acc_id  opp_id  act_90  act_180
0   2019-05-08  acc1    opp1    4   4
1   2019-03-25  acc2    opp2    2   2
2   2019-08-27  acc1    opp3    7   8
3   2019-02-09  acc1    opp4    3   3
4   2019-07-22  acc3    opp5    2   2

p.s。 -我将opp_id用作索引,df1.set_index('opp_id', inplace=True)