有没有一种更快的方法来进行熊猫循环?

时间:2020-08-13 08:54:36

标签: python pandas dataframe loops

我的用户案例是检查客户(帐户)在过去30天内是否已经在同一magasin(商店)中购买了东西(如果是),那么将其在该magasin中购买的时间保存在新列中[HAS_BOUGHT_30_DAYS_AGO] 例如我拥有的数据框是这样的:

df=pd.DataFrame({'date':['2019-07-20','2019-07-29','2019-07-03','2019-08-20','2019-08-24','2019-08-05','2019-07-07','2019-06-23'],'account':['0410','0412','0410','0410','0412','0410','0410','0410'],'store':['amazon','carrefour','amazon','zara','carrefour','carrefour','auchan','amazon']})
df['date']=pd.to_datetime(df['date'])
    date       account  store
0   2019-07-20  0410    amazon
1   2019-07-29  0412    carrefour
2   2019-07-03  0410    amazon
3   2019-08-20  0410    zara
4   2019-08-24  0412    carrefour
5   2019-08-05  0410    carrefour
6   2019-07-07  0410    auchan
7   2019-06-23  0410    amazon

我的方法是:

for transaction in df.itertuples():
    delta=transaction.date-df.date
    trans_before=df.loc[(df.account== transaction.account) & (delta.dt.days>0) & (delta.dt.days<30) &(df.store== transaction.store)]
    df.loc[transaction.Index,'HAS_BOUGHT_30_DAYS_AGO']= len(trans_before)

我得到了结果:

    date        account store    HAS_BOUGHT_30_DAYS_AGO
0   2019-07-20  0410    amazon      2.0
1   2019-07-29  0412    carrefour   0.0
2   2019-07-03  0410    amazon      1.0
3   2019-08-20  0410    zara        0.0
4   2019-08-24  0412    carrefour   1.0
5   2019-08-05  0410    carrefour   0.0
6   2019-07-07  0410    auchan      0.0
7   2019-06-23  0410    amazon      0.0

此方法有效,但是对于我的大型数据集(我有14837843行),它花费了很多时间,请问有人有更快的解决方案吗?

2 个答案:

答案 0 :(得分:0)

您的问题出在df.loc中,它可以有效地对数据框进行笛卡尔乘积运算。它为每一行迭代整个数据帧,这意味着时间复杂度实际上为O(n ^ 2)。因此,随着数据的增长,不仅会减慢for循环的速度,而且每次减慢的速度也会变慢。

为使此类查询更有效,您可能希望在(account, date)上建立索引,或对记录进行相应的排序。我假设交易比帐户多,因此这将缩小您要搜索单个帐户的空间,并减少对O(log n)的查找。我不确定如何在Pandas中执行此操作,但是如果您的记忆允许,则可以简单地使用Python列表和字典。

另一种方法是将数据存储在关系数据库中,并使用SQL进行查询(当然是在创建索引之后)。

答案 1 :(得分:0)

使用DataFrame.sort_values对列date上的数据帧的值进行排序。然后在列account, store上使用DataFrame.groupby,并使用自定义Lambda函数date转换列f

f = lambda x: x.diff().dt.days.cumsum().between(0, 30).cumsum()
df['HAS_BOUGHT'] = df.sort_values('date').groupby(['account', 'store'])['date'].apply(f)

结果:

        date account      store  HAS_BOUGHT
0 2019-07-20    0410     amazon           2
1 2019-07-29    0412  carrefour           0
2 2019-07-03    0410     amazon           1
3 2019-08-20    0410       zara           0
4 2019-08-24    0412  carrefour           1
5 2019-08-05    0410  carrefour           0
6 2019-07-07    0410     auchan           0
7 2019-06-23    0410     amazon           0