Archives Wild Code School
Python

Regroupement et agrégation de données

Quête 59 sur 66 · 1 image archivée

Prérequis

Avant de plonger dans cette leçon, vous devriez être familier avec :

  • Le travail avec les DataFrames Pandas
  • Les statistiques descriptives de base

Objectifs pédagogiques

À la fin de cette leçon, vous serez capable de :

  • Comprendre et utiliser la méthode .groupby() pour organiser et analyser des données.
  • Appliquer des fonctions d'agrégation pour résumer les données groupées (comme la somme, la moyenne, etc.).
  • Explorer des techniques avancées d'analyse, telles que l'agrégation personnalisée, le filtrage des groupes et le classement des données.

Lors de l'analyse de données, il est souvent nécessaire de décomposer de grands jeux de données en groupes plus petits et plus gérables pour en tirer des insights significatifs. Par exemple, vous pourriez avoir besoin de décomposer les données de ventes par région, évaluer le comportement des clients par segment, ou analyser les métriques de performance par département. Dans ces cas, l'examen des moyennes globales ou des totaux sur l'ensemble du jeu de données est souvent insuffisant.

La fonction groupby() de Pandas est un outil puissant qui divise vos données en groupes basés sur une ou plusieurs variables catégorielles, applique des fonctions d'agrégation à chaque groupe, et combine les résultats. Cette approche "diviser-appliquer-combiner" résume les données au sein de chaque groupe, offrant des insights sur les caractéristiques, la variabilité et les tendances.

Nous commencerons par les bases et explorerons progressivement des opérations plus complexes comme le groupement multi-niveaux, l'agrégation personnalisée et le filtrage de groupes.

Comme mentionné précédemment, le groupement est basé sur le concept Diviser-Appliquer-Combiner. Comme montré dans la figure, la fonction groupby() de Pandas divise le DataFrame en groupes basés sur des critères spécifiés. Une fois groupées, nous pouvons appliquer diverses opérations à chaque groupe indépendamment. Ensuite, les résultats sont combinés pour former un nouveau DataFrame ou Series.

Explorons la syntaxe de base de la fonction groupby() dans Pandas. Nous utiliserons un jeu de données représentant des données d'inventaire à travers différents entrepôts :

python
import pandas as pd

data = {
    'warehouse': ['warehouse_1', 'warehouse_2', 'warehouse_1', 'warehouse_3', 'warehouse_2', 'warehouse_3', 'warehouse_1', 'warehouse_2'],
    'product': ['widget_a', 'widget_b', 'widget_a', 'widget_c', 'widget_b', 'widget_c', 'widget_b', 'widget_a'],
    'stock_level': [150, 120, 100, 200, 130, 180, 110, 115],
    'reorder_level': [50, 60, 50, 75, 60, 70, 55, 65],
    'price_per_unit': [10, 12, 10, 8, 12, 8, 12, 10]
}

df = pd.DataFrame(data)
grouped = df.groupby(by=['warehouse'])
print(grouped)

# output:
# <pandas.core.groupby.generic.dataframe_group_by object at 0x...>

Dans cet exemple, le DataFrame est groupé par la colonne 'Warehouse'. Le résultat est un objet DataFrameGroupBy, une structure spéciale qui stocke les informations de groupement sans effectuer immédiatement de calculs. Vous pouvez considérer cet objet comme une collection de plus petits DataFrames, chacun représentant un entrepôt et contenant uniquement les lignes liées à cet entrepôt.

Vous pouvez extraire tous les enregistrements d'un groupe spécifique en utilisant la méthode .get_group(). Par exemple, si vous voulez voir les données pour Warehouse_1, vous pouvez faire :

python
# using .get_group() to extract a specific group
warehouse1_group = grouped.get_group('warehouse_1')
print("\\nwarehouse 1 group:\\n")
print(warehouse1_group)

# output:
# warehouse 1 group:
#
#     warehouse product  stock_level  reorder_level  price_per_unit
# 0  warehouse_1 widget_a         150             50              10
# 2  warehouse_1 widget_a         100             50              10
# 6  warehouse_1 widget_b         110             55              12

