Recherche de site Web

Comment créer des tableaux croisés dynamiques avec des pandas


Un tableau croisé dynamique est un outil d'analyse de données qui vous permet de prendre des colonnes de données brutes d'un DataFrame pandas, de les résumer, puis d'analyser les données récapitulatives pour révéler ses informations.

Les tableaux croisés dynamiques vous permettent d'effectuer des calculs statistiques agrégés courants tels que des sommes, des décomptes, des moyennes, etc. Souvent, les informations produites par un tableau croisé dynamique révèlent des tendances et d’autres observations cachées par vos données brutes d’origine.

Les tableaux croisés dynamiques ont été initialement implémentés dans les premiers tableurs et sont encore une fonctionnalité couramment utilisée dans les derniers. On les trouve également dans les applications de bases de données modernes et dans les langages de programmation. Dans ce didacticiel, vous apprendrez comment implémenter un tableau croisé dynamique en Python à l'aide de la méthode DataFrame.pivot_table() de pandas.

Avant de commencer, vous devez vous familiariser avec à quoi ressemble un DataFrame pandas et comment en créer un. Connaître la différence entre un DataFrame et une série pandas s'avérera également utile.

De plus, vous souhaiterez peut-être utiliser l'outil d'analyse de données Jupyter Notebook pendant que vous parcourez les exemples de ce didacticiel. Alternativement, JupyterLab vous offrira une expérience de notebook améliorée, mais n'hésitez pas à utiliser n'importe quel environnement Python de votre choix.

L’autre chose dont vous aurez besoin pour ce didacticiel, ce sont bien sûr les données. Vous utiliserez les données Présentation des données de vente - Tableaux de bord, qui sont disponibles gratuitement pour que vous puissiez les utiliser sous la licence Apache 2.0. Les données ont été mises à votre disposition dans le fichier sales_data.csv que vous pouvez télécharger en cliquant sur le lien ci-dessous.

Ce tableau fournit une explication des données que vous utiliserez tout au long de ce didacticiel :

Column Name Data Type (PyArrow) Description
order_number int64 Order number (unique)
employee_id int64 Employee’s identifier (unique)
employee_name string Employee’s full name
job_title string Employee’s job title
sales_region string Sales region employee works within
order_date timestamp[ns] Date order was placed
order_type string Type of order (Retail or Wholesale)
customer_type string Type of customer (Business or Individual)
customer_name string Customer’s full name
customer_state string Customer’s state of residence
product_category string Category of product (Bath Products, Gift Basket, Olive Oil)
product_number string Product identifier (unique)
product_name string Name of product
quantity int64 Quantity ordered
unit_price double Selling price of one product
sale_price double Total sale price (unit_price × quantity)

Comme vous pouvez le constater, la table stocke les données d'un ensemble fictif de commandes. Chaque ligne contient des informations sur une seule commande. Vous vous familiariserez davantage avec les données au fur et à mesure que vous parcourrez le didacticiel et tenterez de résoudre les différents exercices de défi qu'il contient.

Tout au long de ce didacticiel, vous utiliserez la bibliothèque pandas pour vous permettre de travailler avec des DataFrames et la nouvelle bibliothèque PyArrow. La bibliothèque PyArrow fournit aux pandas ses propres types de données optimisés, qui sont plus rapides et moins gourmands en mémoire que les types NumPy traditionnels que les pandas utilisent par défaut.

Si vous travaillez en ligne de commande, vous pouvez installer à la fois pandas et pyarrow en utilisant python -m pip install pandas pyarrow, peut-être dans un environnement virtuel pour éviter tout conflit avec votre environnement existant. Si vous travaillez dans un bloc-notes Jupyter, vous devez utiliser !python -m pip install pandas pyarrow. Une fois les bibliothèques en place, vous pouvez ensuite lire vos données dans un DataFrame :

>>> import pandas as pd

>>> sales_data = pd.read_csv(
...     "sales_data.csv",
...     parse_dates=["order_date"],
...     dayfirst=True,
... ).convert_dtypes(dtype_backend="pyarrow")

Tout d'abord, vous avez utilisé import pandas pour rendre la bibliothèque disponible dans votre code. Pour construire le DataFrame et le lire dans la variable sales_data, vous avez utilisé la fonction read_csv() de pandas. Le premier paramètre fait référence au fichier en cours de lecture, tandis que parse_dates souligne que les données de la colonne order_date sont destinées à être lues comme datetime64[ns] taper. Mais il y a un problème qui empêchera que cela se produise.

Dans votre fichier source, les dates de commande sont au format jj/mm/aaaa, donc pour indiquer à read_csv() que la première partie de chaque date représente un jour, vous définissez le paramètre dayfirst sur True. Cela permet à read_csv() de lire désormais les dates de commande en tant que types datetime64[ns].

Avec les dates de commande lues avec succès en tant que types datetime64[ns], la méthode .convert_dtypes() peut ensuite les convertir avec succès en un timestamp[ns][pyarrow] type de données, et non le type string[pyarrow] plus général qu'il aurait autrement fait. Bien que cela puisse sembler un peu détourné, vos efforts vous permettront d'analyser les données par date si vous en avez besoin.

