我有一个包含产品合约的数据框(Product_ID
)。这些合约在特定日期(StartDate
)开立,并在某个特定时刻(CloseDate
)关闭。此时合约也可能处于活动状态,因此没有CloseDate。
有多个客户拥有合同,由ID
引用。这些客户在特定时刻填写调查问卷,此时刻由日期(Key_Date
)表示。
我想要计算的是几个功能,但是对于这个例子,我将重点关注独特产品的数量。在填写调查时,我想知道某个客户有多少独特的产品。
我们有一个数据框df_result
,其中包含客户的ID以及他们在调查中填写的日期。在此数据框中,我们还将附加计算出的特征:
import pandas as pd
import numpy as np
np.random.seed(256)
df_result = pd.DataFrame({'ID' : np.random.randint(3, size=(10)),
'Key_Date' : pd.date_range(start=pd.datetime(2015, 5, 21), periods=10, freq='m')})
df_result.head()
ID Key_Date
0 0 2015-05-31
1 2 2015-06-30
2 1 2015-07-31
3 0 2015-08-31
4 1 2015-09-30
我们还有一个包含不同合同/产品的数据框,名为df_products
:
np.random.seed(321)
df_products = pd.DataFrame({'ID' : np.random.randint(5, size=(10)),
'Product_ID' : np.random.randint(low = 101, high = 104, size=10),
'StartDate' : pd.date_range(start=pd.datetime(2015, 3, 1), periods=10, freq='m'),
'CloseDate' : pd.date_range(start=pd.datetime(2016, 1, 1), periods=10, freq='m')})
df_products.head()
CloseDate StartDate ID Product_ID
0 2016-01-31 2015-03-31 4 102
1 2016-02-29 2015-04-30 2 101
2 2016-03-31 2015-05-31 4 102
3 2016-04-30 2015-06-30 1 102
4 2016-05-31 2015-07-31 0 103
我创建了一个函数来计算填写调查的客户的独特产品,其中合同在填写时仍处于活动状态key_date
(因此合同的开始日期({{ 1}})在此日期之前,结束日期(StartDate
)在此日期之后)。我还希望能够在填写日期之前给出一个范围,所以所有在过去一年中都活跃的独特产品,例如。因此,即使是11个月前的封闭合同也将包括在内。我这样做是通过提供一个额外的参数CloseDate
来减去填充日期(创建一个新日期:timeperiod
)。然后,low_date
必须晚于CloseDate
,而不是low_date
。
key_date
在此之后,我将这些值附加到def unique_products(df,timeperiod,ID,key_date):
low_date = key_date - relativedelta(months=timeperiod)
data = df.loc[(df['StartDate'] <= key_date) &
(df['CloseDate'] >= low_date) &
(df['ID'] == ID)].groupby(['ID'], as_index = False)['Product_ID'].nunique().reset_index()
if 'Product_ID' in list(data):
try:
return float(data['Product_ID'])
except:
return np.nan
中名为unique_products
的新列中:
df_result
然而,当将其应用于我的整个日期集时,由于每个调查对必须进行评估,因为它们具有不同的时间,所以它变得非常慢。有没有办法改善这个?
感谢您的任何意见:)
答案 0 :(得分:1)
您需要使用合并。
merged = pd.merged(df_products,df_results,how='left',on='ID')
现在合并将包含df_products的所有列以及“关键日期”,如果它为空,则此人尚未填写调查。
filled_survey = merged.loc[~(merged['Key Date'].isnull())]
现在您可以通过减去相关日期找到timedelta并相应地过滤。
答案 1 :(得分:0)
df_result['low_date'] = df_result['key_date'] - relativedelta(months=timeperiod) #creating low_date column
df_result2 = pandas.merge(df_result,df_products,how = "outer",on = "ID") #Join both the tables
df_result2 = df_result2[(df_result2['StartDate'] <= df_result2['key_date']) & (df_result2['CloseDate'] >= df_result2['low_date'])] # Filter conditions
df_result2 = df_result2.groupby(['ID','Key_Date'])['Product_ID'].nunique().reset_index()
使用交叉连接而不是您正在使用的一种循环尝试一次。