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