Si vous souhaitez consulter les données, vous pouvez exécuter sales_data.head(2). Cela vous permettra de voir les deux premières lignes de votre dataframe. Lorsque vous utilisez .head(), il est préférable de le faire dans un Jupyter Notebook car toutes les colonnes sont affichées. De nombreux REPL Python affichent uniquement les première et dernière colonnes, sauf si vous utilisez pd.set_option("display.max_columns", None) avant d'exécuter .head().

Si vous souhaitez vérifier que les types PyArrow sont utilisés, sales_data.dtypes le confirmera pour vous. Comme vous le verrez, chaque type de données contient [pyarrow] dans son nom.

Il est maintenant temps de créer votre premier tableau croisé dynamique pandas avec Python. Pour ce faire, vous apprendrez d’abord les bases de l’utilisation de la méthode .pivot_table() du DataFrame.

Comment créer votre premier tableau croisé dynamique avec des pandas

Maintenant que votre parcours d’apprentissage est en cours, il est temps de progresser vers votre première étape d’apprentissage et d’accomplir la tâche suivante :

Calculez les ventes totales pour chaque type de commande pour chaque région.

Avec autant de données à portée de main et avec la méthode .pivot_table() prenant en charge autant de paramètres, vous pourriez vous sentir un peu dépassé. Ne t'inquiète pas. Si vous procédez étape par étape et que vous y réfléchissez soigneusement à l’avance, vous construirez des tableaux croisés dynamiques perspicaces en un rien de temps.

Avant de créer un tableau croisé dynamique, vous devez d'abord réfléchir à comment vous souhaitez que les données soient affichées. Une approche consisterait à avoir une colonne distincte pour chaque catégorie de produits et une ligne distincte pour chaque région. Les ventes totales de chaque catégorie de produits pour chaque région pourraient alors être placées à l'intersection de chaque ligne et colonne.

Après y avoir réfléchi, vous notez quelques réflexions et calculez quelques exemples de résultats pour vous aider à visualiser ce que vous voulez :

Vous en êtes satisfait, vous devez donc ensuite traduire votre plan dans les paramètres requis par la méthode DataFrame.pivot_table(). Dans ce cas, les paramètres dont vous avez besoin sont valeurs, index, colonnes et aggfunc. Il est important de comprendre à quoi ils servent, car ce sont les paramètres de base utilisés dans la plupart des tableaux croisés dynamiques.

Votre tableau croisé dynamique effectuera ses calculs sur la base des chiffres des prix de vente. Celles-ci deviennent les valeurs transmises au paramètre values, donnant à votre tableau croisé dynamique les chiffres avec lesquels il doit travailler. Si vous ne spécifiez pas ce paramètre, le tableau croisé dynamique utilisera toutes les colonnes numériques par défaut.

Le paramètre index vous permet de spécifier comment les données doivent être regroupées. N'oubliez pas que vous devez trouver le total des ventes pour chaque type de commande et pour chaque région. Vous avez décidé de produire une ligne de données agrégées basée sur le champ sales_region, celle-ci sera donc transmise en tant que paramètre index.

Vous souhaitez également une colonne distincte pour chaque type de commande. Celui-ci sera attribué au paramètre columns.

Enfin, vous devez également indiquer à votre tableau croisé dynamique que vous souhaitez calculer les ventes totales pour chaque agrégation. Pour créer un tableau croisé dynamique simple, vous définissez son paramètre aggfunc sur, dans ce cas, la fonction sum().

Maintenant que vous avez bien réfléchi à votre plan, vous pouvez continuer et coder votre tableau croisé dynamique :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index="sales_region", columns="order_type",
...     aggfunc="sum",
... )
order_type          Retail   Wholesale
sales_region
Central East   $102,613.51 $149,137.89
N Central East $117,451.69 $152,446.42
N Central West  $10,006.42   $1,731.50
Northeast       $84,078.95 $127,423.36
Northwest       $34,565.62  $33,240.12
S Central East $130,742.32 $208,945.73
S Central West  $54,681.80  $51,051.03
Southeast       $96,310.12 $127,554.60
Southwest      $104,743.52 $121,977.20

Comme vous pouvez le voir, .pivot_table() a utilisé les paramètres que vous aviez prévus précédemment pour produire votre tableau croisé dynamique.

Pour formater les nombres sous forme de devise, vous avez utilisé le mini-langage de format de chaîne de Python. Ce langage est utilisé à plusieurs endroits en Python, le plus souvent dans les f-strings. Si vous le connaissez déjà dans ce contexte, vous savez déjà comment l’utiliser ici.

En passant "display.float_format" et "$ {:,.2f}".format à la fonction set_option() de pandas, vous définissez le format des nombres à virgule flottante à partir de maintenant. Ils seront arrondis à 2 décimales, utiliseront une virgule (,) comme séparateur de milliers et seront préfixés par une devise ($). symbole.

Sauf si vous souhaitez conserver ce format pour vos futurs nombres à virgule flottante, vous devrez réinitialiser le formatage par défaut en utilisant pd.reset_option("display.float_format") après . pivot_table() a été appelé.

