Панды: выберите строки DF на основе другого DF - самый близкий ответ, который я могу найти на мой вопрос, но я не верю, что он вполне решает его.
В любом случае, я работаю с двумя очень большими панелями данных панд (так что скорость учитывается), 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 строк. Кто-нибудь знает здесь решение, которое является более быстрым и, следовательно, выполнимым?