C'est utile quand vous voulez vous concentrer sur l'analyse d'un groupe particulier en isolation.

Vous pouvez également itérer à travers chaque groupe pour voir comment fonctionne la division :

python
# iterating through each group in the grouped object
for name, group in grouped:
    print(f"group: {name}")
    print(group)
    print()  # add a blank line for better readability

# output
# group: warehouse_1
#     warehouse  product  stock_level  reorder_level  price_per_unit
# 0  warehouse_1 widget_a          150             50              10
# 2  warehouse_1 widget_a          100             50              10
# 6  warehouse_1 widget_b          110             55              12

# group: warehouse_2
#     warehouse  product  stock_level  reorder_level  price_per_unit
# 1  warehouse_2 widget_b          120             60              12
# 4  warehouse_2 widget_b          130             60              12
# 7  warehouse_2 widget_a          115             65              10

# group: warehouse_3
#     warehouse  product  stock_level  reorder_level  price_per_unit
# 3  warehouse_3 widget_c          200             75               8
# 5  warehouse_3 widget_c          180             70               8

Agrégation en une seule colonne

Supposons que vous vouliez connaître les niveaux de stock totaux dans chaque entrepôt. Vous pouvez grouper par Warehouse et agréger la colonne Stock_Level en utilisant sum() :

python
# Group by warehouse and sum the stock levels
total_stock_level_w = df.groupby('warehouse')['stock_level'].sum()
print(total_stock_level_w)

# Output:
# warehouse
# warehouse_1    360
# warehouse_2    365
# warehouse_3    380
# Name: stock_level, dtype: int64

Supposons que vous vouliez identifier non seulement l'entrepôt avec le stock total le plus élevé mais aussi les entrepôts avec une grande variabilité dans les niveaux de stock et le prix moyen par unité à travers différents emplacements. Vous pouvez appliquer plusieurs fonctions d'agrégation en une seule étape en utilisant la fonction agg() :

python
# Group by warehouse and aggregate stock level and price per unit
summary_stock_price = df.groupby('warehouse').agg({
    'stock_level': ['sum', 'mean', 'median', 'count', 'min', 'max'],
    'price_per_unit': 'mean'
})

# Display the summary statistics
print(summary_stock_price)

# Output:
#             stock_level                                    price_per_unit
#                    sum   mean median count  min  max              mean
# warehouse
# warehouse_1        360  120.0    110     3  100  150        10.666667
# warehouse_2        365  121.7    120     3  115  130        11.333333
# warehouse_3        380  190.0    190     2  180  200         8.000000

Pour chaque entrepôt, nous obtenons un résumé des niveaux de stock et des prix.

Les colonnes du DataFrame résultant sont organisées dans une structure hiérarchique, avec les fonctions d'agrégation comme niveau supérieur et les noms de colonnes originaux comme second niveau.

Pour accéder à des données spécifiques dans ce DataFrame à index multiple, par exemple :

python
# Access the sum column of stock_level
print(summary_stock_price['stock_level']['sum'])

#ou pour un indexage plus explicite avec la notation .loc
# Access the sum column of stock_level
print(summary_stock_price.loc[:, ('stock_level', 'sum')])

Agrégation en plusieurs colonnes

Que faire si nous devons analyser des données basées sur plusieurs catégories ? Nous pouvons grouper par plus d'une colonne pour créer des agrégations multi-niveaux. Par exemple, groupons les données par Warehouse et Product pour examiner les niveaux de stock et le niveau de réapprovisionnement :

python
# Group by warehouse and product, then aggregate stock level and reorder level
multi_grouped = df.groupby(['warehouse', 'product']).agg({
    'stock_level': 'sum',
    'reorder_level': 'mean'
})
print(multi_grouped)

# Output:
#                         stock_level  reorder_level
# warehouse   product
# warehouse_1 widget_a           250            50.0
#             widget_b           110            55.0
# warehouse_2 widget_a           115            65.0
#             widget_b           250            60.0
# warehouse_3 widget_c           380            72.5