>>> with pd.option_context("display.float_format", "${:,.2f}".format):
...     sales_data.pivot_table(
...         values="sale_price", index="sales_region",
...         columns="order_type", aggfunc="sum",
...     )

L'instruction with définit le début du gestionnaire de contexte. Vous utilisez ensuite pd.option_context() pour définir le formatage à utiliser pour les nombres à virgule flottante. Cela garantit que votre format n'est appliqué qu'au code indenté en dessous, dans ce cas, la fonction .pivot_table(). Une fois le code indenté terminé, le gestionnaire de contexte n'est plus dans le périmètre, et le formatage précédent revient en jeu.

Malheureusement, lorsque vous utilisez le gestionnaire de contexte dans Jupyter ou IPython, il supprime l'impression de la sortie, sauf si vous l'imprimez explicitement. Pour cette raison, les gestionnaires de contexte ne sont pas utilisés dans ce didacticiel.

Même si vous êtes fondamentalement satisfait de votre résultat, vous sentez qu’il manque quelque chose : les totaux. Au départ, vous n’aviez pas pensé à inclure des colonnes de totaux, mais réalisez maintenant que cela serait utile. Pour résoudre ce problème, vous consultez la documentation et proposez la solution suivante :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index="sales_region", columns="order_type",
...     aggfunc="sum", margins=True, margins_name="Totals",
... )
order_type          Retail   Wholesale        Totals
sales_region
Central East   $102,613.51 $149,137.89   $251,751.40
N Central East $117,451.69 $152,446.42   $269,898.11
N Central West  $10,006.42   $1,731.50    $11,737.92
Northeast       $84,078.95 $127,423.36   $211,502.31
Northwest       $34,565.62  $33,240.12    $67,805.74
S Central East $130,742.32 $208,945.73   $339,688.05
S Central West  $54,681.80  $51,051.03   $105,732.83
Southeast       $96,310.12 $127,554.60   $223,864.72
Southwest      $104,743.52 $121,977.20   $226,720.72
Totals         $735,193.95 $973,507.85 $1,708,701.80

Cette fois, pour ajouter quelques touches finales supplémentaires, vous définissez margins=True et margins_name="Totals". Le paramètre margins=True a ajouté de nouvelles colonnes à droite et en bas de votre tableau croisé dynamique. Chacun contient respectivement les totaux des lignes et des colonnes. Le paramètre margins_name a inséré des étiquettes "Totaux" au lieu des étiquettes "Tous" par défaut qui apparaîtraient autrement.

Maintenant c'est votre tour. Essayez l’exercice suivant pour tester votre compréhension :

Créez un tableau croisé dynamique qui affiche le prix de vente le plus élevé pour chaque région de vente par catégorie de produit. Cette fois, chaque région de vente doit figurer dans une colonne distincte et chaque catégorie de produits dans une ligne distincte. Pour cet exemple, vous pouvez ignorer le total global des lignes et des colonnes, mais vous devez appliquer un format monétaire en utilisant le symbole $ et utiliser le caractère de soulignement (_) comme milliers. séparateur. Encore une fois, deux décimales sont nécessaires.

Vous trouverez une solution possible à cet exercice dans le solutions.ipynb Jupyter Notebook inclus dans les documents téléchargeables.

Si vous êtes arrivé jusqu'ici, vous comprenez maintenant les principes les plus importants de la création de tableaux croisés dynamiques. Félicitations! Mais ne vous détendez pas pour l’instant car il y a encore des choses encore plus passionnantes à apprendre. Poursuivez votre lecture pour approfondir encore davantage vos connaissances.

Inclure des sous-colonnes dans votre tableau croisé dynamique

Dans chacun de vos tableaux croisés dynamiques précédents, vous avez attribué une seule série DataFrame à son paramètre columns. Cela a inséré une colonne distincte dans votre tableau croisé dynamique pour chaque entrée unique dans la série attribuée. Ainsi, en attribuant "order_type" aux colonnes, votre tableau croisé dynamique comprenait à la fois les colonnes Retail et Wholesale, une pour chacune. type de commande. Il est temps d'élargir davantage vos connaissances et d'apprendre à inclure des sous-colonnes dans votre tableau croisé dynamique.

Voici la prochaine étape pour vous :

Calculez les ventes moyennes des différents types de commandes passées par chaque type de client pour chaque état.

Comme avant, arrêtez-vous et réfléchissez. La première étape consiste à réfléchir à ce que vous souhaitez voir et à intégrer les paramètres pertinents dans votre plan.

Une solution consisterait à afficher l’état de chaque client séparément. En ce qui concerne les colonnes à afficher, vous pouvez avoir une colonne distincte pour chaque type de client, puis des sous-colonnes pour chaque type de commande au sein de chaque type de client. Le calcul sera à nouveau basé sur le prix de vente, sauf que vous devrez cette fois calculer leurs moyennes.

Vous sortez à nouveau votre crayon et votre papier et créez une visualisation rapide :

Maintenant que vous avez bien réfléchi à cette question et que vous êtes satisfait, vous pouvez appliquer votre plan aux paramètres. Comme auparavant, le calcul utilisera les valeurs de la colonne sale_price, vous l'utiliserez donc comme paramètre values. Parce que vous voulez les valeurs moyennes, vous pouvez définir aggfunc="mean". Cependant, comme il s’agit de la valeur par défaut, vous n’avez pas besoin de le faire. Chaque ligne sera basée sur l'état du client, vous devrez donc définir index="customer_state".

