Quels analytics patterns un data scientist doit maîtriser ?

Les patterns-clés sont joins + filters, fonctions fenêtre, agrégations/grouping et pivoting, appliqués via SQL réutilisable. Ces schémas, démontrés par exercices d’entretien (StrataScratch) et la doc PostgreSQL, structurent l’analyse et facilitent la réutilisabilité des pipelines analytiques.

Comment joindre et filtrer pour isoler un sous-ensemble ?

On identifie d’abord la table primaire, on joint les données complémentaires, puis on applique des filtres pour isoler le sous-ensemble pertinent.

Étapes logiques :

🚀 Maîtrisez les outils Web Analytics et optimisez votre croissance dès aujourd’hui

Transformez vos données en leviers de performance ! Nos formations en Web Analytics vous permettent de mesurer, analyser et perfectionner l’expérience utilisateur de votre site avec précision. De Google Tag Manager à Piwik Pro, en passant par Matomo Analytics et Google Analytics 4, nous vous guidons à chaque niveau pour une maîtrise complète des outils essentiels. Apprenez à structurer vos données, affinez votre stratégie digitale et prenez des décisions basées sur des insights fiables. Ne laissez plus vos performances au hasard : formez-vous et passez à l’action dès maintenant !

  • Choisir la table primaire qui représente l’entité d’analyse (par exemple flight_schedule pour des vols).
  • Joindre les tables complémentaires en définissant clairement la condition de jointure (INNER JOIN si la correspondance est obligatoire, LEFT JOIN si vous voulez conserver les lignes sans correspondance).
  • Appliquer les filtres métiers après ou dans la jointure selon l’effet voulu (filtre dans ON limite les lignes jointes, filtre dans WHERE peut exclure les lignes LEFT JOIN nullifiées).
  • Contrôler la cardinalité et dédupliquer si nécessaire (agrégation, DISTINCT ON, row_number()).
SELECT
  fs.flight_id,
  fs.origin,
  fs.destination,
  fs.duration_minutes AS flight_minutes,
  ec.movie_id,
  ec.title,
  ec.duration_minutes AS movie_minutes
FROM flight_schedule fs
INNER JOIN entertainment_catalog ec
  ON ec.duration_minutes <= fs.duration_minutes
WHERE fs.flight_date = CURRENT_DATE
ORDER BY fs.flight_id, ec.duration_minutes DESC;

Quand INNER JOIN vs LEFT JOIN : INNER JOIN quand la correspondance est nécessaire et que les lignes sans correspondance n'ont pas de valeur métier. LEFT JOIN quand vous voulez conserver la table primaire même si la table secondaire manque.

Pourquoi l'ordre compte : Placer la table primaire à gauche améliore la lisibilité et, pour certains moteurs, facilite l'optimiseur et la planification d'accès aux index.

Cas d'usage métier :

  • RH : L'entrée est employees, joint sur trainings pour filtrer les employés ayant suivi une formation spécifique (WHERE training_date > ...).
  • Retail : L'entrée est sales_orders, joint sur product_catalog pour sélectionner les commandes contenant des produits en promotion (WHERE discount IS NOT NULL).
  • Streaming : L'entrée est user_sessions, joint sur content_meta pour recommander les titres dont la durée ≤ durée restante de la session.

Pièges fréquents et recommandations :

  • Cardinalité élevée → Peut exploser le volume, recommandez d'agréger ou filtrer avant la jointure.
  • Duplication de lignes → Utiliser DISTINCT ON (Postgres) ou row_number() pour dédupliquer.
  • Filtres dans JOIN vs WHERE → Mettre les conditions d'association dans ON, les conditions de post-filtre dans WHERE pour éviter d'éliminer des LEFT JOIN.
  • Nulls → Traiter explicitement les valeurs NULL avec COALESCE si nécessaire.
  • Index → Créer index sur les clés de jointure; pour inégalités, pré-filtrer la table la plus volumineuse.
ÉtapeAction SQLObjectifRisque
Identifier table primaireFROM primary_table pPoint de départ de l'analyseMauvaise granularité
JoindreJOIN other o ON ...Enrichir les donnéesExplosion des lignes
FiltrerWHERE ...Isoler le sous-ensembleÉlimination involontaire de lignes LEFT JOIN

Quand utiliser les fonctions fenêtre pour classer ?

On utilise les fonctions fenêtre pour classer ou ordonner des lignes à l’intérieur de partitions sans réduire le niveau de granularité.

RANK(), DENSE_RANK() et ROW_NUMBER() calculent des positions dans une partition.

  • RANK() attribue le même rang aux ex-æquo et laisse des "trous" dans la numérotation (ex. deux éléments à rang 1, suivant rang 3).
  • DENSE_RANK() attribue le même rang aux ex-æquo mais sans trous (ex. deux éléments à rang 1, suivant rang 2).
  • ROW_NUMBER() donne un numéro unique à chaque ligne, même si les valeurs sont identiques, utile pour désambiguïser.

