24 mois de ventes sur 9 points de vente et 2 canaux, agrégés différemment selon la métrique : somme pour les volumes, moyenne pour les taux. La heatmap repère les pics d'activité par créneau horaire.
Agrégations conditionnelles selon le type de métrique, requêtes SQL avec fonctions de fenêtrage, projection de tendance en Python, lecture de la saisonnalité.
Les ventes mensuelles et la répartition horaire des commandes partagent la même dimension « Point de vente », ce qui permet de croiser vue macro (tendance) et vue micro (pics d'activité) sans dupliquer le référentiel.
Voici quelques extraits de code utilisés pour la conception de ce projet.
Question métier : quels points de vente sont en croissance ou en décroissance sur la période, pour prioriser les actions commerciales ?
WITH ventes_mensuelles AS (
SELECT point_vente_id, DATE_TRUNC('month', mois) AS mois,
SUM(commandes) AS commandes
FROM fact_ventes
GROUP BY 1, 2
),
bornes AS (
SELECT
point_vente_id,
FIRST_VALUE(commandes) OVER (
PARTITION BY point_vente_id ORDER BY mois
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS debut,
LAST_VALUE(commandes) OVER (
PARTITION BY point_vente_id ORDER BY mois
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS fin
FROM ventes_mensuelles
)
SELECT DISTINCT
p.nom, debut, fin,
ROUND(100.0 * (fin - debut) / NULLIF(debut, 0), 1) AS croissance_pct
FROM bornes b
JOIN dim_point_vente p ON p.id_point_vente = b.point_vente_id
ORDER BY croissance_pct DESC;
Projection simple de la tendance (régression linéaire) — la même méthode alimente la ligne en pointillés du graphique d'évolution ci-dessus.
import numpy as np
import pandas as pd
serie = df.groupby("mois")["commandes"].sum().reset_index()
serie["t"] = np.arange(len(serie))
# Regression lineaire simple pour projeter les 2 prochains mois
a, b = np.polyfit(serie["t"], serie["commandes"], 1)
prochains_t = np.arange(len(serie), len(serie) + 2)
projection = a * prochains_t + b
print(f"Tendance : {a:+.0f} commandes / mois")
print("Projection a 2 mois :", projection.round(0).tolist())