Enfin, il faut penser aux colonnes. Cette fois, parce que vous voulez que les types de clients soient la colonne supérieure avec différents types de commandes pour chacun d'eux, votre paramètre columns sera la liste ["customer_type", "order_type"]. Il est possible de créer plusieurs sous-colonnes imbriquées en transmettant une liste plus longue, mais deux colonnes suffisent ici.

Pour répondre à vos besoins, vous utilisez le code ci-dessous :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index="customer_state",
...     columns=["customer_type", "order_type"], aggfunc="mean",
... )
customer_type        Business           Individual
order_type             Retail Wholesale     Retail
customer_state
Alabama               $362.67   $762.73    $137.47
Alaska                $295.33   $799.83    $137.18
Arizona               $407.50 $1,228.52    $194.46
Arkansas                 <NA> $1,251.25    $181.65
California            $110.53 $1,198.89    $170.94
...

Comme vous pouvez le constater, la transmission de la liste des colonnes a produit l'analyse requise pour vous. Encore une fois, vous avez appliqué une chaîne de format appropriée pour que votre sortie reste lisible. Il n'y a eu aucune vente au détail des entreprises de l'Arkansas, c'est pourquoi cela apparaît comme . Notez que le comportement par défaut du paramètre aggfunc consiste à calculer la moyenne des données, mais vous l'avez inclus ici uniquement pour plus de clarté.

Maintenant, revenez à votre visualisation originale et comparez-la à ce que votre code a réellement produit. Remarquez-vous quelque chose de différent ? C'est vrai, il n'y a pas de colonne Vente en gros individuelle dans votre sortie. C'est parce qu'il n'y a pas de valeurs correspondantes à y mettre. La méthode .pivot_table() l'a supprimé automatiquement pour empêcher l'affichage d'une colonne vide.

Il est temps de passer à un autre point de contrôle de votre compréhension. Voyez si vous pouvez résoudre ce prochain défi :

Créez un tableau croisé dynamique qui affiche les quantités les plus élevées de chaque catégorie de produits pour chaque type de client. Votre tableau croisé dynamique doit contenir une ligne pour chaque état dans lequel vivent les clients. Ajoutez des totaux récapitulatifs avec une étiquette "Quantité maximale".

Comme défi supplémentaire, jetez un œil à la documentation de la méthode .pivot_table() et voyez si vous pouvez comprendre comment remplacer les valeurs par des zéros.

Vous trouverez une solution possible à ces exercices dans le bloc-notes solutions.ipynb Jupyter inclus dans les documents téléchargeables.

Maintenant que vous savez comment travailler avec les sous-colonnes, il est temps d'apprendre à utiliser les sous-lignes.

Inclure des sous-lignes dans votre tableau croisé dynamique

Dans la section précédente, vous avez vu comment passer une liste dans le paramètre columns permet de créer des sous-colonnes. Vous avez peut-être déjà deviné que pour analyser le trafic d'une ligne à l'autre, vous transmettez une liste dans le paramètre index. Comme auparavant, vous pouvez mieux comprendre cela avec un exemple, alors voici votre prochaine étape :

Calculez les ventes totales de chacune de vos différentes catégories de produits, mais affichez également les détails des différents types de commandes passées par les différents types de clients.

Comme d’habitude, prenez du recul et commencez à planifier soigneusement votre solution.

Une façon de résoudre ce problème serait d'avoir une colonne distincte pour chaque catégorie de produit et une ligne distincte analysant les types de commandes au sein des types de clients. Encore une fois, vous allez travailler avec des totaux de chiffres de ventes.

Taillez votre crayon :

Le calcul sera basé sur le prix de vente, qui sera votre paramètre values, tandis que la définition de aggfunc="sum" garantira que les totaux sont calculés. Pour séparer chaque catégorie de produits dans sa propre colonne, vous attribuez celle-ci au paramètre columns. Enfin, pour vous assurer que le type de client est sous-analysé par type de commande, vous attribuez ["customer_type", "order_type"] au paramètre index.

Lorsque vous transformez votre plan en code, voici le résultat :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index=["customer_type", "order_type"],
...     columns="product_category", aggfunc="sum",
... )
product_category          Bath products  Gift Basket   Olive Oil
customer_type order_type
Business      Retail          $1,060.87    $3,678.50  $23,835.00
              Wholesale       $6,024.60   $18,787.50 $948,695.75
Individual    Retail         $32,711.58  $113,275.00 $560,633.00
...

Ici, vous avez appris qu'en transmettant une liste de colonnes dans index, vous pouviez produire l'analyse requise. Encore une fois, vous avez utilisé $ {:,.2f} pour vous assurer que la devise était correctement formatée.

Il est temps de passer votre prochain contrôle de connaissances. Voyez si vous pouvez résoudre les problèmes suivants :

Cette fois, vous souhaitez un tableau croisé dynamique qui affiche les quantités totales de produits vendus analysées par état du client au sein du type de commande et par catégorie de produits au sein du type de client. Vous devez remplacer les valeurs par des zéros.

