2017-07-25 1 views
1

J'ai une base de données avec des contrats de produits (avec un Product_ID). Ces contrats sont ouverts à une certaine date (StartDate) et fermés à un certain moment (CloseDate). Il est également possible que le contrat soit actif à ce moment-là et qu'il n'ait donc pas de CloseDate.Optimisation de pandas groupby python

Plusieurs clients ont des contrats, référencés par ID. Ces clients remplissent des enquêtes à certains moments dans le temps, ce moment est indiqué par une date (Key_Date). Ce que je veux calculer est un certain nombre de fonctionnalités, mais pour cet exemple, je vais me concentrer sur le nombre de produits uniques. Je veux savoir combien de produits uniques un certain client a, au moment de remplir l'enquête.

Nous avons un dataframe df_result, qui contient l'identifiant du client et la date à laquelle il a été rempli. Dans ce dataframe nous allons également ajouter la fonction calculée:

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 

Nous avons aussi un dataframe avec les différents contrats/produits, du nom 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 

J'ai fait fonction de compter les produits uniques de le client qui a rempli le sondage, où le contrat était encore actif au moment du remplissage, key_date (la date de début du contrat (StartDate) est antérieure à cette date, et la date de fin (CloseDate) est postérieure à cette date) . Je veux aussi être en mesure de donner une gamme avant la date de remplissage, donc tous les produits uniques qui ont été actifs l'année dernière par exemple. Donc même les contrats fermés d'il y a 11 mois seront inclus. Je fais cela en donnant un paramètre supplémentaire timeperiod que je soustrais de la date de remplissage (en faisant une nouvelle date: low_date). Ensuite, le CloseDate devra être postérieur à low_date, au lieu du 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 

Après cela, j'append ces valeurs dans une nouvelle colonne nommée unique_products dans 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 

Cependant lors de l'application à mon entière dateset, il devient assez lent en raison du fait que chaque surveyrow doit être évalué car ils ont des moments différents. Y a-t-il un moyen d'améliorer cela?

Merci pour toute entrée :)

Répondre

1

Vous devez utiliser la fusion.

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

maintenant fusionné aura toutes les colonnes de df_products avec « Date clé », si elle est nulle alors la personne n'a pas rempli l'enquête.

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

Maintenant, vous pouvez trouver le timedelta en soustrayant les dates pertinentes et filtrer en conséquence.

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() 

Essayez ceci une fois en utilisant une jointure croisée au lieu d'une sorte de boucle que vous utilisez.