1
votes

Vous voulez trouver le calcul annuel à l'aide de Groupby et postuler pour plusieurs années

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 commentaires

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


3 Réponses :


2
votes

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)


9 commentaires

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.



1
votes

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.


2 commentaires

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é.



1
votes

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'])


1 commentaires

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.