Vous trouverez une solution possible à cet exercice dans le solutions.ipynb Jupyter Notebook inclus dans les documents téléchargeables.

Maintenant que vous savez comment travailler avec les sous-colonnes et les sous-lignes, vous pourriez penser que vous avez terminé. Mais il y a plus ! Vous apprendrez ensuite comment inclure plusieurs valeurs dans vos calculs.

Calcul de plusieurs valeurs dans votre tableau croisé dynamique

Jusqu'à présent, chacun de vos tableaux croisés dynamiques a analysé les données d'une seule colonne telle que sale_price ou quantité. Supposons que vous souhaitiez analyser les données des deux colonnes de la même manière. Pouvez-vous deviner comment faire ? Si vous envisagez de fournir les deux colonnes d'une liste au paramètre values, vous avez raison.

En fonction de vos progrès jusqu’à présent, la prochaine étape est à votre portée :

Calculez la somme des prix de vente et des quantités vendues de chaque catégorie de produits dans chaque région de vente.

Presque inconsciemment, votre cerveau est passé en mode planification :

Une solution consisterait à produire des lignes pour chaque catégorie de produit au sein de chaque région de vente, puis à afficher la quantité totale et le prix de vente pour chacune dans des colonnes distinctes. En d’autres termes, vous envisagez de créer quelque chose comme ceci :

Pour mettre cela en œuvre, vous utilisez des principes similaires à ceux que vous avez utilisés précédemment, mais vous devez tenir compte d’une ou deux mises en garde. Pour calculer les totaux, vous définissez aggfunc="sum", tandis que pour traiter les valeurs en les remplaçant par zéro, vous définissez fill_value à 0. Pour produire les lignes montrant la région de vente sous-analysée par catégorie de produit, vous les transmettez toutes les deux dans une liste à index.

Le code que vous pouvez utiliser pour faire tout cela est présenté ci-dessous :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     index=["sales_region", "product_category"],
...     values=["sale_price", "quantity"],
...     aggfunc="sum", fill_value=0,
... )
                                 quantity  sale_price
sales_region   product_category
Central East   Bath Products          543   $5,315.40
               Gift Basket            267  $16,309.50
               Olive Oil             1497 $230,126.50
N Central East Bath Products          721   $6,905.36
               Gift Basket            362  $21,533.00
               Olive Oil             1648 $241,459.75
N Central West Bath Products           63     $690.92
               Gift Basket             26   $2,023.50
               Olive Oil               87   $9,023.50
...

Pour calculer les totaux de sale_price et de quantité, vous les avez transmis sous forme de liste au paramètre values. Un petit problème est que les colonnes de calcul sont affichées différemment de l'ordre de leur liste de définition. Dans cet exemple, vous avez transmis ["sale_price", "quantity"] à values, mais si vous regardez attentivement le résultat, vous verrez qu'il est affiché par ordre alphabétique.

Encore une fois, vous avez utilisé "$ {:,.2f}" pour vous assurer que la devise était correctement formatée. Notez que cela ne s'applique qu'aux flotteurs. Le formatage des valeurs entières quantité s'est fait tout seul.

Un autre point à noter est qu'il n'y a pas de paramètre columns. Vous avez peut-être pensé que la liste passée aux valeurs aurait dû être passée aux colonnes, mais ce n'est pas le cas. Le paramètre columns, ainsi que index, permettent de définir la manière dont les données sont regroupées. Dans cet exemple, vous n'effectuez pas de regroupement par prix de vente ou par quantité. Au lieu de cela, vous les utilisez dans un calcul. Par conséquent, ils doivent être affectés à des valeurs.

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     index=["sales_region", "product_category"],
...     values=["sale_price", "quantity"],
...     aggfunc="sum", fill_value=0,
... ).loc[:, ["sale_price", "quantity"]]

Vous pouvez extraire des données d'un tableau croisé dynamique en utilisant son attribut .loc[]. Cela vous permet de définir les lignes et les colonnes que vous souhaitez voir par leurs étiquettes d'index.

Dans ce cas, vous vouliez afficher toutes les lignes, vous avez donc transmis un caractère deux-points (:) comme premier paramètre de .loc[]. Ensuite, comme vous souhaitiez voir les colonnes sale_price et quantity (dans cet ordre), vous les avez transmises sous forme de liste au deuxième paramètre.

Allez-y et exécutez ce code. Vous verrez que les colonnes sont dans le même ordre que celui défini dans la liste passée à .loc[].

Il est temps de masser à nouveau cette matière grise. Voyez si vous pouvez résoudre le prochain défi :

Supposons que vous souhaitiez en savoir plus sur les ventes de votre entreprise. Créez un tableau croisé dynamique avec des lignes qui analysent les différents types de commandes pour chaque type de client, et avec des colonnes qui affichent la quantité totale pour chaque catégorie de produit, ainsi que les ventes totales pour chaque catégorie de produit commandée.

Vous trouverez une solution possible à cet exercice dans le solutions.ipynb Jupyter Notebook inclus dans les documents téléchargeables.

