我有一个带有产品合同(带有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(因此合同的开始日期(StartDate)早于此)日期,并且结束日期(CloseDate)在此日期之后)。我还希望能够在填写日期之前给出范围,例如,在过去一年中一直活跃的所有独特产品。因此,即使11个月前的已关闭合同也将包括在内。我通过提供一个额外的参数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的新列中:

df_result['unique_products'] = df_result.apply(lambda row: unique_products(df_products, 3, row['ID'], row['Key_Date']), axis=1)
df_result.head()


    ID  Key_Date    unique_products
0   0   2015-05-31  NaN
1   2   2015-06-30  1.0
2   1   2015-07-31  1.0
3   0   2015-08-31  1.0
4   1   2015-09-30  2.0


但是,将其应用于我的整个日期集时,由于必须对每个调查行进行评估,因为它们具有不同的时间,因此它变得相当慢。有什么办法可以改善这一点?

谢谢你的任何投入

最佳答案

您需要使用合并。

merged = pd.merged(df_products,df_results,how='left',on='ID')


现在,合并后的数据将包含df_products的所有列以及“关键日期”,如果为空,则表示该人尚未填写调查。

filled_survey = merged.loc[~(merged['Key Date'].isnull())]


现在,您可以通过减去相关日期来找到时间增量并相应地进行过滤。

关于python - 通过python优化 Pandas 分组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45302034/

10-09 03:08