Le DataFrame résultant utilise un multi-index (avec Warehouse et Product comme niveaux d'index). Vous pouvez accéder à des groupes spécifiques en utilisant la méthode .loc[] ou effectuer des analyses supplémentaires en dépliant ou en remodelant les données.

Par exemple, si vous voulez accéder aux données pour Warehouse_1 et Widget_A, vous pourriez utiliser :

python
# Access data for a specific warehouse and product combination
print(multi_grouped.loc[('warehouse_1', 'widget_a')])

# Output:
# stock_level      250.0
# reorder_level     50.0
# Name: (warehouse_1, widget_a), dtype: float64

Lorsque vous avez des données groupées avec plusieurs niveaux (multi-index), cela peut compliquer les manipulations ou la lecture. Réinitialiser l'index en utilisant reset_index() simplifie la structure et rend les données plus faciles à utiliser, surtout pour des calculs simples ou pour les exporter.

Cela transformera l'index complexe en table avec colonnes normales, simplifiant ainsi la manipulation des données :

python
# Reset the multi-index to create a flat DataFrame
reset_df = multi_grouped.reset_index()
print(reset_df)

# Output:
#      warehouse  product  stock_level  reorder_level
# 0  warehouse_1 widget_a         250           50.0
# 1  warehouse_1 widget_b         110           55.0
# 2  warehouse_2 widget_a         115           65.0
# 3  warehouse_2 widget_b         250           60.0
# 4  warehouse_3 widget_c         380           72.5

Si vous voulez réinitialiser uniquement l'index Product tout en gardant Warehouse comme index, vous pouvez utiliser la fonction reset_index() avec le paramètre level='Product'.

Exercice

Groupement et agrégation des données de ventes

Votre responsable vous a demandé d'analyser les données de ventes pour découvrir des insights clés qui aideront à améliorer les stratégies commerciales. Vous utiliserez le jeu de données complete_data_with_time_features.csv, qui contient les transactions de vente à travers différentes régions, produits et périodes. Ce fichier se trouve dans votre dossier exercise du répertoire data_wrangling.

Aperçu du jeu de données :

Nous utiliserons les colonnes suivantes :

  • region_id : La région où la vente a eu lieu
  • product_name : Le produit vendu
  • quantity : Le nombre d'unités vendues
  • price : Le prix par unité du produit

Assurez-vous de copier complete_data_with_time_features.csv dans le dossier exercise du dossier Grouping_Aggregating.

Tâche 1 : Configurer votre Notebook

  1. Ouvrez un nouveau notebook Jupyter nommé sales_groupby_aggregation.ipynb dans le dossier exercise du dossier Grouping_Aggregating.
  2. Chargez le jeu de données complete_data_with_time_features.csv dans un DataFrame pandas. :paperclip: complete_data_with_time_features.csv

Tâche 2 : Groupement et agrégation

  1. Grouper par région et agréger les ventes :

    • Votre responsable veut savoir quelles régions performent le mieux en termes de ventes. Groupez les données par la colonne region_id.
    • Créez la colonne 'sales_amount' en multipliant les colonnes quantity et price.
    • Calculez les ventes totales pour chaque région.

    Conseil : Utilisez la fonction groupby() pour grouper les données par region_id puis utilisez la fonction sum() pour agréger les ventes totales. Si besoin, vous pouvez transformer le nom des colonnes de votre DataFrame df en minuscule avec la commande : df.columns = df.columns.str.lower().

  2. Analyser les données pour des groupes spécifiques :

    • Étape 1 : Identifier la région avec les ventes totales les plus élevées

      • Utilisez la fonction idxmax() sur les ventes totales agrégées pour trouver la région avec les ventes totales les plus élevées.
      • Stockez le résultat dans une variable nommée top_region.

      Conseil : idxmax() renvoie l'index de la valeur maximum dans une Series. Utilisez cette fonction pour trouver la région avec les ventes totales les plus élevées.

    • Étape 2 : Extraire les données pour la région avec les ventes totales les plus élevées

      • Filtrez le DataFrame original pour n'inclure que les lignes où la region est égale à top_region. Cela vous donnera toutes les données de ventes pour la région avec les ventes totales les plus élevées.
      • Stockez ce DataFrame filtré dans une variable nommée region_data.

      Conseil : Utilisez l'indexation booléenne pour filtrer le DataFrame.

    • Étape 3 : Trouver les 3 produits avec la quantité vendue la plus élevée dans la meilleure région

      • Maintenant que vous avez les données de ventes pour la région avec les ventes totales les plus élevées (region_data), groupez ces données par product et calculez la quantity totale vendue pour chaque produit.
      • Utilisez la fonction nlargest() pour trouver les 3 produits avec la quantité totale vendue la plus élevée.

      Conseil : nlargest(3) renverra les 3 valeurs les plus élevées de la Series.

Solution

Voir la solution

Grouper par région et agréger les ventes

python
# Create a 'sales_amount' column
df['sales_amount'] = df['quantity'] * df['price']

# Group by region and calculate total sales
total_sales_by_region = df.groupby('region_id')['sales_amount'].sum()

# Display the result
print(total_sales_by_region)

Voir la solution

Identifier la région avec les ventes totales les plus élevées

python
# Use idxmax to find the region with the maximum total sales
top_region = total_sales_by_region.idxmax()
print(f"The region with the highest total sales is: {top_region}")

Voir la solution

Extraire les données pour la région avec les ventes totales les plus élevées

python
# Extract data for the top region
region_data = df[df['region_id'] == top_region]
print(region_data.head())

Voir la solution

Trouver les 3 produits avec la quantité vendue la plus élevée dans la meilleure région

python
# Group by product and calculate total quantity sold in the top region
total_quantity_by_product = region_data.groupby('product')['quantity'].sum()

# Find the top 3 products with the highest quantity sold
top_products = total_quantity_by_product.nlargest(3)
print("Top 3 products by quantity sold in the top region:")
print(top_products)

Les métriques de base comme la somme, la moyenne, ou le décompte ne racontent souvent pas toute l'histoire. Par exemple, si vous gérez des stocks dans plusieurs entrepôts, vous pourriez vouloir voir comment les niveaux de stock varient. Vous pouvez faire cela en calculant l'étendue ou l'intervalle interquartile (IQR). Une autre mesure utile est le coefficient de variation (CV), qui vous indique à quel point les niveaux de stock fluctuent par rapport à la moyenne.

Continuons à utiliser le jeu de données représentant les données d'inventaire à travers différents entrepôts :

python
data = {
    'Warehouse': ['Warehouse_1', 'Warehouse_1', 'Warehouse_2', 'Warehouse_2', 'Warehouse_3'],
    'Product': ['Widget_A', 'Widget_B', 'Widget_A', 'Widget_B', 'Widget_C'],
    'Stock_Level': [250, 110, 115, 250, 380],
    'Reorder_Level': [50.0, 55.0, 65.0, 60.0, 72.5]
}

df = pd.DataFrame(data)
print(df)

# Output:
#      Warehouse  Product  Stock_Level  Reorder_Level
# 0  Warehouse_1 Widget_A         250           50.0
# 1  Warehouse_1 Widget_B         110           55.0
# 2  Warehouse_2 Widget_A         115           65.0
# 3  Warehouse_2 Widget_B         250           60.0
# 4  Warehouse_3 Widget_C         380           72.5

D'abord, définissons nos fonctions d'agrégation personnalisées pour calculer l'étendue, le coefficient de variation et l'intervalle interquartile des niveaux de stock :

python
def stock_range(series):
    return series.max() - series.min()

def coeff_variation(series):
    return series.std() / series.mean()

def interquartile_range(series):
    q3 = series.quantile(0.75)
    q1 = series.quantile(0.25)
    return q3 - q1

Maintenant, groupons les données par la colonne Warehouse et appliquons ces fonctions personnalisées sur la colonne Stock_Level en utilisant la méthode agg() :

python
custom_agg = df.groupby('Warehouse').agg({
    'Stock_Level': [stock_range, coeff_variation, interquartile_range],
    'Reorder_Level': 'mean'
})

print("Custom Aggregation Results:")
print(custom_agg)

# Output:
# Custom Aggregation Results:
#
#             Stock_Level                                          Reorder_Level
#              stock_range      coeff_variation interquartile_range         mean
# Warehouse
# Warehouse_1        140.0             0.393855              70.00         52.5
# Warehouse_2        135.0             0.384615              67.50         62.5
# Warehouse_3          0.0             0.000000               0.00         72.5

Filtrage des groupes basé sur les valeurs agrégées

Imaginez que vous analysez les niveaux d'inventaire à travers plusieurs entrepôts et voulez inclure uniquement ceux avec un niveau de stock total au-dessus d'un certain seuil. En fixant un seuil (par exemple, 370 unités), vous pouvez filtrer les entrepôts qui pourraient être insignifiants pour votre analyse.

Cet exemple démontre comment filtrer au sein des groupes basés sur les valeurs agrégées :

python
def filter_by_total_stock(group):
    return group['Stock_Level'].sum() > 370

filtered_df = df.groupby('Warehouse').filter(filter_by_total_stock)

print("\nFiltered Data (Total Stock Level > 370):")

print(filtered_df)

# Output:
#      Warehouse  Product  Stock_Level  Reorder_Level
# 4  Warehouse_3 Widget_C         380           72.5

La fonction filter_by_total_stock filtre les groupes qui ne répondent pas à une condition spécifiée. Dans ce cas, elle filtre basé sur le niveau de stock total dans chaque groupe. L'entrée group représente chaque groupe créé par groupby(). Dans ce contexte, chaque groupe est essentiellement un sous-ensemble du DataFrame original, contenant les lignes qui appartiennent au même entrepôt.

Nous pouvons aussi filtrer en utilisant une des fonctions d'agrégation personnalisées définies précédemment, avec un seuil de 50 ici :

python
def stock_range(series): # custom aggregation function
    return series.max() - series.min()

def filter_by_stock_range(group):
    return stock_range(group['Stock_Level']) > 50

filtered_df = df.groupby('Warehouse').filter(filter_by_stock_range)

print("\nFiltered Data (Stock Range > 50):")
print(filtered_df)

# Output:
#      Warehouse  Product  Stock_Level  Reorder_Level
# 0  Warehouse_1 Widget_A         250           50.0
# 1  Warehouse_1 Widget_B         110           55.0
# 2  Warehouse_2 Widget_A         115           65.0
# 3  Warehouse_2 Widget_B         250           60.0

Exercice

Création d’une nouvelle métrique d’agrégation

Vous avez appris à calculer l’étendue, le coefficient de variation et l’intervalle interquartile des niveaux de stock. Maintenant, ajoutez une nouvelle métrique personnalisée qui calcule l’écart moyen absolu (Mean Absolute Deviation, MAD) des niveaux de stock dans chaque entrepôt.

Objectif :

  1. Définissez une fonction mean_absolute_deviation(series) qui calcule l’écart moyen absolu d’une série de niveaux de stock.
  2. Appliquez cette fonction avec groupby().agg() pour obtenir la MAD de Stock_Level pour chaque entrepôt.
  3. Affichez les résultats sous forme de DataFrame.

Indices :

  1. La MAD est calculée comme ceci :

    $$ MAD = \frac{1}{n} \sum_{i=1}^{n} |x_i - \bar{x}| $$

    où  $\bar{x}$  est la moyenne de la série et  $x_i$  sont les valeurs individuelles.

  2. Utilise series.mean() pour calculer la moyenne $\bar{x}$ d’une série Pandas.

  3. Soustrais cette moyenne à chaque élément de la série, puis prends la valeur absolue avec .abs().

  4. ****Fais la moyenne des écarts absolus avec .mean().

  5. Applique cette fonction aux niveaux de stock dans chaque entrepôt avec .groupby('Warehouse').agg(...).

Début du code :

python
import pandas as pd

data = {
    "Warehouse": [
        "Warehouse_1",
        "Warehouse_1",
        "Warehouse_2",
        "Warehouse_2",
        "Warehouse_3",
    ],
    "Product": ["Widget_A", "Widget_B", "Widget_A", "Widget_B", "Widget_C"],
    "Stock_Level": [250, 110, 115, 250, 380],
    "Reorder_Level": [50.0, 55.0, 65.0, 60.0, 72.5],
}

df = pd.DataFrame(data)

# TODO: Define the mean_absolute_deviation() function
# TODO: Group by 'Warehouse' and apply this function with agg()
# TODO: Display the resulting DataFrame

# Output:
# Custom Aggregation Results:
#                         Stock_Level        Reorder_Level
#             mean_absolute_deviation   mean          mean
# Warehouse
# Warehouse_1                    70.0  180.0          52.5
# Warehouse_2                    67.5  182.5          62.5
# Warehouse_3                     0.0  380.0          72.5

Solution

Voir la solution

Explication :

1. Définition de la fonction mean_absolute_deviation()

  • On calcule la moyenne de la série avec .mean().
  • On soustrait cette moyenne à chaque valeur de la série.
  • On prend la valeur absolue de ces écarts et on calcule la moyenne de ces valeurs absolues.

2. Application de groupby().agg()

  • On groupe les données par Warehouse.
  • On applique la fonction mean_absolute_deviation sur Stock_Level.
  • On ajoute également la moyenne (mean) pour voir la comparaison.
  • On calcule aussi la moyenne de Reorder_Level pour voir les tendances générales.

3. Affichage des résultats

  • Un DataFrame est affiché avec la MAD des niveaux de stock pour chaque entrepôt.
python
import pandas as pd

# Inventory data
data = {
    "Warehouse": [
        "Warehouse_1",
        "Warehouse_1", 
        "Warehouse_2",
        "Warehouse_2",
        "Warehouse_3",
    ],
    "Product": ["Widget_A", "Widget_B", "Widget_A", "Widget_B", "Widget_C"],
    "Stock_Level": [250, 110, 115, 250, 380],
    "Reorder_Level": [50.0, 55.0, 65.0, 60.0, 72.5],
}

df = pd.DataFrame(data)

# Function to calculate Mean Absolute Deviation (MAD)
def mean_absolute_deviation(series):
    mean = series.mean()
    return (series - mean).abs().mean()

# Apply custom aggregation
custom_agg = df.groupby("Warehouse").agg(
    {"Stock_Level": [mean_absolute_deviation, "mean"], "Reorder_Level": "mean"}
)

print("Custom Aggregation Results:")
print(custom_agg)

# Output:
# Custom Aggregation Results:
#                         Stock_Level        Reorder_Level
#             mean_absolute_deviation   mean          mean
# Warehouse                                               
# Warehouse_1                    70.0  180.0          52.5
# Warehouse_2                    67.5  182.5          62.5
# Warehouse_3                     0.0  380.0          72.5

Lorsque vous travaillez avec des données groupées dans Pandas, vous avez souvent besoin de plus que de simples résumés agrégés comme les totaux ou les moyennes. Par exemple, si vous analysez les niveaux de stock à travers plusieurs entrepôts, vous pourriez vouloir voir comment le stock de chaque produit se compare au niveau de stock moyen dans son entrepôt. Vous pourriez aussi avoir besoin de classer les produits au sein de chaque entrepôt basé sur les niveaux de stock pour identifier quels articles sont les plus ou moins stockés.

Souvent, vous devez maintenir la structure originale de votre jeu de données tout en effectuant des calculs qui prennent en compte le contexte au niveau du groupe. Pour cela, vous pouvez utiliser la fonction `transform()` dans Pandas. Considérons à nouveau le jeu de données représentant les données d'inventaire à travers différents entrepôts :

```python
data = {
    'warehouse': ['warehouse_1', 'warehouse_1', 'warehouse_2', 'warehouse_2', 'warehouse_3'],
    'product': ['widget_a', 'widget_b', 'widget_a', 'widget_b', 'widget_c'],
    'stock_level': [250, 110, 115, 250, 380],
    'reorder_level': [50.0, 55.0, 65.0, 60.0, 72.5]
}

df = pd.DataFrame(data)
print(df)

# Output:
#      warehouse  product  stock_level  reorder_level
# 0  warehouse_1 widget_a         250           50.0
# 1  warehouse_1 widget_b         110           55.0
# 2  warehouse_2 widget_a         115           65.0
# 3  warehouse_2 widget_b         250           60.0
# 4  warehouse_3 widget_c         380           72.5

Supposons que nous devons calculer comment le niveau de stock de chaque produit se compare au niveau de stock moyen dans son entrepôt :

python
# Calculate the mean stock level for each warehouse
groups_mean = df.groupby(by=['warehouse'])['stock_level'].transform('mean')
print(groups_mean)

# Output:
# 0    180.0
# 1    180.0
# 2    182.5
# 3    182.5
# 4    380.0
# Name: stock_level, dtype: float64

# Calculate the difference between each product's stock level and its warehouse's mean
df['stock_average'] = df['stock_level'] - groups_mean

print(df)

# Output:
#      warehouse   product  stock_level  reorder_level  stock_average
# 0  warehouse_1  widget_a         250           50.0           70.0
# 1  warehouse_1  widget_b         110           55.0          -70.0
# 2  warehouse_2  widget_a         115           65.0          -67.5
# 3  warehouse_2  widget_b         250           60.0           67.5
# 4  warehouse_3  widget_c         380           72.5            0.0

Ce code calcule le niveau de stock moyen pour chaque entrepôt et assigne cette moyenne à chaque ligne appartenant à cet entrepôt. Décomposons la sortie :

  • Lignes 0 et 1 (180.0) : Elles appartiennent à warehouse_1, qui a un niveau de stock moyen de 180.
  • Lignes 2 et 3 (182.5) : Elles appartiennent à warehouse_2, avec un niveau de stock moyen de 182.5.
  • Ligne 4 (380.0) : Elle appartient à warehouse_3, qui n'a qu'un seul article avec un niveau de stock de 380.

La fonction transform() applique le calcul de la moyenne à chaque groupe (entrepôt) puis diffuse le résultat à la forme originale du DataFrame, maintenant l'index original.

Après avoir calculé le niveau de stock moyen pour chaque entrepôt en utilisant la fonction transform('mean'), le code soustrait ensuite cette moyenne du niveau de stock de chaque produit pour créer une nouvelle colonne appelée stock_average.

Transformations personnalisées

Comme avec l'agrégation, nous pouvons définir des transformations personnalisées à appliquer au sein des groupes. Par exemple, nous pourrions vouloir calculer le pourcentage de contribution du niveau de stock de chaque produit au stock total dans son entrepôt. Définissons une fonction pour cet objectif :

python
# Define a function to calculate the percentage of stock for each product within its warehouse
def stock_percentage(group):
    # Calculate the total stock level for the group (warehouse)
    total_group = group['stock_level'].sum()
    # Return the percentage of each product's stock relative to the total
    return group['stock_level'] / total_group * 100

Maintenant, appliquons cette transformation à chaque groupe et créons une nouvelle colonne :

python
# Apply the custom transformation to calculate stock percentage for each product within its warehouse
df['stock_perc'] = df.groupby('warehouse')['stock_level'].transform(stock_percentage)

# Display the updated DataFrame
print(df)

# Output:
#      warehouse  product  stock_level  reorder_level  stock_average  stock_perc
# 0  warehouse_1 widget_a         250           50.0           70.0      69.44
# 1  warehouse_1 widget_b         110           55.0          -70.0      30.56
# 2  warehouse_2 widget_a         115           65.0          -67.5      31.51
# 3  warehouse_2 widget_b         250           60.0           67.5      68.49
# 4  warehouse_3 widget_c         380           72.5            0.0     100.00

Imputation des valeurs manquantes en fonction des groupes avec transform()

Dans un environnement commercial complexe, comme la gestion des stocks à travers plusieurs entrepôts, il est essentiel que les données soient aussi précises que possible pour prendre des décisions éclairées. Lorsque certaines valeurs sont manquantes, il peut être plus pertinent de les imputer en utilisant des statistiques spécifiques à chaque groupe (par exemple, par entrepôt), plutôt que de se fier à une mesure globale comme la moyenne ou la médiane générale. Par exemple, les niveaux de stock peuvent varier considérablement d'un entrepôt à l'autre, en fonction de la demande locale, des capacités de stockage, ou des stratégies de réapprovisionnement.

Si vous remplacez les valeurs manquantes par la moyenne globale, vous risquez d'obtenir des données qui ne reflètent pas ces différences et qui pourraient fausser les décisions liées à l'approvisionnement ou à la gestion des stocks.

Voyons comment résoudre cela en considérant à nouveau le jeu de données représentant les données d'inventaire à travers différents entrepôts où vous avez des valeurs manquantes dans la colonne stock_level et reorder_level :

python
# Define the data dictionary
data = {
    'warehouse': ['warehouse_1', 'warehouse_1', 'warehouse_1', 'warehouse_2', 'warehouse_2', 'warehouse_3'],
    'product': ['widget_a', 'widget_b', 'widget_c', 'widget_a', 'widget_b', 'widget_c'],
    'stock_level': [250, None, 115, 134, 250, 380],
    'reorder_level': [50.0, None, 65.0, 45.0, 60.0, 72.5]
}

# Create DataFrame
df = pd.DataFrame(data)

# Display the number of missing values in each column
print(df.isna().sum())

# Output:
# warehouse      0
# product        0
# stock_level    1
# reorder_level  1
# dtype: int64

Créons une fonction qui définit notre stratégie d'imputation puis appliquons-la à nos données groupées en utilisant la méthode transform().

python
# Define the imputation function
def impute(series):
    return series.fillna(series.mean())

# Apply the function using transform
df[['stock_level', 'reorder_level']] = df.groupby('warehouse')[['stock_level', 'reorder_level']].transform(impute)

print(df)

# Output:
#      warehouse   product  stock_level  reorder_level
# 0  warehouse_1  widget_a       250.0           50.0
# 1  warehouse_1  widget_b       182.5           57.5
# 2  warehouse_1  widget_c       115.0           65.0
# 3  warehouse_2  widget_a       134.0           45.0
# 4  warehouse_2  widget_b       250.0           60.0
# 5  warehouse_3  widget_c       380.0           72.5

Dans ce code, nous définissons une fonction d'imputation qui remplit les valeurs manquantes dans une série avec sa moyenne. En utilisant transform(), la fonction est appliquée aux colonnes stock_level et reorder_level, groupées par la colonne warehouse. L'imputation se produit au sein de chaque groupe séparément, et les valeurs mises à jour sont directement appliquées aux colonnes originales.

Excellent travail ! Vous savez maintenant comment utiliser les fonctions de groupement comme groupby() pour diviser un DataFrame en groupes basés sur des variables catégorielles et effectuer des opérations d'agrégation au sein des groupes, en utilisant des méthodes comme sum() oumean() pour calculer des statistiques résumées pour chaque catégorie.

Enfin, vous avez exploré comment utiliser des transformations personnalisées au sein des groupes pour effectuer des calculs plus complexes, comme appliquer des fonctions pour standardiser les valeurs au sein de chaque groupe, calculer des sommes cumulatives, ou normaliser des données entre 0 et 1. Les techniques comme transform() vous permettent d'aller au-delà des agrégations standard et de créer des calculs personnalisés adaptés à vos besoins spécifiques d'analyse.

Continuez votre excellent travail !