À présent, vous devenez un créateur compétent de tableaux croisés dynamiques. Vous vous demandez peut-être ce qu’il reste à apprendre. Si vous souhaitez savoir comment appliquer différentes fonctions d'agrégation aux mêmes données, différentes fonctions d'agrégation à différentes données, ou même comment écrire vos propres fonctions d'agrégation personnalisées, poursuivez votre lecture.

Effectuer des agrégations plus avancées

Maintenant que vous volez haut en toute confiance, vous vous demandez si vous pouvez faire ce qui suit :

Calculez les ventes maximales et minimales de chaque catégorie de produits pour chaque type de client.

Avec le système de refroidissement de votre cerveau désormais à pleine puissance, vous pouvez recommencer à réfléchir.

Pour résoudre ce problème, vous devez appliquer plusieurs fonctions d'agrégation aux mêmes données. Auparavant, lorsque vous analysiez des données à l'aide de plusieurs critères, vous le faisiez en transmettant des listes à un paramètre approprié. Pouvez-vous deviner comment appliquer plusieurs fonctions ? Sinon, la réponse est indiquée ci-dessous :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values=["sale_price"], index="product_category",
...     columns="customer_type", aggfunc=["max", "min"],
... )
                        max                   min
                 sale_price            sale_price
customer_type      Business Individual   Business Individual
product_category
Bath Products       $300.00    $120.00      $5.99      $5.99
Gift Basket       $1,150.00    $460.00     $27.00     $19.50
Olive Oil         $3,276.00    $936.00     $16.75     $16.75

Ici, vous avez appliqué plusieurs fonctions aux mêmes données en les transmettant sous forme de liste au paramètre aggfunc. Vous avez également décidé de créer une ligne pour chaque catégorie de produits, et cela est devenu le paramètre index. Vos calculs étaient basés sur le prix de vente, vous l'avez donc transmis comme paramètre values. Puisque vous l'avez transmis sous forme de liste, vous avez veillé à ce que la rubrique prix de vente apparaisse comme un en-tête de colonne supplémentaire pour une meilleure lisibilité.

De plus, comme vous souhaitiez afficher les données par type de client, vous avez décidé d'en faire le paramètre colonnes. Pour effectuer à la fois un calcul "max" et "min", vous avez transmis ces deux éléments au paramètre aggfunc dans une liste.

Ensuite, vous décidez de pousser le bateau un peu plus loin en essayant ceci :

Ajustez l'analyse précédente pour afficher à la fois le prix de vente moyen et la quantité maximale vendue.

En y réfléchissant bien, vous réalisez qu'il s'agirait d'un exemple d'application de différentes fonctions d'agrégation à différentes données. Pour ce faire, vous devez transmettre non pas une liste, mais un dictionnaire à aggfunc qui définit chaque colonne de données et la fonction d'agrégation à utiliser dessus. N'oubliez pas de vous assurer que chaque élément de données apparaît également dans le paramètre values.

Pour créer ce tableau croisé dynamique, vous pouvez procéder comme suit :

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values=["sale_price", "quantity"],
...     index=["product_category"],
...     columns="customer_type",
...     aggfunc={"sale_price": "mean", "quantity": "max"},
... )
                 quantity            sale_price
customer_type    Business Individual   Business Individual
product_category
Bath Products          14          4     $53.27     $25.94
Gift Basket            14          4    $335.31    $156.24
Olive Oil              14          4  $1,385.37    $250.06

Le tableau croisé dynamique affiche désormais les quantités maximales pour chacun des deux types de clients et le prix de vente moyen pour chacun. Vous y êtes parvenu en ajoutant quantity à la liste des paramètres values, puis vous avez passé un dictionnaire dans aggfunc avant .pivot_table() a opéré sa magie.

Vous avancez et poussez ce bateau encore plus loin en faisant ceci :

Calculez le nombre d'employés dans chaque région de vente.

À première vue, cela paraît simple. Vous y réfléchissez rapidement et pensez que ce code vous donnera ce que vous voulez :

>>> sales_data.pivot_table(
...     values="employee_id",
...     index="sales_region",
...     aggfunc="count",
... )
                employee_id
sales_region
Central East            697
N Central East          832
N Central West           70
Northeast               604
...

Lorsque vous exécutez ce code, il semble y avoir de nombreux vendeurs dans chaque région. Ces chiffres sont incorrects car count a compté les valeurs employee_id en double. Si vous examinez les données d'origine, vous verrez que le même employé est répertorié plusieurs fois dans sa région de vente. Vous devez repenser.

Jusqu'à présent, vous avez utilisé le paramètre aggfunc pour spécifier les fonctions utilisées pour effectuer les calculs d'agrégation. Chacun d'eux a pris la série définie par le paramètre values et l'a agrégée en une seule valeur selon les paramètres index et columns. Vous pouvez aller plus loin dans ces idées et écrire votre propre fonction d'agrégation personnalisée, à condition qu'elle prenne une série comme argument et renvoie une seule valeur agrégée.

Lorsque vous écrivez votre propre fonction d'agrégation, vous devez transmettre une sous-série de valeurs employee_id pour chaque sales_region. Votre fonction doit ensuite déterminer le nombre de valeurs uniques dans chaque sous-série et renvoyer les résultats à .pivot_table(). Une façon d’écrire une telle fonction est présentée ci-dessous :

