【发布时间】:2019-11-16 06:41:42
【问题描述】:
Pandas: select DF rows based on another DF 是我能找到的最接近我的问题的答案,但我认为它不能完全解决它。
无论如何,我正在使用两个非常大的 pandas 数据帧(所以速度是一个考虑因素),df_emails 和 df_trips,它们都已经按 CustID 排序,然后按日期排序。
df_emails 包含我们向客户发送电子邮件的日期,如下所示:
CustID DateSent
0 2 2018-01-20
1 2 2018-02-19
2 2 2018-03-31
3 4 2018-01-10
4 4 2018-02-26
5 5 2018-02-01
6 5 2018-02-07
df_trips 包括客户到店的日期和消费金额,如下所示:
CustID TripDate TotalSpend
0 2 2018-02-04 25
1 2 2018-02-16 100
2 2 2018-02-22 250
3 4 2018-01-03 50
4 4 2018-02-28 100
5 4 2018-03-21 100
6 8 2018-01-07 200
基本上,我需要做的是在发送的每封电子邮件之间找出每位客户的旅行次数和总支出。如果这是最后一次为给定客户发送电子邮件,我需要在电子邮件之后但在数据结束之前 (2018-04-01) 找到总旅行次数和总支出。所以最终的数据框看起来像这样:
CustID DateSent NextDateSentOrEndOfData TripsBetween TotalSpendBetween
0 2 2018-01-20 2018-02-19 2.0 125.0
1 2 2018-02-19 2018-03-31 1.0 250.0
2 2 2018-03-31 2018-04-01 0.0 0.0
3 4 2018-01-10 2018-02-26 0.0 0.0
4 4 2018-02-26 2018-04-01 2.0 200.0
5 5 2018-02-01 2018-02-07 0.0 0.0
6 5 2018-02-07 2018-04-01 0.0 0.0
尽管我已尽我所能以 Python/Pandas 友好的方式来实现这一点,但我能够实现的唯一准确的解决方案是通过 np.where、移位和循环。解决方案如下所示:
df_emails["CustNthVisit"] = df_emails.groupby("CustID").cumcount()+1
df_emails["CustTotalVisit"] = df_emails.groupby("CustID")["CustID"].transform('count')
df_emails["NextDateSentOrEndOfData"] = pd.to_datetime(df_emails["DateSent"].shift(-1)).where(df_emails["CustNthVisit"] != df_emails["CustTotalVisit"], pd.to_datetime('04-01-2018'))
for i in df_emails.index:
df_emails.at[i, "TripsBetween"] = len(df_trips[(df_trips["CustID"] == df_emails.at[i, "CustID"]) & (df_trips["TripDate"] > df_emails.at[i,"DateSent"]) & (df_trips["TripDate"] < df_emails.at[i,"NextDateSentOrEndOfData"])])
for i in df_emails.index:
df_emails.at[i, "TotalSpendBetween"] = df_trips[(df_trips["CustID"] == df_emails.at[i, "CustID"]) & (df_trips["TripDate"] > df_emails.at[i,"DateSent"]) & (df_trips["TripDate"] < df_emails.at[i,"NextDateSentOrEndOfData"])].TotalSpend.sum()
df_emails.drop(['CustNthVisit',"CustTotalVisit"], axis=1, inplace=True)
但是,%%timeit 显示仅在上面显示的七行上这需要 10.6 毫秒,这使得该解决方案在我大约 1,000,000 行的实际数据集上几乎不可行。有谁知道这里的解决方案更快,因此可行?
【问题讨论】:
-
@QuangHoang 2018-04-01 是 df_trips 数据的最后/最近日期。因此,在 2018 年 4 月 1 日之后不可能有任何旅行或花费,因此在给每位顾客的最后一封电子邮件的行中,NextDateSentOrEndOfData 将为 2018 年 4 月 1 日。
标签: python pandas numpy dataframe jupyter-notebook