Répéter l'analyse de cohorte Power BI à l'aide de Python

Bonjour, chers lecteurs! La raison de la rédaction de cette publication était un webinaire que j'ai consulté sur Youtube. Il s'est consacré à une analyse de cohorte des ventes. L'auteur a utilisé la plateforme Power BI Desktop pour travailler avec des données. Je ne fournirai pas de lien vers la vidéo spécifiée afin que cet article ne soit pas considéré comme une publicité, mais au cours de la narration j'essaierai de faire des spoilers à la source d'origine afin de mieux expliquer la logique de ma propre décision. Ce webinaire m'a donné l'idée qu'il serait intéressant de répéter les possibilités des formules DAX avec les fonctions de la bibliothèque Pandas.

Deux points sur lesquels je veux me concentrer. Premièrement, ce matériel est destiné aux analystes débutants qui font leurs premiers pas dans l'utilisation du langage de programmation Python. Idéal si les lecteurs connaissent la plate-forme d'analyse Power BI BI. Deuxièmement, étant donné que les calculs DAX ont servi de source d’inspiration, je vais «copier» les algorithmes de l’auteur dans la mesure du possible, et il y aura inévitablement un écart par rapport aux principaux paradigmes de programmation.

Avec l'introduction, c'est tout. Prenons la route!

Nous effectuerons tous les calculs dans l'environnement JupyterLab. Les solutions pour ordinateur portable sont disponibles sur ( lien ).