Exemple simple illustrant un ex-æquo : si trois posts ont 100, 100 et 90 likes alors RANK → 1,1,3 ; DENSE_RANK → 1,1,2 ; ROW_NUMBER → 1,2,3 (ordre déterminé par ORDER BY additionnel si nécessaire).

Exemple PostgreSQL pour "top 3 posts par channel" avec gestion des égalités via CTE :

WITH ranked AS (
  SELECT
    id,
    channel,
    likes,
    RANK() OVER (PARTITION BY channel ORDER BY likes DESC) AS rnk
  FROM posts
)
SELECT id, channel, likes, rnk
FROM ranked
WHERE rnk <= 3
ORDER BY channel, rnk;

Conseils pratiques :

  • Choisir la clé de partition en fonction du périmètre métier (par ex. region, channel, course).
  • Définir l'ordre (ORDER BY) avec les colonnes pertinentes et des tie-breakers si besoin (date, id) pour reproductibilité.
  • Anticiper l'impact performance : index sur (partition_key, order_key DESC) peut accélérer les scans ; envisager index couvrant si la table est large.
  • Utiliser ORDER BY dans la requête finale pour présenter les résultats ; les fonctions fenêtre ne trient pas implicitement l'ensemble final.

Trois cas d'usage métier :

  • Ventes : top produits par région — implémentation : PARTITION BY region ORDER BY revenue DESC.
  • Éducation : top étudiants par cours — implémentation : PARTITION BY course_id ORDER BY grade DESC, submission_date ASC.
  • Logistique : top hubs par volume — implémentation : PARTITION BY hub_id ORDER BY shipments DESC.
FonctionUsageComportement en cas d’égalitéQuand l’utiliser
RANK()Classement avec trousMême rang pour ex-æquo, laisse des sautsQuand les positions ordinales doivent refléter les égalités (ex. podium)
DENSE_RANK()Classement denseMême rang pour ex-æquo, pas de sautsQuand on veut des positions compactes sans trous
ROW_NUMBER()Numérotation uniqueJamais d’égalité (numéros uniques)Pour sélectionner une ligne par partition (top 1) de façon déterministe

Comment agréger et grouper pour résumer les données ?

On identifie la dimension de regroupement, on applique GROUP BY et les agrégats (COUNT, SUM, AVG) puis éventuellement HAVING pour filtrer les groupes.

Identifier la clé de regroupement signifie choisir la colonne temporelle, la catégorie ou l'identifiant qui répond au besoin métier. Appliquer GROUP BY calcule des résumés par groupe, les fonctions d'agrégat synthétisent les valeurs, et HAVING filtre après agrégation.

WITH sessions AS (
  SELECT DISTINCT user_id, date_trunc('day', started_at) AS day
  FROM sessions
),
orders AS (
  SELECT id, user_id, date_trunc('day', created_at) AS day, total_amount
  FROM orders
)
SELECT
  s.day,
  COUNT(o.id) AS total_orders,
  SUM(o.total_amount) AS total_value
FROM sessions s
JOIN orders o ON s.user_id = o.user_id AND s.day = o.day
GROUP BY s.day
HAVING COUNT(o.id) >= 1
ORDER BY s.day;

La requête précédente identifie les jours où un utilisateur a à la fois démarré une session et passé commande le même jour, puis calcule le nombre total de commandes et la valeur totale par jour.

Pour obtenir sous-totaux et total global, utiliser ROLLUP ou GROUPING SETS.

Syntaxe PostgreSQL standard : GROUP BY ROLLUP(col1, col2) ou GROUP BY GROUPING SETS ((col1, col2), (col1), ());

SELECT date_trunc('month', created_at) AS month, product_category,
       SUM(amount) AS total
FROM sales
GROUP BY ROLLUP (date_trunc('month', created_at), product_category);
  • Attention aux NULL dans GROUP BY : Les NULL forment un groupe unique, penser à COALESCE si nécessaire.
  • Agrégats filtrés : Utiliser SUM(CASE WHEN condition THEN value ELSE 0 END) pour filtrer dans l'agrégat.
  • Ordre des filtres : WHERE réduit les lignes avant l'agrégation, HAVING filtre les groupes après agrégation.
  • Optimisation : Pré-agréger, réduire la cardinalité (par ex. bucketiser les dates) et indexer les colonnes de jointure.

Cas d'usage :

  • E-commerce : Agréger par jour et catégorie pour suivre ventes et panier moyen par segment.
  • SaaS : Grouper par plan et mois pour calculer churn, MRR et tendances par cohortes.
  • Finance : Agréger par instrument et jour pour mesurer volume, VaR et exposition par classe d'actifs.
ObjectifClause SQLRecommandation
Résumé par cléGROUP BY colIndexer la clé et réduire les colonnes avant d'agréger
Sous-totaux et totalGROUP BY ROLLUP(...) / GROUPING SETS(...)Utiliser ROLLUP pour hiérarchies, GROUPING SETS pour combinaisons spécifiques
Filtrer groupesHAVING conditionAppliquer WHERE d'abord, utiliser HAVING pour conditions sur agrégats

Comment pivotiser des lignes en colonnes efficacement ?