>>> def count_unique(values):
...     return len(values.unique())
...

Votre fonction count_unique() a accepté une série pandas nommée values et a utilisé la méthode Series.unique() pour obtenir un tableau NumPy contenant les éléments uniques de la Série. La fonction len() intégrée à Python a ensuite renvoyé la longueur, ou le nombre d'éléments, dans chaque série values.

Pour appeler votre fonction et lui passer les différentes sous-séries, vous attribuez le nom de la fonction au paramètre aggfunc de .pivot_table(). Heureusement, vous n'avez pas à vous soucier de transmettre chaque sous-série car cela est fait pour vous par .pivot_table(). Cela signifie que votre fonction est appelée une fois pour chaque région de vente différente. Les valeurs de retour sont ensuite affichées dans le tableau croisé dynamique résultant.

Pour voir comment cela fonctionne, jetez un œil au code ci-dessous :

>>> sales_data.pivot_table(
...     values="employee_id",
...     index=["sales_region"],
...     aggfunc=count_unique,
... )
                employee_id
sales_region
Central East              6
N Central East            6
N Central West            1
Northeast                 4
...

Comme vous pouvez le voir, en attribuant count_unique au paramètre aggfunc, .pivot_table() a calculé le nombre de valeurs uniques d'identifiant d'employé. En d’autres termes, il a calculé le nombre de vendeurs présents dans chaque région. Encore une réussite !

Il est temps de relever un autre défi. Amusez-vous avec celui-ci :

Voyez si vous pouvez déterminer le nombre de produits uniques que l'organisation vend dans chaque région de vente et le revenu total généré dans chaque région.

Une solution possible à cet exercice est incluse dans le solutions.ipynb Jupyter Notebook inclus dans les documents téléchargeables.

À ce stade, vous êtes désormais devenu un expert dans la création de tableaux croisés dynamiques en Python. Enfin, vous découvrirez d’autres façons d’agréger des données.

Utilisation de .groupby() et crosstab() pour l'agrégation

Bien que votre expérience de tableau croisé dynamique se soit jusqu'à présent concentrée sur .pivot_table(), ce n'est pas le seul moyen d'effectuer l'agrégation de données dans les pandas. DataFrames dispose également d'une méthode .groupby(), tandis que pandas fournit une fonction crosstab() qui agrège également les données. Dans cette section, vous verrez des exemples de la façon dont ils peuvent être utilisés pour faire plus que .pivot_table().

Une fonctionnalité que vous pourriez trouver utile et qui existe dans .groupby() mais pas dans .pivot_table() est les agrégations nommées. Ceux-ci vous permettent d'appliquer des en-têtes de colonnes personnalisés pour clarifier l'affichage de vos calculs agrégés.

Voici votre prochaine étape d’apprentissage :

Calculez le prix le plus bas, la moyenne, le plus élevé et l'écart type des prix de chaque catégorie de produits, puis utilisez des agrégations nommées pour améliorer votre résultat.

Vous décidez d'utiliser vos connaissances existantes pour planifier et écrire le code suivant. Vous êtes sûr que cela vous permettra au moins de voir les données dont vous avez besoin :

>>> sales_data.pivot_table(
...     values="sale_price",
...     index="product_category",
...     aggfunc=["min", "mean", "max", "std"],
... )
                        min       mean        max        std
                 sale_price sale_price sale_price sale_price
product_category
Bath Products         $5.99     $28.55    $300.00     $23.98
Gift Basket          $19.50    $171.39  $1,150.00    $131.64
Olive Oil            $16.75    $520.78  $3,276.00    $721.49

Le code ci-dessus a analysé vos données par catégorie de produits et calculé le minimum, la moyenne, le maximum et l'écart type du prix de vente. Cependant, même si cette sortie vous a certainement donné ce que vous vouliez, les en-têtes de colonnes indiquant chaque prix de vente ne sont pas très bien présentés. C’est là que les agrégations nommées peuvent aider.

Pour effectuer un calcul d'agrégation à l'aide de .groupby(), vous lui transmettez la ou les colonnes que vous souhaitez regrouper. Cela renvoie quelque chose appelé un objet DataFrameGroupBy, qui contient une méthode .agg() qui vous permet de définir les fonctions d'agrégation à utiliser, ainsi que leurs en-têtes. .

Une façon d'utiliser .agg() est de lui transmettre un ou plusieurs tuples contenant la colonne de données et la fonction d'agrégation à utiliser pour cette colonne. Vous transmettez chaque tuple à l'aide d'un mot-clé, qui devient le nom de l'en-tête dans l'agrégation résultante.

L'exemple ci-dessus pourrait être écrit avec .groupby() comme indiqué ci-dessous :

>>> (
...     sales_data
...     .groupby("product_category")
...     .agg(
...          low_price=("sale_price", "min"),
...          average_price=("sale_price", "mean"),
...          high_price=("sale_price", "max"),
...          standard_deviation=("sale_price", "std"),
...     )
... )
                  low_price  average_price  high_price  standard_deviation