Les données sont chargées dans Power BI à l'aide de l'outil Power Query (en fait, c'est un éditeur visuel qui génère des requêtes dans le langage M). La règle suivante doit être suivie pendant le développement: tout le prétraitement des données doit être effectué à l'aide de Power Query et les mesures doivent être calculées à l'aide de Power Pivot. Puisque notre bibliothèque principale est Pandas, nous utilisons immédiatement ses capacités.

%%time
#   
path_to_data = "C:/Users/Pavel/Documents/Demo/"
# 
df = pd.read_csv(os.path.join(path_to_data, "ohortAnalysis_2016_2018.csv"), sep=";", parse_dates=["date"], dayfirst=True)

Nous testerons les lignes de code lors de l'exécution afin d'établir les sections les plus chronophages à l'avenir. Pour définir le chemin d'accès complet au fichier lu, utilisez la bibliothèque os. Pour simplifier le processus de développement d'un ordinateur portable, vous pouvez vous en passer. L'ensemble de données lui-même est composé de manière aléatoire. Il y a 1 048 575 lignes dans le fichier CSV. La lecture de données avec la fonction read_csv () est généralement simple. Il suffit de spécifier le séparateur de colonnes et la colonne avec les dates, le cas échéant, dans le tableau. Si les informations ont été téléchargées avec certaines "fonctionnalités", vous devrez peut-être configurer des paramètres supplémentaires, par exemple, en spécifiant l'encodage pour chaque colonne.

La fonction head () sera souvent utilisée dans le cas pour surveiller visuellement la progression de la transformation des données. Toutes les erreurs ne peuvent pas être supprimées, mais des défauts évidents peuvent être corrigés sur place.

Après avoir chargé les données dans le modèle, l'auteur du webinaire trie le tableau de données. Ceci est fait afin d'ajouter une colonne auxiliaire avec des index. Dans notre cas, cette colonne ne sera pas utilisée, mais les données seront également triées afin de contrôler plus facilement le calcul correct des champs dans les tableaux.

%%time
#  ,      
df.sort_values(["user_id","date"], inplace = True)

image

À l'étape suivante, la solution sur la plateforme Power BI propose de créer une table auxiliaire, dont les données seront extraites dans le tableau principal. La création d'une table se fait à l'aide de la fonction SUMMARIZE (). Il crée un tableau df_groupby_user = SUMMARIZE(df;df[user_id];"first_date_transaction";MIN(df[date]);"total_amount_user";SUM(df[amount]);"count_transaction_user";COUNT(df[amount]))

croisé dynamique avec des totaux agrégés pour les groupes sélectionnés: La bibliothèque Pandas a son homologue - la fonction groupby (). Pour appliquer groupby (), il suffit de spécifier le bloc de données nécessaire, les colonnes groupables, à la fin de la liste les colonnes pour lesquelles les fonctions d'agrégation seront utilisées. Le résultat obtenu est réduit au format de la trame de données habituelle par la fonction reset_index (). En conclusion, renommez les champs.

%%time
#       user_id. 
df_groupby_user = df.groupby(by = ["user_id"]).agg({"date": "min", "amount": ["sum","count"]})
df_groupby_user.reset_index(inplace = True)
# 
new_columns = ["user_id","first_date_transaction", "total_amount_user","count_transaction_user"]
df_groupby_user.columns = new_columns

En plus de la mesure «première date d'achat», le nombre de transactions par client et le montant total des achats des clients pour toute la période sont calculés. Les mesures n'ont pas trouvé leur application dans l'ordinateur portable, mais nous ne les supprimerons pas.

Nous revenons au webinaire. La nouvelle mesure "année-mois du premier achat" est calculée. Formule DAX: first_transaction = FORMAT(df_groupby_user[first_date_transaction];"YYYY-MM")

image

Python utilise la syntaxe dt.strftime ('% Y-% m'). Vous trouverez une explication détaillée de son fonctionnement dans les publications en ligne concernant l'utilisation de la date et de l'heure en Python. À ce stade, autre chose est important. Faites attention au moment de l'opération.

Pas du tout des performances de type pandas (24,8 sec.). La ligne de code est plus lente que toutes les précédentes.
Cette fiche devient le premier candidat à une éventuelle refactorisation.

%%time
#  -
df_groupby_user["first_transaction"] = df_groupby_user["first_date_transaction"].dt.strftime('%Y-%m')

Il est temps de revenir au webinaire. Il y a une union de tables par un champ clé. Ensuite, les champs nécessaires sont tirés dans la table principale à l'aide de la fonction RELATED (). Pandas n'a pas cette fonctionnalité. Mais il y a merge (), join (), concat (). Dans ce cas, il est préférable d'appliquer la première option.

%%time
# 
df_final = pd.merge(df, df_groupby_user, how = "left", on = "user_id")

Une fois que les données avec la date de la première transaction sont tombées dans le tableau principal, vous pouvez calculer le delta. Nous utilisons la construction apply (lambda x: ...) pour montrer clairement à quel point ce processus est gourmand en ressources (39,7 sec.). Voici un autre candidat pour la réécriture de code.

%%time
#   "    "
df_final["delta_days"] = df_final["date"] - df_final["first_date_transaction"]
df_final["delta_days"] = df_final["delta_days"].apply(lambda x: x.days)

La table principale a déjà un delta par jour, vous pouvez donc diviser les données de la colonne en cohortes. Principe: 0 (c'est-à-dire la première vente au client) - cohorte 0; les valeurs supérieures à 0, mais inférieures ou égales à 30 sont 30; les valeurs supérieures à 30, mais inférieures ou égales à 90 sont 90, etc. À ces fins, dans DAX, vous pouvez utiliser la fonction CEILING (). Il arrondit le nombre à l'entier le plus proche, un multiple de la valeur du deuxième paramètre.

image

En Python, je n'ai pas trouvé de fonction mathématique similaire, même si j'avais prévu de la trouver dans le module mathématique (j'ai peut-être mal cherché). Par conséquent, j'ai dû faire le tour et appliquer la fonction cut (). Après avoir réparti les données en cohortes, NaN a été mappé à des valeurs numériques de 0. Nous ne pouvons pas résoudre ce problème en utilisant la fonction fillna (), car nous avons affaire à des données catégorielles. Vous devez d'abord ajouter une nouvelle valeur à la catégorie. À la fin de cette liste de codes, changez le type de données en int. Ceci est fait de sorte qu'à l'avenir, lors de la construction d'un tableau croisé dynamique à l'aide d'un bloc de données, la nouvelle cohorte n'apparaisse pas à la fin d'une série de valeurs.

%%time
#  . 
cut_labels_days = [x for x in range (30, 1230, 30)]
cut_bins_days = [x for x in range (0, 1230, 30)]
df_final["cohort_days"] = pd.cut(df_final["delta_days"], bins = cut_bins_days, labels=cut_labels_days, right = True)
%%time
#     .   fillna   !
df_final["cohort_days"] = df_final["cohort_days"].cat.add_categories([0])
df_final["cohort_days"].fillna(0, inplace = True)
%%time
#     .   fillna   !
df_final["cohort_days"] = df_final["cohort_days"].cat.add_categories([0])
df_final["cohort_days"].fillna(0, inplace = True)
#    .    ,  "0"        ,     
#    .
df_final["cohort_days"] = df_final["cohort_days"].astype(int)

En utilisant la fonction pivot_table (), nous obtenons le tableau croisé dynamique souhaité. Nous obtenons beaucoup de cohortes, donc le résultat ne peut pas être complètement affiché à l'écran. Pour éviter cela, lors de la résolution d'un cas réel, vous pouvez prendre un intervalle de temps plus court pour l'analyse ou agrandir les plages de valeurs des cohortes elles-mêmes.

%%time
#  
df_pivot_table = pd.pivot_table(df_final, values=["amount"], index=["first_transaction"], columns=["cohort_days"], aggfunc=np.sum, fill_value = 0)

image

Dans Power BI, vous devez utiliser l'outil Matrix pour créer une telle visualisation.

image

La prochaine étape consiste à comploter. La nuance de la situation est que nous avons besoin du montant selon la comptabilité d'exercice. Dans Power BI, sélectionnez simplement l'élément de menu «Action rapide» requis et la formule DAX nécessaire sera automatiquement générée. Avec la bibliothèque Pandas, la situation est un peu plus compliquée. Nous groupons de manière séquentielle la trame de données existante et appliquons la fonction cumsum (). Puisque le résultat sera toujours utilisé, nous allons faire une copie du bloc de données pour construire le graphique. Les valeurs des ventes cumulées étaient assez importantes, nous divisons donc les valeurs par 1 000 000 et arrondissons le résultat à deux chiffres après la virgule décimale.

%%time
#     amount
df_pivot_table_cumsum = df_final.groupby(by = ["first_transaction","cohort_days"]).agg({"amount": ["sum"]}).groupby(level=0).cumsum().reset_index()
df_pivot_table_cumsum.columns = ["first_transaction","cohort_days","cumsum_amount"]
%%time
#     
df_pivot_table_cumsum_chart = copy.deepcopy(df_pivot_table_cumsum)
#     ,       Y.
df_pivot_table_cumsum_chart["cumsum_amount"]=round(df_pivot_table_cumsum_chart["cumsum_amount"]/1000000, 2)

Nous utilisons les capacités de la bibliothèque pour construire le graphique. Un diagramme est construit en une seule ligne de code, mais le résultat n'est pas impressionnant. Ce graphique perd clairement aux visualisations sur n'importe quelle plateforme de BI. Vous pouvez connecter la bibliothèque Plotly et évoquer des modules complémentaires, mais cela représente des coûts de main-d'œuvre complètement différents par rapport à l'approche présentée dans la vidéo.

%%time
df_pivot_table_cumsum_chart.pivot(index="cohort_days", columns="first_transaction", values="cumsum_amount").plot(figsize = (15,11))

image

Faisons de brèves conclusions.

En termes de calculs, la bibliothèque Pandas pourrait bien remplacer Power Pivot (DAX).

La faisabilité d'un tel remplacement reste en dehors de la conversation.

DAX, comme les fonctions de la bibliothèque Python, fait un bon travail pour effectuer des opérations sur des champs entiers d'une table.

En termes de vitesse, de simplicité et de facilité de conception de la visualisation, Power BI est supérieur aux Pandas. À mon avis, les graphiques intégrés (ainsi que ceux créés à l'aide du matplotlib, des bibliothèques marines) sont appropriés pour s'appliquer dans deux cas: analyse express d'un certain nombre de données pour les valeurs aberrantes, minima / maxima locaux ou préparation de diapositives pour la présentation. Il est préférable de laisser le développement de panneaux de contrôle graphiques aux solutions BI.

C'est tout. Toute santé, bonne chance et réussite professionnelle!

All Articles