Le pivot transforme des valeurs de lignes en colonnes pour faciliter la lecture ou alimenter des dashboards, via des agrégations conditionnelles ou l’extension crosstab de PostgreSQL.

Approche 1 — Agrégations conditionnelles avec FILTER. Cette méthode utilise des agrégations standard avec un filtre conditionnel par colonne cible. Exemple pour le paiement le plus élevé par méthode et par client :

SELECT
  customer_id,
  MAX(amount) FILTER (WHERE payment_method = 'card')   AS card_max,
  MAX(amount) FILTER (WHERE payment_method = 'cash')   AS cash_max,
  MAX(amount) FILTER (WHERE payment_method = 'bank_transfer') AS bank_transfer_max
FROM payments
GROUP BY customer_id;

Approche 2 — tablefunc.crosstab. Cette extension pivote en une seule opération, mais nécessite des colonnes fixes (les catégories). Exemple par mois :

CREATE EXTENSION IF NOT EXISTS tablefunc;

SELECT *
FROM crosstab(
  $$
  SELECT date_trunc('month', paid_at)::date AS month, payment_method, MAX(amount) AS max_amount
  FROM payments
  GROUP BY 1,2
  ORDER BY 1,2
  $$,
  $$ VALUES ('card'), ('cash'), ('bank_transfer') $$
) AS ct(month date, card numeric, cash numeric, bank_transfer numeric);

Cette même logique s'applique pour pivoter par client en remplaçant date_trunc par customer_id dans la première requête.

  • Comparaison rapide : FILTER offre une grande flexibilité et s'adapte aux catégories dynamiques via SQL généré; crosstab offre de bonnes performances pour des rapports fixes mais exige des colonnes prédéfinies.
  • Points pratiques : Vérifier l'ordre des colonnes avec crosstab; gérer les valeurs manquantes avec COALESCE(...,0) si besoin; générer dynamiquement la liste des catégories pour FILTER si elles sont nombreuses.
  • Recommandation : Utiliser FILTER pour dashboards dynamiques et crosstab pour exports ou rapports réguliers et contrôlés.
MéthodeQuand l’utiliserComplexitéExemple d’usage
FILTERDashboards dynamiques, catégories variablesFaible à moyenValeur max par méthode par client
crosstabExports fixes, rapports périodiquesMoyen (colonnes fixes)Tableau mensuel des max par méthode

Prêt à appliquer ces patterns pour fiabiliser vos analyses ?

Ces quatre patterns — joins + filters, fonctions fenêtre, aggregation/grouping et pivoting — forment le socle de l’analyse SQL réutilisable. Maîtrisés, ils permettent de résoudre la majorité des besoins analytiques métier, d'améliorer la reproductibilité et de réduire le temps d'investigation. En appliquant ces schémas vous gagnerez en robustesse, vitesse d'exécution et clarté des livrables analytiques. Passez de l'exploration ponctuelle à des pipelines analytiques fiables pour vos décisions.

FAQ

Quels sont les 4 patterns analytiques essentiels ?
Les quatre patterns essentiels sont joins + filters, fonctions fenêtre (ranking/ordering), aggregation + grouping (incluant ROLLUP/GROUPING SETS) et pivoting (agrégations conditionnelles ou crosstab). Ils couvrent la majorité des besoins analytiques métier.
Quand utiliser RANK() vs ROW_NUMBER() ?
ROW_NUMBER() attribue un rang unique même en cas d'ex-aequo; RANK() laisse des égalités et crée des trous dans la numérotation; DENSE_RANK() laisse des égalités sans trous. Choisissez selon le traitement souhaité des égalités et le filtrage ultérieur.
Quelle méthode pour pivoter dans PostgreSQL ?
Deux approches : agrégations conditionnelles avec FILTER (flexible, fonctionne sans extension) ou tablefunc.crosstab (plus performante pour sorties fixes mais exige colonnes connues à l'avance). Choisir selon dynamisme du rapport et contraintes de performance.
Comment éviter les doublons après jointure ?
Vérifiez la cardinalité des clés de jointure, privilégiez des pré-agrégations si nécessaire, utilisez DISTINCT ON ou GROUP BY pour dédupliquer, et ajoutez tests unitaires (ex. compter les lignes avant/après) pour valider les transformations.
Quels gains attendus en standardisant ces patterns ?
Standardiser réduit le temps de production (réutilisabilité des requêtes), diminue les erreurs, facilite la revue et l'industrialisation vers des pipelines ETL/ELT. Concrètement : analyses plus rapides, maintenabilité accrue et meilleure traçabilité des données.

 

 

A propos de l'auteur

Franck Scandolera — expert & formateur en tracking avancé server-side, Analytics Engineering, automatisation No/Low Code (n8n) et intégration de l'IA en entreprise. Responsable de l'agence webAnalyste et de l'organisme de formation Formations Analytics. Références : Logis Hôtel, Yelloh Village, BazarChic, Fédération Française de Football, Texdecor. Dispo pour aider les entreprises => contactez moi.

Retour en haut
Formations Analytics