J'ai un dataframe comme suit:
df_list_for_yoy = [['USA MARKET', 'APPLE', 'QUARTER', '2020-06-01', 100], ['USA MARKET', 'APPLE', 'YEARLY', '2020-06-01', 1000], ['USA MARKET', 'PEAR', 'QUARTER', '2020-06-01', 200], ['USA MARKET', 'PEAR', 'YEARLY', '2020-06-01', 5000], ['USA MARKET', 'APPLE', 'QUARTER', '2019-06-01', 300], ['USA MARKET', 'APPLE', 'YEARLY', '2019-06-01', 2000], ['USA MARKET', 'PEAR', 'QUARTER', '2019-06-01', 100], ['USA MARKET', 'PEAR', 'YEARLY', '2019-06-01', 3000], ['USA MARKET', 'APPLE', 'QUARTER', '2018-06-01', 300], ['USA MARKET', 'APPLE', 'YEARLY', '2018-06-01', 2000], ['USA MARKET', 'PEAR', 'QUARTER', '2018-06-01', 100], ['USA MARKET', 'PEAR', 'YEARLY', '2018-06-01', 3000], ['UK MARKET', 'WATERMELON', 'QUARTER', '2020-06-01', 200], ['UK MARKET', 'WATERMELON', 'YEARLY', '2020-06-01', 5000], ['UK MARKET', 'GRAPE', 'QUARTER', '2020-06-01', 200], ['UK MARKET', 'GRAPE', 'YEARLY', '2020-06-01', 5000], ['UK MARKET', 'WATERMELON', 'QUARTER', '2019-06-01', 500], ['UK MARKET', 'WATERMELON', 'YEARLY', '2019-06-01', 300], ['UK MARKET', 'GRAPE', 'QUARTER', '2019-06-01', 50], ['UK MARKET', 'GRAPE', 'YEARLY', '2019-06-01', 500], ['UK MARKET', 'WATERMELON', 'QUARTER', '2018-06-01', 500], ['UK MARKET', 'WATERMELON', 'YEARLY', '2018-06-01', 300], ['UK MARKET', 'GRAPE', 'QUARTER', '2018-06-01', 50], ['UK MARKET', 'GRAPE', 'YEARLY', '2018-06-01', 500]] column_names = ['MARKET', 'PRODUCT', 'TIMEPERIOD', 'DATE', 'VALUES'] df_2 = pd.DataFrame(df_list_for_yoy, columns = column_names) df_2['DATE']= pd.to_datetime(df_2['DATE'])
Je veux trouver la différence d'année en année de chaque produit dans chaque marché pour chaque période (c'est une bouchée!) Par exemple, pour le produit APPLE sur le marché américain au cours du trimestre TIMEPERIOD, le taux de croissance 2020-06-01 est simplement (100 -300) / 300 = 66,6% où j'ai utilisé les valeurs de 2020-06-01 moins 2019-06-01 divisées par 2019-06-01.
Comme vous pouvez le voir, le problème avec le code ci-dessous est qu'il ne renvoie que le taux de croissance de l'année en cours par rapport à l'année dernière. Et ignore l'année dernière 2019 par rapport à 2018. J'ai essayé quelques blocs if-else
, mais tous semblent indiquer des erreurs, je l'apprécierais s'il existe des solutions intéressantes pour résoudre ce problème. En bref, mon growth_rate_prev
n'est pas utilisé ici (même si j'ai essayé de l'intégrer mais cela a échoué).
def year_on_year(df): try: curr_year_val = df[df['DATE']==max(df['DATE'])]['VALUES'].sum() prev_year_val = df[df['DATE']==(max(df['DATE'])-relativedelta(months=12))]['VALUES'].sum() prev_prev_year_val = df[df['DATE']==(df(df['DATE'])-relativedelta(months=24))]['VALUES'].sum() growth_rate_curr = ((curr_year_val)-(prev_year_val))/(prev_year_val) growth_rate_prev = ((prev_year_val)-(prev_prev_year_val))/(prev_prev_year_val) except ZeroDivisionError: growth_rate_curr, growth_rate_prev = 0 , 0 return growth_rate_curr def product_growth(applied_group_df): applied_group_df['Year on Year difference'] = year_on_year(applied_group_df) return applied_group_df growth_rate_df = df_2.groupby(["TIMEPERIOD",'MARKET', 'PRODUCT']).apply(product_growth)
Au cas où quelqu'un voudrait reproduire le code, vous pouvez créer le df en utilisant le code ci-dessous:
MARKET PRODUCT TIMEPERIOD DATE VALUES 0 USA MARKET APPLE QUARTER 2020-06-01 100 1 USA MARKET APPLE YEARLY 2020-06-01 1000 2 USA MARKET PEAR QUARTER 2020-06-01 200 3 USA MARKET PEAR YEARLY 2020-06-01 5000 4 USA MARKET APPLE QUARTER 2019-06-01 300 5 USA MARKET PEAR YEARLY 2019-06-01 2000 6 USA MARKET PEAR QUARTER 2019-06-01 100 7 USA MARKET PEAR YEARLY 2019-06-01 3000 8 USA MARKET APPLE QUARTER 2018-06-01 300 9 USA MARKET PEAR YEARLY 2018-06-01 2000 10 USA MARKET PEAR QUARTER 2018-06-01 100 11 USA MARKET PEAR YEARLY 2018-06-01 3000 12 UK MARKET WATERMELON QUARTER 2020-06-01 200 13 UK MARKET WATERMELON YEARLY 2020-06-01 5000 14 UK MARKET GRAPE QUARTER 2020-06-01 200 15 UK MARKET GRAPE YEARLY 2020-06-01 5000 16 UK MARKET WATERMELON QUARTER 2019-06-01 500 17 UK MARKET WATERMELON YEARLY 2019-06-01 300 18 UK MARKET GRAPE QUARTER 2019-06-01 50 19 UK MARKET GRAPE YEARLY 2019-06-01 500 20 UK MARKET WATERMELON QUARTER 2018-06-01 500 21 UK MARKET WATERMELON YEARLY 2018-06-01 300 22 UK MARKET GRAPE QUARTER 2018-06-01 50 23 UK MARKET GRAPE YEARLY 2018-06-01 500
3 Réponses :
Vous pouvez utiliser itertools.combinations
pour obtenir la combinaison année-année, ainsi que d'autres manipulations à l'intérieur d'une fonction à appliquer dans les groupes, comme ceci:
Annual Reference Annual Growth (%) TIMEPERIOD MARKET PRODUCT QUARTER UK MARKET GRAPE 0 2019-2020 300 1 2018-2019 0 WATERMELON 0 2019-2020 -60 1 2018-2019 0 USA MARKET APPLE 0 2019-2020 -66.67 1 2018-2019 0 PEAR 0 2019-2020 100 1 2018-2019 0 YEARLY UK MARKET GRAPE 0 2019-2020 900 1 2018-2019 0 WATERMELON 0 2019-2020 1566.67 1 2018-2019 0 USA MARKET APPLE 0 2019-2020 -50 1 2018-2019 0 PEAR 0 2019-2020 66.67 1 2018-2019 0
Production:
import numpy as np import pandas as pd from itertools import combinations def get_annual_growth(grp): # Get all possible combination of the years from dataset year_comb_lists = np.sort([sorted(comb) for comb in combinations(grp.DATE.dt.year, 2)]) # Remove those combinations in which difference is greather than 1 (for example, 2018-2020) year_comb_lists = year_comb_lists[(np.diff(year_comb_lists) == 1).flatten()] # comment this line if it's not the case # Get year-combination labels year_comb_strings = ['-'.join(map(str, comb)) for comb in year_comb_lists] # Create sub-dataframe with to be concated afterwards by pandas `groupby` subdf = pd.DataFrame(columns=['Annual Reference', 'Annual Growth (%)']) for i,years in enumerate(year_comb_lists): # for each year combination ... actual_value, last_value = grp[grp.DATE.dt.year==years[1]].VALUES.mean(), grp[grp.DATE.dt.year==years[0]].VALUES.mean() growth = (actual_value - last_value) / last_value # calculate the annual growth subdf.loc[i, :] = [year_comb_strings[i], growth] return subdf df_2.groupby(['TIMEPERIOD','MARKET', 'PRODUCT']).apply(get_annual_growth)
Merci, ça a l'air bien aussi! Ne jamais utiliser les choses à partir de combinaisons
C'est bien parce que vous n'avez pas à vous soucier des combinaisons, c'est escaladable. btw, très intéressant votre problème! haha
Oui, un problème intéressant en effet, quelque chose de facile à faire dans Excel peut être assez difficile chez les pandas. En fait, je n'ai pas encore tout à fait les colonnes 0, 1, 0, 1
Juste pour clarifier, la partie où vous avez écrit VALUES.mean()
, la mean()
est purement là pour convertir la valeur de la série en valeur flottante, n'est-ce pas?
Pas seulement à cela. Je ne sais pas si vous avez plus de valeurs par an dans votre ensemble de données complet. Si vous n'avez qu'une seule donnée par catégorie comme la date que vous avez fournie, .mean()
ne fait rien d'autre que de prendre la valeur telle .mean()
. Mais si vous avez plus de dates par catégorie (par exemple, 2020-06-01 et 2020-10-01), il calcule la moyenne. Dans de telles circonstances, il serait quelque peu nécessaire d'appliquer une fonction d'agrégation afin de fournir une sortie comme celle-ci.
Concernant les «colonnes» 0, 1, il ne s'agit pas d'index de colonnes, ce sont les index des années. Notez que j'obtiens d'abord les combinaisons année-année dans un tableau avec des listes triées, comme celles-ci: [[2019, 2020], [2018, 2019]]
. Ensuite, nous itérons dans chacune de ces listes, où les index 0 et 1 correspondent respectivement aux années 2019 et 2020 de la première liste triée.
Je recommande d'analyser ce que fait chaque ligne séparément. Pandas groupby
sous-ensemble automatiquement le dataframe pour nous avec les groupes cibles. On peut simuler l'un de ces groupes avec: grp = df_2[(df_2.TIMEPERIOD=='QUARTER') & (df_2.MARKET=='USA MARKET') & (df_2.PRODUCT=='APPLE')]
pour que grp
représente un groupe. Ensuite, vous pouvez exécuter chaque ligne de la fonction séparément, en vérifiant sa sortie.
Merci pour la réponse détaillée, j'ai parcouru votre code ligne par ligne et apporté quelques modifications dans ma propre réponse ci-dessous. Jetez un œil si vous voulez :)
Cool! Vos propres modifications seront nécessaires. Je suis content que vous ayez bien compris. Meilleur.
Veuillez trouver cette approche.
MARKET TIMEPERIOD PRODUCT VALUES 0 UK MARKET QUARTER GRAPE [200, 50, 50] 1 UK MARKET QUARTER WATERMELON [200, 500, 500] ....
C'est un code généralisé qui devrait fonctionner pour toutes les années précédentes remontant à 2013, comme mentionné dans le commentaire.
Impressions:
df_2.groupby(['MARKET','TIMEPERIOD','PRODUCT'])['VALUES'].apply(list).reset_index()
Explication:
Tout d'abord, je fais un groupe par sur les valeurs et je les mets dans une liste:
MARKET TIMEPERIOD PRODUCT 2020-Growth 2019-Growth 0 UK MARKET QUARTER GRAPE 300.00 0.0 1 UK MARKET QUARTER WATERMELON 60.00 0.0 2 UK MARKET YEARLY GRAPE 900.00 0.0 3 UK MARKET YEARLY WATERMELON 1566.67 0.0 4 USA MARKET QUARTER APPLE 66.67 0.0 5 USA MARKET QUARTER PEAR 100.00 0.0 6 USA MARKET YEARLY APPLE 50.00 0.0 7 USA MARKET YEARLY PEAR 66.67 0.0
par exemple
df = df_2.groupby(['MARKET','TIMEPERIOD','PRODUCT'])['VALUES'].apply(list).reset_index() def func(x): year = 2021 for i in range(1,len(x['VALUES'])): colname = str(year-i) + '-Growth' x[colname] = round(abs(x['VALUES'][i]- x['VALUES'][i-1])/x['VALUES'][i]*100,2) return x df = df.apply(lambda x: func(x), axis=1).drop('VALUES',axis=1) print(df)
Ensuite, j'écris une boucle Apply to à travers la colonne de liste 'VALUES' et je fais le calcul de croissance.
Cela semble bien, mais pensez-vous que nous devrions également groupby Market
Ouais, c'est vrai. J'ai oublié que vous vouliez aussi cela par «MARKET». Vous pouvez simplement ajouter «MARKET» à groupby comme vous l'avez mentionné.
J'ai apporté quelques modifications à la réponse de Caina Max pour tenir compte de mes données réelles, où il y a plusieurs mois dans une année. Il peut y avoir 2020-06-01, 2020-03-01, 2019-12-01 etc. et par conséquent, je dois apporter les modifications ci-dessous pour obtenir les paires de combinaisons des dates qui sont espacées d'un an exactement, à savoir, [2019- 06-01, 2020-06-01], [2019-03-01, 2020-03-01], [2018-12-01, 2019-12-01] etc. etc.
import numpy as np import pandas as pd from itertools import combinations def get_annual_growth(grp): # Get all possible combination of the years from dataset year_comb_lists = np.sort([sorted(comb) for comb in combinations(grp.DATE, 2)]) new_year_comb_lists = [comb_dates for comb_dates in year_comb_lists if comb_dates[0]==comb_dates[1]-relativedelta(months=12)] # Get year-combination labels year_comb_strings = [comb[1] for comb in new_year_comb_lists] # Create sub-dataframe with to be concated afterwards by pandas `groupby` subdf = pd.DataFrame(columns=['Annual Reference', 'Annual Growth (%)']) for i,years in enumerate(new_year_comb_lists ): # for each year combination ... actual_value, last_value = grp[grp['Date']==years[1]].Values.mean(), grp[grp['Date']==years[0]].Values.mean() growth = (actual_value - last_value) / last_value # calculate the annual growth subdf.loc[i, :] = [year_comb_strings[i], growth] return subdf df_2.groupby(['TIMEPERIOD','MARKET', 'PRODUCT']).apply(get_annual_growth) df_2= df_2.reset_index() df_2['Annual_Reference'] = pd.to_datetime(df_2['Annual_Reference'])
Bien! Bien que je pense que vous devrez parcourir new_year_comb_lists
au lieu de year_comb_lists
pour travailler, puisque vous avez créé year_comb_strings
sur cette base.
Juste à noter:
(100-300)/300
équivaut à une "croissance négative" de ~ 66,6%.Doit-on supposer que le dataframe n'a que les valeurs 2020, 2019 et 2018 ou pourrait-il en avoir plus?
@sharathnatraj Il pourrait en avoir plus, dans mes données réelles, il en a jusqu'en 2013