product_category
Bath Products         $5.99         $28.55     $300.00              $23.98
Gift Basket          $19.50        $171.39   $1,150.00             $131.64
Olive Oil            $16.75        $520.78   $3,276.00             $721.49

Cette fois, vous pouvez voir que les données sont à nouveau regroupées par catégorie de produits, mais chaque regroupement a un en-tête plus clair nommé d'après le mot-clé utilisé pour spécifier le tuple. Si vous souhaitez approfondir .groupby() et comment l'utiliser, consultez pandas GroupBy : votre guide pour regrouper des données en Python.

Une autre manière courante de créer une agrégation consiste à utiliser la fonction crosstab(). Cela a une fonctionnalité similaire à .pivot_table() car, comme vous l'avez appris précédemment, il utilise .pivot_table() pour effectuer son analyse. La principale différence est que les données sont transmises à crosstab() en tant que série pandas individuelle.

Votre dernière étape est :

Calculez le nombre d'employés dans chaque région analysé par titre de poste à l'aide de crosstab().

En lisant la documentation, vous vous rendez vite compte que les paramètres de crosstab() sont similaires à ceux de pivot_table(). Cela signifie que vous en savez plus sur crosstab() que vous ne le pensiez au départ. Vos connaissances existantes vous permettent de planifier et d'exécuter rapidement le code suivant :

>>> pd.crosstab(
...     index=sales_data["job_title"],
...     columns=sales_data["sales_region"],
...     margins=True,
...     margins_name="Totals",
... )
sales_region                 Central East  N Central East \
job_title
Sales Associate                         0             132
Sales Associate I                       0               0
Sales Associate II                    139               0
Sales Associate III                     0               0
...
Totals                                697             832

N Central West  ...  Southeast  Southwest  Totals
                ...
             0  ...          0        138      357
            70  ...        195        254      929
             0  ...          0         95      727
             0  ...        231          0      358
                ...
            70  ...        694        731     5130
[10 rows x 10 columns]

Les paramètres utilisés par crosstab() sont similaires à ceux utilisés par pivot_table(). En effet, dans l'exemple ci-dessus, chaque paramètre a le même effet que son équivalent pivot_table(). La seule différence est que la fonction crosstab() doit avoir sa série de données transmise à l'aide d'une référence DataFrame, ce qui signifie que les données peuvent provenir de plusieurs DataFrames. Dans cet exemple, chaque ligne est analysée par titre de poste, tandis que chaque colonne est analysée par région de vente.

Une fonctionnalité prise en charge par crosstab() qui n'est pas incluse dans .pivot_table() est le paramètre normalize. Lorsque vous définissez normalize=True, vous divisez chaque cellule par le total de toutes les autres cellules du DataFrame résultant :

>>> pd.set_option("display.float_format", "{:.2%}".format)

>>> pd.crosstab(
...     index=sales_data["job_title"],
...     columns=sales_data["sales_region"],
...     margins=True,
...     margins_name="Totals",
...     normalize=True,
... )
sales_region                Central East N Central East \
job_title
Sales Associate                    0.00%          2.57%
Sales Associate I                  0.00%          0.00%
Sales Associate II                 2.71%          0.00%
Sales Associate III                0.00%          0.00%
...
Totals                            13.59%         16.22%

N Central West  ... Southeast Southwest  Totals
                ...
         0.00%  ...     0.00%     2.69%    6.96%
         1.36%  ...     3.80%     4.95%   18.11%
         0.00%  ...     0.00%     1.85%   14.17%
         0.00%  ...     4.50%     0.00%    6.98%
                ...
         1.36%  ...    13.53%    14.25%  100.00%

[10 rows x 10 columns]

Ce code est très similaire à l'exemple précédent, sauf qu'en définissant normalize=True, vous avez calculé chaque chiffre en pourcentage du total. Vous avez également utilisé "{:.2%}" pour afficher le résultat au format de pourcentage conventionnel.

Conclusion

Vous comprenez désormais parfaitement comment utiliser la méthode .pivot_table(), ses paramètres principaux et comment ils sont utilisés pour structurer les tableaux croisés dynamiques. Plus important encore, vous avez appris l’importance de réfléchir à ce que vous souhaitez réaliser avant de le mettre en œuvre.

Vous avez également appris comment créer un tableau croisé dynamique à l'aide des principaux paramètres de la méthode DataFrame.pivot_table() et comment certains paramètres peuvent produire une analyse plus détaillée avec des listes de valeurs au lieu de valeurs uniques. Pour compléter vos connaissances sur .pivot_table(), vous avez appris à créer des tableaux croisés dynamiques qui effectuent plusieurs agrégations, des agrégations spécifiques aux colonnes et même des agrégations personnalisées.

Vous avez également découvert deux autres méthodes d'agrégation de données à l'aide de .groupby() et crosstab(), que vous pouvez continuer à étudier par vous-même pour élargir encore plus vos connaissances. . Désormais, vous pouvez produire en toute confiance des vues intéressantes et complexes des données pour permettre aux informations qu'elles contiennent d'être révélées.

Félicitations pour avoir terminé ce didacticiel et profitez de l'application de ces nouvelles compétences à vos futurs projets d'analyse de données !