熊猫,我怎样才能避免使用 iterrow (如何根据来自另一个数据帧的值将值分配给数据帧中的新列)

Posted

技术标签:

【中文标题】熊猫,我怎样才能避免使用 iterrow (如何根据来自另一个数据帧的值将值分配给数据帧中的新列)【英文标题】:Pandas, how can I avoid the use of iterrow (how to assign values to a new column in a dataframe based on the values from another dataframe) 【发布时间】:2020-02-19 11:58:57 【问题描述】:

我有三个不同的实体:OpportunityAccountActivity

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

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

另外,值得注意的是

机会 具有以下字段: opp_id ; opp_date ; acc_id Activity 有以下字段: 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)

最后,我在 Opportunity 和这个 new df 之间做了一个merge()。当然,手术需要很长时间。但是,我不知道如何改进这一点。

主要问题是我需要在 Opportunity 中插入一些统计信息,这些统计信息需要同时来自 Opportunity 和 Activity 的数据,但我无法事先合并它们,因为对于每个机会我有多个活动要考虑(而你不能这样做有重复项时的左连接)

有什么想法吗?非常感谢!


编辑 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   

【问题讨论】:

你能添加一些虚拟数据和你得到的输出吗?我相信您的代码中也存在一些拼写错误(最后几行:accoutopportuntiy)。 完成了,这并不容易,但完成了;)感谢您向我展示错别字! 在您的预期输出中,为什么第二行有 0?在该日期前一天,从 acc2 执行了两项操作。 一个操作是在机会日期之后,而第二个操作返回 0 作为天数,可能是因为一些近似值 【参考方案1】:

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

我们将您的第一个数据框称为df1,将您的第二个数据框称为df2。 我们可以通过将其写为函数并apply-ing 来计算有多少活动适合您的条件,而不是在行上迭代:

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

附言- 我会使用opp_id 作为索引,使用df1.set_index('opp_id', inplace=True)

【讨论】:

这至少快 50 倍,不,我不想检查确切的比率。非常感谢!有关如何改进标题的任何建议,以便其他用户可以找到此问题? 类似于“如何根据来自另一个数据帧的值将值分配给数据帧中的新列”?

以上是关于熊猫,我怎样才能避免使用 iterrow (如何根据来自另一个数据帧的值将值分配给数据帧中的新列)的主要内容,如果未能解决你的问题,请参考以下文章

在这个例子中避免使用 iterrows 的好方法是啥?

熊猫两个数据框交叉连接[重复]

具有先前值的 Python Pandas iterrows()

为啥熊猫转换后在csv文件的开头添加数字[重复]

我怎样才能将时间序列数据从昨天移到今天的大熊猫?

如何绘制熊猫数据框的多列