{"id":6037,"date":"2026-10-14T18:02:04","date_gmt":"2026-10-14T18:02:04","guid":{"rendered":"http:\/\/french.arabpsychology.com\/statistics\/comment-trouver-la-valeur-mediane-par-groupe-dans-pandas\/"},"modified":"2026-10-14T18:02:04","modified_gmt":"2026-10-14T18:02:04","slug":"comment-trouver-la-valeur-mediane-par-groupe-dans-pandas","status":"publish","type":"psych_stat","link":"https:\/\/french.arabpsychology.com\/?psych_stat=comment-trouver-la-valeur-mediane-par-groupe-dans-pandas","title":{"rendered":"Comment trouver la valeur m\u00e9diane par groupe dans Pandas"},"content":{"rendered":"<p>Dans le champ contemporain de l&rsquo;analyse exploratoire des donn\u00e9es et de l&rsquo;\u00e9conom\u00e9trie computationnelle, l&rsquo;estimation des param\u00e8tres de tendance centrale constitue le socle indispensable \u00e0 la caract\u00e9risation des distributions statistiques. Si la moyenne arithm\u00e9tique demeure le premier r\u00e9flexe de synth\u00e8se en raison de ses propri\u00e9t\u00e9s math\u00e9matiques \u00e9l\u00e9gantes au sein des distributions gaussiennes, elle r\u00e9v\u00e8le une vuln\u00e9rabilit\u00e9 r\u00e9dhibitoire d\u00e8s lors que les s\u00e9ries empiriques manifestent une asym\u00e9trie marqu\u00e9e ou int\u00e8grent des valeurs aberrantes. Dans les jeux de donn\u00e9es multidimensionnels modernes, cette sensibilit\u00e9 aux extr\u00eames exige l&rsquo;adoption d&rsquo;estimateurs d&rsquo;une robustesse \u00e9prouv\u00e9e, au premier rang desquels figure la m\u00e9diane statistique. Or, la r\u00e9alit\u00e9 op\u00e9rationnelle impose rarement l&rsquo;\u00e9valuation d&rsquo;un vecteur isol\u00e9 : les observations sont presque syst\u00e9matiquement structur\u00e9es par des variables de stratification factorielles telles que des cat\u00e9gories sociod\u00e9mographiques, des groupes de traitement clinique ou des cohortes temporelles.<\/p>\n<p>L&rsquo;\u00e9cosyst\u00e8me de calcul scientifique Python, structur\u00e9 autour de la biblioth\u00e8que fondamentale <a rel=\"noopener noreferrer\" href=\"https:\/\/pandas.pydata.org\/docs\/\">Pandas<\/a>, offre des m\u00e9canismes vectoris\u00e9s extr\u00eamement sophistiqu\u00e9s pour op\u00e9rer ces segmentations d&rsquo;agr\u00e9gats. Gr\u00e2ce \u00e0 l&rsquo;impl\u00e9mentation algorithmique de la m\u00e9thode <code>groupby()<\/code>, la d\u00e9composition d&rsquo;une population statistique en sous-ensembles homog\u00e8nes et l&rsquo;application d&rsquo;estimateurs robustes s&rsquo;ex\u00e9cutent avec une efficacit\u00e9 computationnelle optimale. Ce paradigme algorithmique permet de d\u00e9passer les limitations inh\u00e9rentes aux it\u00e9rations s\u00e9quentielles en langage de haut niveau, en exploitant des structures de hachage optimis\u00e9es en C et Cython qui garantissent une scalabilit\u00e9 indispensable face aux flux massifs de donn\u00e9es contemporains.<\/p>\n<p>Le pr\u00e9sent article propose une exploration m\u00e9thodique, exhaustive et r\u00e9solument ancr\u00e9e dans la rigueur statistique de la d\u00e9termination de la valeur m\u00e9diane par groupe au sein de Pandas. En partant des fondements math\u00e9matiques de l&rsquo;invariance distributionnelle jusqu&rsquo;aux techniques d&rsquo;optimisation de bas niveau sur des structures de plusieurs millions d&rsquo;enregistrements, ce guide d\u00e9cortique les arcanes de la manipulation tabulaire. Qu&rsquo;il s&rsquo;agisse de traiter des croisements factoriels complexes, d&rsquo;arbitrer les dilemmes soulev\u00e9s par les donn\u00e9es manquantes, de d\u00e9ployer des calculs via <code>transform()<\/code> ou d&rsquo;orchestrer des analyses psychom\u00e9triques multivari\u00e9es, les chercheurs, ing\u00e9nieurs et analystes trouveront ici l&rsquo;ensemble des leviers m\u00e9thodologiques pour mener des inf\u00e9rences infaillibles et reproductibles.<\/p>\n<h2>1. Introduction aux mesures de tendance centrale et au partitionnement avec Pandas<\/h2>\n<h3>1.1 Pertinence m\u00e9thodologique de la m\u00e9diane en analyse de donn\u00e9es<\/h3>\n<p>La statistique descriptive repose fondamentalement sur la capacit\u00e9 \u00e0 condenser un ensemble d&rsquo;observations au sein d&rsquo;une m\u00e9trique scalaire unique, capable de refl\u00e9ter l&rsquo;ordre de grandeur repr\u00e9sentatif d&rsquo;une population. Formellement, pour une variable al\u00e9atoire continue unidimensionnelle <span class=\"arabpsy-math-inline\" data-latex=\"X\">$X$<\/span> dont les r\u00e9alisations ordonn\u00e9es sont not\u00e9es <span class=\"arabpsy-math-inline\" data-latex=\"x_{(1)} le x_{(2)} le dots le x_{(n)}\">$x_{(1)} le x_{(2)} le dots le x_{(n)}$<\/span>, la m\u00e9diane empirique correspond \u00e0 la valeur centrale s\u00e9parant formellement la distribution en deux moiti\u00e9s d&rsquo;effectifs rigoureusement \u00e9gaux. Lorsque la taille de l&rsquo;\u00e9chantillon <span class=\"arabpsy-math-inline\" data-latex=\"n\">$n$<\/span> est impaire, la m\u00e9diane s&rsquo;identifie de fa\u00e7on univoque \u00e0 la statistique d&rsquo;ordre centrale <span class=\"arabpsy-math-inline\" data-latex=\"x_{((n+1)\/2)}\">$x_{((n+1)\/2)}$<\/span>. Dans l&rsquo;\u00e9ventualit\u00e9 o\u00f9 <span class=\"arabpsy-math-inline\" data-latex=\"n\">$n$<\/span> est pair, elle est conventionnellement d\u00e9finie par la moyenne arithm\u00e9tique des deux valeurs centrales cons\u00e9cutives, soit la demi-somme des rangs <span class=\"arabpsy-math-inline\" data-latex=\"n\/2\">$n\/2$<\/span> et <span class=\"arabpsy-math-inline\" data-latex=\"(n\/2)+1\">$(n\/2)+1$<\/span>. Cette construction formelle lui conf\u00e8re un point de rupture asymptotique de 50 %, ce qui signifie qu&rsquo;il est n\u00e9cessaire de corrompre au moins la moiti\u00e9 des observations d&rsquo;un \u00e9chantillon pour d\u00e9placer la m\u00e9diane vers l&rsquo;infini, alors qu&rsquo;une seule valeur pathologique suffit \u00e0 alt\u00e9rer ind\u00e9finiment la moyenne arithm\u00e9tique.<\/p>\n<p>Cette propri\u00e9t\u00e9 de robustesse s&rsquo;av\u00e8re absolument d\u00e9terminante d\u00e8s lors que les donn\u00e9es sous-jacentes s&rsquo;\u00e9cartent des hypoth\u00e8ses id\u00e9alis\u00e9es de normalit\u00e9. Dans les domaines appliqu\u00e9s tels que la biom\u00e9trie, l&rsquo;\u00e9conomie des salaires, ou la psychom\u00e9trie exp\u00e9rimentale, les distributions empiriques pr\u00e9sentent r\u00e9guli\u00e8rement des queues de distribution \u00e9paisses et une forte asym\u00e9trie positive. Par exemple, lors de la mesure des temps de r\u00e9action cognitive face \u00e0 un stimulus perceptif, les temps physiologiques minimaux sont comprim\u00e9s par une barri\u00e8re incompressible, tandis que des \u00e9pisodes d&rsquo;inattention transitoire engendrent des latences consid\u00e9rables qui \u00e9tirent d\u00e9mesur\u00e9ment la queue droite de la distribution. Recourir \u00e0 la moyenne dans un tel contexte induit un biais d&rsquo;estimation syst\u00e9mique, en surestimant artificiellement la performance cognitive type. La m\u00e9diane neutralise l&rsquo;influence disproportionn\u00e9e de ces art\u00e9facts sans exiger d&rsquo;\u00e9lagage arbitraire du corpus.<\/p>\n<p>Cependant, l&rsquo;analyse globale d&rsquo;une population occulte fr\u00e9quemment l&rsquo;h\u00e9t\u00e9rog\u00e9n\u00e9it\u00e9 structurelle g\u00e9n\u00e9r\u00e9e par des sous-populations distinctes. Les ph\u00e9nom\u00e8nes observ\u00e9s r\u00e9sultent de l&rsquo;interaction entre une m\u00e9trique continue et une ou plusieurs variables cat\u00e9gorielles ind\u00e9pendantes qui partitionnent l&rsquo;\u00e9chantillon initial. D\u00e8s lors, l&rsquo;estimation de la tendance centrale ne peut demeurer aveugle \u00e0 cette stratification. Il devient scientifiquement indispensable de conditionner le calcul de l&rsquo;estimateur m\u00e9dian \u00e0 l&rsquo;appartenance factorielle de chaque individu. Isoler ces m\u00e9triques robustes selon des segments bien d\u00e9finis permet d&rsquo;effectuer des comparaisons intergroupes l\u00e9gitimes, de neutraliser des variables confondantes et de mettre en lumi\u00e8re des variations comportementales ou biologiques masqu\u00e9es par l&rsquo;agr\u00e9gation globale des donn\u00e9es.<\/p>\n<h3>1.2 Le paradigme Split-Apply-Combine dans l&rsquo;\u00e9cosyst\u00e8me Pandas<\/h3>\n<p>Pour operationaliser le calcul de m\u00e9triques statistiques conditionnelles au sein de structures tabulaires massives, l&rsquo;\u00e9cosyst\u00e8me open-source s&rsquo;est ralli\u00e9 au formalisme m\u00e9thodologique formalis\u00e9 par le statisticien <a rel=\"noopener noreferrer\" href=\"https:\/\/vita.had.co.nz\/papers\/plyr.html\">Hadley Wickham<\/a> en 2011, connu sous l&rsquo;appellation g\u00e9n\u00e9rique de paradigme \u00ab Split-Apply-Combine \u00bb (D\u00e9couper-Appliquer-Combiner). Cette architecture conceptuelle apporte une r\u00e9ponse structur\u00e9e au d\u00e9fi pos\u00e9 par la manipulation de sous-ensembles ind\u00e9pendants au sein de grands volumes de donn\u00e9es. Initialement popularis\u00e9 dans l&rsquo;environnement R \u00e0 travers les biblioth\u00e8ques plyr et dplyr, ce paradigme a \u00e9t\u00e9 profond\u00e9ment repens\u00e9 et optimis\u00e9 au c\u0153ur des algorithmes fondamentaux de Pandas par son cr\u00e9ateur Wes McKinney, offrant une interface unifi\u00e9e pour le calcul distribu\u00e9 ou multithread\u00e9.<\/p>\n<p>Le processus se d\u00e9roule selon une s\u00e9quence rigoureusement articul\u00e9e en trois phases cardinales. En premier lieu intervient la phase de d\u00e9composition (Split). Durant cette \u00e9tape, le jeu de donn\u00e9es originel, encapsul\u00e9 sous la forme d&rsquo;un <code>DataFrame<\/code>, fait l&rsquo;objet d&rsquo;une partition virtuelle en sous-ensembles disjoints, chacun correspondant \u00e0 une modalit\u00e9 sp\u00e9cifique ou \u00e0 une combinaison unique de modalit\u00e9s des facteurs de stratification sp\u00e9cifi\u00e9s. Sur le plan algorithmique, cette s\u00e9paration n&rsquo;op\u00e8re g\u00e9n\u00e9ralement pas de duplications physiques imm\u00e9diates en m\u00e9moire afin d&rsquo;\u00e9viter une surcharge de l&rsquo;allocation vive. Pandas g\u00e9n\u00e8re une table de hachage associant chaque cl\u00e9 de regroupement unique \u00e0 un vecteur d&rsquo;indices entiers pointant vers les positions physiques des observations correspondantes dans la matrice sous-jacente.<\/p>\n<p>Vient ensuite la phase d&rsquo;application (Apply). \u00c0 cet instant pr\u00e9cis, la fonction d&rsquo;agr\u00e9gation ou de r\u00e9duction souhait\u00e9e \u2014 en l&rsquo;occurrence l&rsquo;algorithme de calcul de la m\u00e9diane \u2014 est ex\u00e9cut\u00e9e individuellement sur chaque sous-segment extrait. Cette ex\u00e9cution it\u00e8re sur les diff\u00e9rentes partitions r\u00e9pertori\u00e9es dans la table d&rsquo;indexation. La fonction ing\u00e8re un sous-ensemble homog\u00e8ne de donn\u00e9es vectorielles et produit un r\u00e9sultat agr\u00e9g\u00e9 scalaire ou vectoriel de dimension r\u00e9duite. Enfin, la troisi\u00e8me phase est celle de recombinaison (Combine). Les scalaires r\u00e9sultant de l&rsquo;application de la m\u00e9diane sur chaque partition sont regroup\u00e9s et r\u00e9align\u00e9s au sein d&rsquo;une structure de donn\u00e9es coh\u00e9rente, typiquement une <code>Series<\/code> ou un nouveau <code>DataFrame<\/code>. Cette nouvelle structure associe explicitement les cl\u00e9s factorielles d&rsquo;origine aux estimateurs m\u00e9dians calcul\u00e9s, garantissant une int\u00e9grit\u00e9 relationnelle totale et un alignement dimensionnel parfait pour les analyses statistiques ult\u00e9rieures.<\/p>\n<h3>1.3 Environnement d&rsquo;ex\u00e9cution et pr\u00e9requis logiciels<\/h3>\n<p>L&rsquo;exploitation efficiente des fonctionnalit\u00e9s de regroupement statistique implique une architecture logicielle robuste et actualis\u00e9e. L&rsquo;ensemble des proc\u00e9dures expos\u00e9es dans cet ouvrage repose sur l&rsquo;interpr\u00e9teur standard Python dans sa version 3.9 ou sup\u00e9rieure, garantissant une prise en charge compl\u00e8te du typage avanc\u00e9 et des am\u00e9liorations substantielles des m\u00e9canismes d&rsquo;allocation m\u00e9moire interne. La biblioth\u00e8que <a rel=\"noopener noreferrer\" href=\"https:\/\/pandas.pydata.org\/\">Pandas<\/a> doit \u00eatre d\u00e9ploy\u00e9e de pr\u00e9f\u00e9rence dans une version contemporaine (version 2.0.0 ou ult\u00e9rieure), laquelle a profond\u00e9ment remani\u00e9 la gestion interne des types de donn\u00e9es en int\u00e9grant notamment le backend Apache Arrow. Cette \u00e9volution offre une efficience d\u00e9cupl\u00e9e lors du traitement de volum\u00e9tries imposantes et rationalise le comportement des agr\u00e9gateurs face aux colonnes non num\u00e9riques.<\/p>\n<p>La biblioth\u00e8que <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/\">NumPy<\/a> constitue la fondation num\u00e9rique indispensable sur laquelle Pandas \u00e9rige l&rsquo;int\u00e9gralit\u00e9 de ses tableaux sous-jacents (ndarrays). Il est imp\u00e9ratif de s&rsquo;assurer de la synchronisation de NumPy avec les compilateurs BLAS et LAPACK de la machine h\u00f4te pour permettre une acc\u00e9l\u00e9ration vectorielle des calculs sous-jacents. Le calcul de la m\u00e9diane fait sp\u00e9cifiquement appel \u00e0 des routines de s\u00e9lection partielle hautement optimis\u00e9es \u00e9crites en langage C, int\u00e9gr\u00e9es directement au noyau de NumPy. Une disparit\u00e9 de versions entre NumPy et Pandas peut engendrer des r\u00e9gressions de performance notables ou des avertissements d&rsquo;obsolescence (Deprecation Warnings) lors de l&rsquo;appel \u00e0 des fonctions d&rsquo;agr\u00e9gation personnalis\u00e9es.<\/p>\n<p>La mise en \u0153uvre pratique de ces analyses s&rsquo;effectue \u00e0 travers la manipulation de deux structures fondamentales : la <code>Series<\/code>, tableau unidimensionnel \u00e9tiquet\u00e9 capable d&rsquo;accueillir tout type de donn\u00e9es homog\u00e8ne, et le <code>DataFrame<\/code>, structure de donn\u00e9es bidimensionnelle h\u00e9t\u00e9rog\u00e8ne assimilable \u00e0 une feuille de calcul relationnelle. Il convient d&rsquo;initialiser son espace de travail en important formellement ces biblioth\u00e8ques selon les alias conventionnels consacr\u00e9s par l&rsquo;usage scientifique international :<\/p>\n<p><code>import pandas as pd<\/code><br \/>\n<code>import numpy as np<\/code><\/p>\n<p>Cette d\u00e9claration unifi\u00e9e assure la portabilit\u00e9 du code source \u00e0 travers les divers environnements interactifs (Jupyter Notebooks, IPython) et les architectures de production automatis\u00e9es, posant ainsi les bases d&rsquo;un environnement de calcul stable, reproductible et conforme aux standards m\u00e9thodologiques les plus rigoureux.<\/p>\n<h2>2. Fondements th\u00e9oriques et syntaxiques de la m\u00e9thode groupby()<\/h2>\n<h3>2.1 M\u00e9canisme interne de l&rsquo;objet DataFrameGroupBy<\/h3>\n<p>Lorsque l&rsquo;analyste invoque l&rsquo;instruction <code>df.groupby('variable')<\/code>, il est crucial de comprendre qu&rsquo;aucune op\u00e9ration math\u00e9matique n&rsquo;est ex\u00e9cut\u00e9e instantan\u00e9ment. L&rsquo;interpr\u00e9teur ne proc\u00e8de \u00e0 aucun calcul de moyenne, de variance ou de m\u00e9diane \u00e0 ce stade pr\u00e9cis de l&rsquo;ex\u00e9cution. En lieu et place, cette commande instancie un objet sp\u00e9cialis\u00e9 de la classe <code>pandas.core.groupby.generic.DataFrameGroupBy<\/code>. Cet objet constitue une structure interm\u00e9diaire paresseuse (lazy evaluation), con\u00e7ue pour consigner rigoureusement les m\u00e9tadonn\u00e9es n\u00e9cessaires au partitionnement ult\u00e9rieur sans engager pr\u00e9matur\u00e9ment les cycles de calcul de l&rsquo;unit\u00e9 centrale de traitement.<\/p>\n<p>Au c\u0153ur de cette architecture r\u00e9side la g\u00e9n\u00e9ration dynamique d&rsquo;une table d&rsquo;indexation associative. L&rsquo;algorithme scanne le vecteur cat\u00e9goriel d\u00e9sign\u00e9 comme crit\u00e8re de regroupement et identifie de mani\u00e8re exhaustive les valeurs distinctes (cl\u00e9s de hachage). Pour chacune de ces valeurs, le moteur r\u00e9pertorie l&rsquo;ensemble des coordonn\u00e9es scalaires relatives aux lignes du <code>DataFrame<\/code> source correspondant \u00e0 ladite modalit\u00e9. Cette relation biunivoque entre les cl\u00e9s et les indices peut \u00eatre directement examin\u00e9e par le programmeur par le biais de l&rsquo;attribut <code>groups<\/code>. Ce dernier expose un dictionnaire Python standardis\u00e9 o\u00f9 chaque cl\u00e9 correspond \u00e0 une modalit\u00e9 factorielle unique, et chaque valeur associ\u00e9e se pr\u00e9sente sous la forme d&rsquo;un objet <code>Index<\/code> r\u00e9pertoriant les positions des lignes affili\u00e9es.<\/p>\n<p>Parall\u00e8lement, la propri\u00e9t\u00e9 <code>ngroups<\/code> permet de quantifier instantan\u00e9ment la cardinalit\u00e9 de l&rsquo;espace factoriel d\u00e9limit\u00e9 par le partitionnement, c&rsquo;est-\u00e0-dire le nombre absolu de groupes distincts identifi\u00e9s par le syst\u00e8me de hachage. Cette inspection pr\u00e9liminaire est hautement recommand\u00e9e en contexte d&rsquo;assurance qualit\u00e9 des donn\u00e9es : elle permet de d\u00e9tecter imm\u00e9diatement une prolif\u00e9ration ind\u00e9sirable de micro-classes, fr\u00e9quemment attribuable \u00e0 des fautes de frappe ou des variations typographiques dans les donn\u00e9es qualitatives brutes. L&rsquo;objet <code>DataFrameGroupBy<\/code> agit ainsi comme un conteneur s\u00e9curis\u00e9, pr\u00eat \u00e0 distribuer les calculs num\u00e9riques sur chacune des partitions index\u00e9es d\u00e8s qu&rsquo;une m\u00e9thode terminale d&rsquo;agr\u00e9gation est appel\u00e9e.<\/p>\n<h3>2.2 Syntaxe de base de l&rsquo;agr\u00e9gation m\u00e9diane<\/h3>\n<p>La formulation syntaxique canonique pour calculer la m\u00e9diane d&rsquo;une variable quantitative conditionnellement \u00e0 une variable qualitative s&rsquo;articule sous une s\u00e9quence d&rsquo;appels fluide et \u00e9l\u00e9gante. Sa forme fondamentale s&rsquo;\u00e9nonce comme suit :<\/p>\n<p><code>df.groupby('variable_groupe')['variable_valeur'].median()<\/code><\/p>\n<p>Cette \u00e9criture illustre parfaitement la s\u00e9paration fonctionnelle entre la phase de partitionnement et la s\u00e9lection dimensionnelle. En ins\u00e9rant l&rsquo;op\u00e9rateur d&rsquo;indexation <code>['variable_valeur']<\/code> imm\u00e9diatement apr\u00e8s l&rsquo;appel \u00e0 la m\u00e9thode <code>groupby()<\/code>, l&rsquo;analyste transforme l&rsquo;objet g\u00e9n\u00e9rique <code>DataFrameGroupBy<\/code> en un objet d\u00e9riv\u00e9 <code>SeriesGroupBy<\/code>. Cette projection dimensionnelle rev\u00eat une importance capitale : elle restreint d\u00e9lib\u00e9r\u00e9ment l&rsquo;espace de calcul \u00e0 la seule colonne continue d&rsquo;int\u00e9r\u00eat, \u00e9vitant ainsi au moteur d&rsquo;\u00e9valuation de tenter de calculer inutilement la m\u00e9diane sur les autres variables num\u00e9riques ou, pire, de d\u00e9clencher des exceptions logicielles lors de la confrontation avec des colonnes textuelles incompatibles.<\/p>\n<p>D\u00e8s lors que la m\u00e9thode <code>median()<\/code> est appel\u00e9e sur l&rsquo;objet <code>SeriesGroupBy<\/code>, le moteur interne d\u00e9l\u00e8gue le calcul \u00e0 des boucles vectoris\u00e9es impl\u00e9ment\u00e9es en Cython. Pour chaque groupe identifi\u00e9, les valeurs scalaires associ\u00e9es sont extraites, isol\u00e9es et transmises \u00e0 l&rsquo;algorithme de calcul m\u00e9dian. Par d\u00e9faut, cette m\u00e9thode num\u00e9rique traite exclusivement les types de donn\u00e9es compatibles avec l&rsquo;arithm\u00e9tique r\u00e9elle (entiers sign\u00e9s ou non, flottants \u00e0 simple ou double pr\u00e9cision). Si la s\u00e9rie sous-jacente est format\u00e9e sous un type cat\u00e9goriel ou textuel, l&rsquo;op\u00e9ration l\u00e8vera syst\u00e9matiquement une erreur de type (<code>TypeError<\/code>), rappelant la stricte exigence d&rsquo;une coh\u00e9rence math\u00e9matique entre la nature de l&rsquo;estimateur appliqu\u00e9 et l&rsquo;\u00e9chelle de mesure des donn\u00e9es observ\u00e9es.<\/p>\n<h3>2.3 Gestion du type de retour et granularit\u00e9 de l&rsquo;index<\/h3>\n<p>L&rsquo;ex\u00e9cution de la m\u00e9thode d&rsquo;agr\u00e9gation <code>median()<\/code> alt\u00e8re profond\u00e9ment la topologie structurelle des donn\u00e9es. Alors que le <code>DataFrame<\/code> initial se pr\u00e9sentait sous la forme d&rsquo;une matrice bidimensionnelle de dimension <span class=\"arabpsy-math-inline\" data-latex=\"n \\times p\">$n \\times p$<\/span> (o\u00f9 <span class=\"arabpsy-math-inline\" data-latex=\"n\">$n$<\/span> est le nombre d&rsquo;observations individuelles et <span class=\"arabpsy-math-inline\" data-latex=\"p\">$p$<\/span> le nombre de colonnes observ\u00e9es), le r\u00e9sultat \u00e9mergeant de l&rsquo;instruction <code>df.groupby('groupe')['valeur'].median()<\/code> est une <code>Series<\/code> unidimensionnelle de dimension <span class=\"arabpsy-math-inline\" data-latex=\"k\">$k$<\/span>, o\u00f9 <span class=\"arabpsy-math-inline\" data-latex=\"k\">$k$<\/span> correspond au nombre de modalit\u00e9s distinctes de la variable de groupe.<\/p>\n<p>La transformation la plus fondamentale r\u00e9side dans la transmutation de l&rsquo;axe indiciel. Dans le r\u00e9sultat produit, les cl\u00e9s de regroupement ne sont plus h\u00e9berg\u00e9es en tant que valeurs vectorielles ordinaires : elles sont promues au rang d&rsquo;<code>Index<\/code> de la <code>Series<\/code> r\u00e9sultante. Cet index structurel devient le syst\u00e8me d&rsquo;adressage primaire des scalaires m\u00e9dians calcul\u00e9s. Si cette disposition s&rsquo;av\u00e8re particuli\u00e8rement adapt\u00e9e \u00e0 la recherche rapide de valeurs scalaires via l&rsquo;accesseur <code>loc[]<\/code>, elle g\u00e9n\u00e8re fr\u00e9quemment des difficult\u00e9s op\u00e9rationnelles lors des \u00e9tapes ult\u00e9rieures de la cha\u00eene de traitement de donn\u00e9es (data pipeline). En particulier, un index d&rsquo;agr\u00e9gation ne peut \u00eatre directement joint ou concat\u00e9n\u00e9 sans r\u00e9alignement pr\u00e9alable avec des structures relationnelles externes.<\/p>\n<p>Par ailleurs, si la syntaxe employ\u00e9e utilise une double paire de crochets pour s\u00e9lectionner la variable quantitative, \u00e0 savoir <code>df.groupby('groupe')[['valeur']].median()<\/code>, le r\u00e9sultat renvoy\u00e9 n&rsquo;est plus une <code>Series<\/code> mais un <code>DataFrame<\/code> de dimension <span class=\"arabpsy-math-inline\" data-latex=\"k \\times 1\">$k \\times 1$<\/span>. Cette distinction syntaxique subtile gouverne la dimensionalit\u00e9 de sortie : l&rsquo;usage de la double parenth\u00e8se carr\u00e9e pr\u00e9serve la nature matricielle bidimensionnelle de l&rsquo;objet, ce qui simplifie son int\u00e9gration au sein de biblioth\u00e8ques de mod\u00e9lisation pr\u00e9dictive ou d&rsquo;outils de visualisation graphique exigeant syst\u00e9matiquement des matrices d&rsquo;observations en entr\u00e9e.<\/p>\n<h2>3. Calcul de la m\u00e9diane pour une variable de regroupement unique<\/h2>\n<h3>3.1 Cr\u00e9ation du jeu de donn\u00e9es de r\u00e9f\u00e9rence<\/h3>\n<p>Afin d&rsquo;ancrer les d\u00e9monstrations th\u00e9oriques dans un cadre exp\u00e9rimental contr\u00f4l\u00e9 et reproductible, nous concevons un jeu de donn\u00e9es synth\u00e9tique mod\u00e9lisant les performances physiques et biom\u00e9triques d&rsquo;athl\u00e8tes professionnels engag\u00e9s dans une comp\u00e9tition sportive d&rsquo;\u00e9lite. Cette cohorte comprend des variables qualitatives nominales (l&rsquo;\u00e9quipe de rattachement, la position tactique sur le terrain) et des variables quantitatives continues (le nombre de points inscrits au terme de la rencontre, le total des rebonds capt\u00e9s). Ce sch\u00e9ma permet de reproduire fid\u00e8lement les situations d&rsquo;asym\u00e9trie et de dispersion fr\u00e9quemment rencontr\u00e9es dans les \u00e9tudes observationnelles.<\/p>\n<p>L&rsquo;instanciation de ce jeu de donn\u00e9es de r\u00e9f\u00e9rence s&rsquo;effectue via un dictionnaire transmis au constructeur <code>pd.DataFrame<\/code> :<\/p>\n<p><code>donnees = {<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'equipe': ['Alpha', 'Alpha', 'Alpha', 'Alpha', 'Beta', 'Beta', 'Beta', 'Beta', 'Gamma', 'Gamma', 'Gamma', 'Gamma'],<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'position': ['Meneur', 'Meneur', 'Pivot', 'Ailier', 'Meneur', 'Pivot', 'Pivot', 'Ailier', 'Meneur', 'Ailier', 'Pivot', 'Pivot'],<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'points': [18, 26, 12, 15, 22, 8, 10, 31, 14, 28, 6, 9],<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'rebonds': [4, 3, 11, 6, 5, 14, 12, 4, 3, 7, 15, 13]<br \/>\n}<br \/>\ndf = pd.DataFrame(donnees)<\/code><\/p>\n<p>D\u00e8s la g\u00e9n\u00e9ration de cette matrice tabulaire, il est m\u00e9thodologiquement imp\u00e9ratif de v\u00e9rifier l&rsquo;int\u00e9grit\u00e9 structurelle des vecteurs sous-jacents en sollicitant la m\u00e9thode d&rsquo;inspection <code>df.info()<\/code>. Cette pr\u00e9caution \u00e9l\u00e9mentaire confirme l&rsquo;absence de discordance de types : les colonnes de regroupement doivent \u00eatre r\u00e9pertori\u00e9es sous le type g\u00e9n\u00e9rique <code>object<\/code> ou explicitement converties en <code>category<\/code>, tandis que les m\u00e9triques d&rsquo;\u00e9valuation doivent pr\u00e9senter des formats entiers (<code>int64<\/code>) ou r\u00e9els (<code>float64<\/code>), autorisant ainsi sans restriction la mise en \u0153uvre d&rsquo;op\u00e9rations d&rsquo;ordre et de calculs arithm\u00e9tiques m\u00e9dians.<\/p>\n<h3>3.2 Application pas \u00e0 pas de l&rsquo;agr\u00e9gation univari\u00e9e<\/h3>\n<p>Consid\u00e9rons l&rsquo;hypoth\u00e8se op\u00e9rationnelle selon laquelle l&rsquo;analyste cherche \u00e0 \u00e9valuer la performance offensive typique de chaque organisation sportive, en neutralisant l&rsquo;effet potentiellement distorsif d&rsquo;exploits individuels exceptionnels. Dans cette perspective, la d\u00e9termination de la m\u00e9diane univari\u00e9e des points par \u00e9quipe constitue l&rsquo;approche m\u00e9thodologique la plus \u00e9quilibr\u00e9e. La commande est d\u00e9clar\u00e9e formellement :<\/p>\n<p><code>mediane_points = df.groupby('equipe')['points'].median()<\/code><\/p>\n<p>Pour appr\u00e9hender pleinement l&rsquo;ex\u00e9cution s\u00e9quentielle interne r\u00e9gissant cette ligne de commande, il convient d&rsquo;observer le flux de donn\u00e9es sous-jacent. Le moteur Pandas proc\u00e8de d&rsquo;abord \u00e0 l&rsquo;isolement des sous-vecteurs de points associ\u00e9s \u00e0 chacune des trois entit\u00e9s factorielles r\u00e9pertori\u00e9es :<\/p>\n<ul>\n<li>Pour l&rsquo;\u00e9quipe <strong>Alpha<\/strong>, le sous-ensemble de points extrait est constitu\u00e9 de la suite : [18, 26, 12, 15]. L&rsquo;algorithme op\u00e8re le tri ascendant de cette s\u00e9rie, produisant le vecteur ordonn\u00e9 [12, 15, 18, 26]. La dimension de l&rsquo;\u00e9chantillon \u00e9tant paire (<span class=\"arabpsy-math-inline\" data-latex=\"n = 4\">$n = 4$<\/span>), la m\u00e9diane s&rsquo;\u00e9tablit par le calcul de la demi-somme des \u00e9l\u00e9ments centraux situ\u00e9s aux rangs 2 et 3, soit <span class=\"arabpsy-math-inline\" data-latex=\"(15 + 18) \/ 2 = 16.5\">$(15 + 18) \/ 2 = 16.5$<\/span>.<\/li>\n<li>Pour l&rsquo;\u00e9quipe <strong>Beta<\/strong>, le vecteur ordonn\u00e9 correspondant aux observations brutes [22, 8, 10, 31] devient [8, 10, 22, 31]. L&rsquo;interpolation centrale isole les composantes 10 et 22, conduisant \u00e0 une m\u00e9diane exacte de <span class=\"arabpsy-math-inline\" data-latex=\"(10 + 22) \/ 2 = 16.0\">$(10 + 22) \/ 2 = 16.0$<\/span>.<\/li>\n<li>Pour l&rsquo;\u00e9quipe <strong>Gamma<\/strong>, le vecteur initial [14, 28, 6, 9] une fois r\u00e9ordonn\u00e9 produit la suite ordonn\u00e9e [6, 9, 14, 28]. Les observations m\u00e9dianes 9 et 14 aboutissent au calcul de <span class=\"arabpsy-math-inline\" data-latex=\"(9 + 14) \/ 2 = 11.5\">$(9 + 14) \/ 2 = 11.5$<\/span>.<\/li>\n<\/ul>\n<p>L&rsquo;observation de ces r\u00e9sultats chiffr\u00e9s met en \u00e9vidence la puissance de discrimination de la m\u00e9diane. Bien que l&rsquo;\u00e9quipe Beta poss\u00e8de dans ses rangs un scoreur individuel particuli\u00e8rement prolifique ayant cumul\u00e9 31 unit\u00e9s lors d&rsquo;un match singulier, son niveau m\u00e9dian de performance collective (16.0) s&rsquo;av\u00e8re rigoureusement inf\u00e9rieur \u00e0 celui de l&rsquo;\u00e9quipe Alpha (16.5). La m\u00e9diane r\u00e9v\u00e8le ainsi fid\u00e8lement l&rsquo;homog\u00e9n\u00e9it\u00e9 du rendement d&rsquo;Alpha face \u00e0 l&rsquo;asym\u00e9trie marqu\u00e9e caract\u00e9risant la production offensive de Beta, illustrant concr\u00e8tement l&rsquo;int\u00e9r\u00eat d&rsquo;un estimateur insensible aux singularit\u00e9s extr\u00eames.<\/p>\n<h3>3.3 Int\u00e9gration syst\u00e9matique de la m\u00e9thode reset_index()<\/h3>\n<p>Bien que la <code>Series<\/code> issue de l&rsquo;\u00e9tape pr\u00e9c\u00e9dente fournisse une synth\u00e8se statistique irr\u00e9prochable, son int\u00e9gration ult\u00e9rieure au sein d&rsquo;architectures de traitement automatis\u00e9es soul\u00e8ve des contraintes d&rsquo;interop\u00e9rabilit\u00e9. L&rsquo;incorporation de la cl\u00e9 de partitionnement au sein de l&rsquo;index structurel brise le formatage relationnel traditionnel \u00e0 colonnes homog\u00e8nes (principe du Tidy Data \u00e9nonc\u00e9 par Hadley Wickham). C&rsquo;est pour rem\u00e9dier pr\u00e9cis\u00e9ment \u00e0 cette limitation que l&rsquo;adjonction de la m\u00e9thode <code>reset_index()<\/code> s&rsquo;impose comme une bonne pratique syst\u00e9matique :<\/p>\n<p><code>df_mediane_tabulaire = df.groupby('equipe')['points'].median().reset_index()<\/code><\/p>\n<p>L&rsquo;effet architectural de cette m\u00e9thode est imm\u00e9diat : l&rsquo;index d&rsquo;agr\u00e9gation contenant les libell\u00e9s [&lsquo;Alpha&rsquo;, &lsquo;Beta&rsquo;, &lsquo;Gamma&rsquo;] est r\u00e9ins\u00e9r\u00e9 dans le corps matriciel sous la forme d&rsquo;une colonne de donn\u00e9es standard, tandis qu&rsquo;un nouvel index num\u00e9rique s\u00e9quentiel standardis\u00e9 allant de 0 \u00e0 <span class=\"arabpsy-math-inline\" data-latex=\"k-1\">$k-1$<\/span> (ici <code>RangeIndex(0, 3)<\/code>) est assign\u00e9 aux lignes r\u00e9sultantes. Le <code>DataFrame<\/code> bidimensionnel qui en d\u00e9coule pr\u00e9sente une topologie propre, compos\u00e9e de deux colonnes explicites : la colonne cat\u00e9gorielle d&rsquo;origine (&lsquo;equipe&rsquo;) et la colonne d&rsquo;estimation num\u00e9rique (&lsquo;points&rsquo;).<\/p>\n<p>Ce reformatage s&rsquo;av\u00e8re indispensable d\u00e8s lors que les donn\u00e9es doivent \u00eatre export\u00e9es vers des formats plats standards tels que le format CSV, s\u00e9rialis\u00e9es sous forme de structures JSON destin\u00e9es \u00e0 des interfaces applicatives, ou fusionn\u00e9es (via l&rsquo;instruction <code>pd.merge<\/code>) avec d&rsquo;autres tables de dimensions sociod\u00e9mographiques ou comportementales. La discipline m\u00e9thodologique consistant \u00e0 clore syst\u00e9matiquement ses op\u00e9rations d&rsquo;agr\u00e9gation group\u00e9e par un appel \u00e0 <code>reset_index()<\/code> pr\u00e9vient l&rsquo;apparition d&rsquo;erreurs de r\u00e9f\u00e9rence structurelle complexes lors des manipulations ult\u00e9rieures de la cha\u00eene de calcul.<\/p>\n<h2>4. Calcul de la m\u00e9diane sur plusieurs variables de regroupement<\/h2>\n<h3>4.1 Principes du regroupement multivari\u00e9<\/h3>\n<p>L&rsquo;investigation empirique n\u00e9cessite fr\u00e9quemment de d\u00e9passer les analyses factorielles simples pour \u00e9valuer l&rsquo;interaction conjointe de multiples variables explicatives ind\u00e9pendantes sur la variable continue \u00e9tudi\u00e9e. Dans le cadre de notre cohorte sportive, supposer que la production de points d\u00e9pend exclusivement de l&rsquo;\u00e9quipe de rattachement constituerait une simplification abusive : le positionnement tactique des joueurs sur le terrain exerce une influence biom\u00e9canique et strat\u00e9gique d\u00e9terminante sur les opportunit\u00e9s de tir. Il est donc indispensable d&rsquo;\u00e9largir le spectre analytique en op\u00e9rant une stratification multivari\u00e9e \u00e0 facteurs crois\u00e9s.<\/p>\n<p>Le regroupement multivari\u00e9 repose sur le croisement combinatoire des modalit\u00e9s associ\u00e9es aux diff\u00e9rentes colonnes de segmentation sp\u00e9cifi\u00e9es. Si une premi\u00e8re variable <span class=\"arabpsy-math-inline\" data-latex=\"A\">$A$<\/span> comporte <span class=\"arabpsy-math-inline\" data-latex=\"I\">$I$<\/span> modalit\u00e9s distinctes et qu&rsquo;une seconde variable <span class=\"arabpsy-math-inline\" data-latex=\"B\">$B$<\/span> en poss\u00e8de <span class=\"arabpsy-math-inline\" data-latex=\"J\">$J$<\/span>, l&rsquo;espace factoriel th\u00e9orique ainsi constitu\u00e9 comprend un total de <span class=\"arabpsy-math-inline\" data-latex=\"I \\times J\">$I \\times J$<\/span> intersections factorielles potentielles. Lors de la phase de d\u00e9composition (Split), le moteur d&rsquo;ex\u00e9cution de Pandas identifie l&rsquo;ensemble des tuples uniques form\u00e9s par les paires de valeurs <span class=\"arabpsy-math-inline\" data-latex=\"(a_i, b_j)\">$(a_i, b_j)$<\/span> observ\u00e9es conjointement au sein de la matrice brute.<\/p>\n<p>Il est fondamental de noter la distinction \u00e9pist\u00e9mologique entre le nombre th\u00e9orique de cellules factorielles (<span class=\"arabpsy-math-inline\" data-latex=\"I \\times J\">$I \\times J$<\/span>) et le nombre effectif de groupes peupl\u00e9s d&rsquo;observations r\u00e9elles. Dans de nombreux protocoles d&rsquo;\u00e9chantillonnage quasi-exp\u00e9rimentaux ou observationnels, certaines combinaisons factorielles demeurent non repr\u00e9sent\u00e9es (cellules vides). Pandas adapte dynamiquement son algorithme de partitionnement en indexant exclusivement les combinaisons poss\u00e9dant au moins une ligne d&rsquo;observation effective dans les donn\u00e9es sources, \u00e9vitant ainsi l&rsquo;instanciation de partitions fant\u00f4mes non document\u00e9es, sous r\u00e9serve que les variables sous-jacentes ne soient pas d\u00e9clar\u00e9es sous un type <code>Categorical<\/code> rigide non observ\u00e9.<\/p>\n<h3>4.2 Impl\u00e9mentation technique \u00e0 facteurs crois\u00e9s<\/h3>\n<p>Pour mat\u00e9rialiser ce partitionnement \u00e0 deux facteurs explicatifs, la syntaxe de Pandas exige la transmission d&rsquo;une liste ordonn\u00e9e de descripteurs factoriels en argument de la m\u00e9thode de regroupement. L&rsquo;\u00e9valuation de la m\u00e9diane des points conditionn\u00e9e simultan\u00e9ment par l&rsquo;\u00e9quipe et la position tactique se programme ainsi :<\/p>\n<p><code>mediane_croisee = df.groupby(['equipe', 'position'])['points'].median().reset_index()<\/code><\/p>\n<p>Cette instruction d\u00e9clenche l&rsquo;ex\u00e9cution conjointe de la stratification. L&rsquo;algorithme partitionne la s\u00e9rie des points en fonction des paires uniques <code>(equipe, position)<\/code>. Examinons le traitement sp\u00e9cifique appliqu\u00e9 aux observations de notre jeu de donn\u00e9es :<\/p>\n<ul>\n<li>Pour la combinaison <strong>(&lsquo;Alpha&rsquo;, &lsquo;Meneur&rsquo;)<\/strong>, la sous-population r\u00e9pertorie deux observations de scores : 18 et 26. La m\u00e9diane calcul\u00e9e correspond \u00e0 la demi-somme : <span class=\"arabpsy-math-inline\" data-latex=\"(18 + 26) \/ 2 = 22.0\">$(18 + 26) \/ 2 = 22.0$<\/span>.<\/li>\n<li>Pour <strong>(&lsquo;Alpha&rsquo;, &lsquo;Ailier&rsquo;)<\/strong>, une seule observation est pr\u00e9sente : [15]. La m\u00e9diane scalaire d&rsquo;un singleton co\u00efncide trivialement avec l&rsquo;observation elle-m\u00eame, soit 15.0.<\/li>\n<li>Pour <strong>(&lsquo;Alpha&rsquo;, &lsquo;Pivot&rsquo;)<\/strong>, l&rsquo;effectif unitaire isole l&rsquo;observation 12.0.<\/li>\n<li>Pour <strong>(&lsquo;Beta&rsquo;, &lsquo;Pivot&rsquo;)<\/strong>, deux observations sont identifi\u00e9es : 8 et 10. La m\u00e9diane s&rsquo;\u00e9tablit \u00e0 <span class=\"arabpsy-math-inline\" data-latex=\"(8 + 10) \/ 2 = 9.0\">$(8 + 10) \/ 2 = 9.0$<\/span>.<\/li>\n<\/ul>\n<p>Ce niveau de d\u00e9composition r\u00e9v\u00e8le des dynamiques internes invisibles \u00e0 l&rsquo;\u00e9chelle macroscopique de l&rsquo;agr\u00e9gation univari\u00e9e. Alors que l&rsquo;\u00e9quipe Beta affichait globalement une m\u00e9diane collective comp\u00e9titive de 16.0 points, la d\u00e9sagr\u00e9gation par position met en lumi\u00e8re la tr\u00e8s faible efficacit\u00e9 offensive m\u00e9diane de son poste de Pivot (9.0 points), contrastant avec le niveau exceptionnel de son Ailier (31.0 points). L&rsquo;analyse crois\u00e9e d\u00e9montre ainsi sa sup\u00e9riorit\u00e9 m\u00e9thodologique en \u00e9vitant les conclusions trompeuses inh\u00e9rentes aux paradoxes d&rsquo;agr\u00e9gation statistique.<\/p>\n<h3>4.3 Structure et manipulation du MultiIndex r\u00e9sultant<\/h3>\n<p>Dans l&rsquo;\u00e9ventualit\u00e9 o\u00f9 l&rsquo;analyste omet volontairement ou involontairement l&rsquo;invocation terminale de la m\u00e9thode <code>reset_index()<\/code>, la structure r\u00e9sultante adopte une morphologie hi\u00e9rarchique complexe r\u00e9gie par la classe <code>pandas.MultiIndex<\/code>. Le MultiIndex repr\u00e9sente un syst\u00e8me d&rsquo;indexation vectorielle multidimensionnelle, o\u00f9 chaque ligne de donn\u00e9es n&rsquo;est plus adress\u00e9e par un identifiant scalaire simple, mais par un n-uplet ordonn\u00e9 de cl\u00e9s d&rsquo;indexation hi\u00e9rarchis\u00e9es.<\/p>\n<p>Au sein de cet objet hi\u00e9rarchique, les niveaux d&rsquo;index (levels) sont empil\u00e9s selon l&rsquo;ordre strict de d\u00e9claration sp\u00e9cifi\u00e9 lors de l&rsquo;appel initial \u00e0 <code>groupby()<\/code>. Dans notre exemple, le niveau 0 correspond \u00e0 la variable &lsquo;equipe&rsquo;, tandis que le niveau 1 correspond \u00e0 la variable &lsquo;position&rsquo;. Pour interroger directement des valeurs scalaires imbriqu\u00e9es au sein de cette architecture sans devoir aplatir pr\u00e9alablement l&rsquo;objet, il est imp\u00e9ratif de mobiliser l&rsquo;accesseur <code>.loc[]<\/code> en lui transmettant un tuple d&rsquo;indexation explicite :<\/p>\n<p><code>mediane_hierarchique = df.groupby(['equipe', 'position'])['points'].median()<\/code><br \/>\n<code>score_pivot_alpha = mediane_hierarchique.loc[('Alpha', 'Pivot')]<\/code><\/p>\n<p>Bien que le MultiIndex offre des fonctionnalit\u00e9s avanc\u00e9es d&rsquo;agr\u00e9gation marginale et de d\u00e9coupage vectoriel tridimensionnel via les objets <code>pd.IndexSlice<\/code>, sa manipulation accro\u00eet consid\u00e9rablement la complexit\u00e9 cognitive du code source et engendre des frictions logicielles lors de l&rsquo;exportation vers des formats tabulaires non hi\u00e9rarchiques. Dans la grande majorit\u00e9 des cas d&rsquo;application en science des donn\u00e9es appliqu\u00e9e, l&rsquo;aplatissement syst\u00e9matique du MultiIndex par le recours imm\u00e9diat \u00e0 <code>reset_index()<\/code> constitue l&rsquo;approche programmatique la plus saine, la plus p\u00e9renne et la plus lisible.<\/p>\n<h2>5. Application simultan\u00e9e \u00e0 plusieurs variables d\u00e9pendantes<\/h2>\n<h3>5.1 Agr\u00e9gation m\u00e9diane sur un sous-ensemble de colonnes continues<\/h3>\n<p>L&rsquo;exploration des ph\u00e9nom\u00e8nes r\u00e9els se limite rarement \u00e0 l&rsquo;examen d&rsquo;une m\u00e9trique quantitative unique. Les protocoles exp\u00e9rimentaux collectent couramment un faisceau de dimensions continues destin\u00e9es \u00e0 qualifier simultan\u00e9ment divers aspects du syst\u00e8me \u00e9tudi\u00e9. Dans notre paradigme d&rsquo;\u00e9valuation sportive, il est tout aussi essentiel d&rsquo;analyser le volume de rebonds capt\u00e9s que le total de points marqu\u00e9s pour appr\u00e9hender l&rsquo;impact global des effectifs en comp\u00e9tition. Pandas autorise une s\u00e9lection multidimensionnelle \u00e9l\u00e9gante pour r\u00e9pondre \u00e0 ce besoin m\u00e9thodologique :<\/p>\n<p><code>mediane_multivariable = df.groupby('equipe')[['points', 'rebonds']].median().reset_index()<\/code><\/p>\n<p>L&rsquo;utilisation d\u00e9lib\u00e9r\u00e9e de la liste de descripteurs vectoriels <code>[['points', 'rebonds']]<\/code> signale formellement au moteur de partitionnement que le calcul de la m\u00e9diane doit \u00eatre appliqu\u00e9 de mani\u00e8re ind\u00e9pendante et vectoris\u00e9e sur chacune des colonnes quantitatives s\u00e9lectionn\u00e9es. L&rsquo;algorithme op\u00e8re alors deux s\u00e9ries parall\u00e8les de tris partiels et d&rsquo;interpolations m\u00e9dianes pour chaque sous-groupe factoriel identifi\u00e9.<\/p>\n<p>Le r\u00e9sultat de cette op\u00e9ration est un <code>DataFrame<\/code> structur\u00e9 conservant fid\u00e8lement la dimensionalit\u00e9 bidimensionnelle requise : la colonne cat\u00e9gorielle &lsquo;equipe&rsquo; sert de rep\u00e8re relationnel, tandis que les deux colonnes &lsquo;points&rsquo; et &lsquo;rebonds&rsquo; restituent les m\u00e9dianes respectives associ\u00e9es \u00e0 chaque formation sportive. Le tableau synth\u00e9tique permet de constater imm\u00e9diatement des compromis tactiques : l&rsquo;\u00e9quipe Gamma, qui affichait la m\u00e9diane de scoring la plus faible (11.5 points), d\u00e9montre en revanche une sup\u00e9riorit\u00e9 d\u00e9fensive remarquable sous le cercle avec une m\u00e9diane impressionnante de 10.0 rebonds, surpassant largement les organisations concurrentes sur ce crit\u00e8re physique.<\/p>\n<h3>5.2 Application g\u00e9n\u00e9ralis\u00e9e \u00e0 l&rsquo;ensemble du DataFrame<\/h3>\n<p>Dans certaines d\u00e9marches exploratoires non cibl\u00e9es, l&rsquo;analyste peut \u00e9prouver la tentation de solliciter le calcul automatique de la m\u00e9diane sur l&rsquo;int\u00e9gralit\u00e9 des colonnes constitutives du <code>DataFrame<\/code>, sans proc\u00e9der \u00e0 une s\u00e9lection dimensionnelle explicite, selon la syntaxe suivante :<\/p>\n<p><code>df_medianes_globales = df.groupby('equipe').median(numeric_only=True).reset_index()<\/code><\/p>\n<p>Sur le plan de la gouvernance du code et de la compatibilit\u00e9 des versions, cette instruction soul\u00e8ve des enjeux critiques. Historiquement, les versions ant\u00e9rieures de Pandas tentaient une politique permissive d\u00e9sign\u00e9e sous le nom d&rsquo;\u00ab \u00e9limination automatique \u00bb (data type dropping) : les colonnes de type texte ou non arithm\u00e9tiques \u00e9taient silencieusement ignor\u00e9es lors de l&rsquo;application de fonctions math\u00e9matiques. Toutefois, les versions r\u00e9centes de Pandas (notamment depuis l&rsquo;av\u00e8nement des versions 2.x) ont formellement banni ce laxisme syntaxique pour pr\u00e9venir les effets de bord ind\u00e9sirables et les baisses de performance inattendues.<\/p>\n<p>D\u00e9sormais, l&rsquo;omission du param\u00e8tre <code>numeric_only=True<\/code> lors d&rsquo;une agr\u00e9gation globale d\u00e9clenche au minimum un avertissement d&rsquo;obsolescence s\u00e9v\u00e8re (FutureWarning) et, dans les configurations d&rsquo;ex\u00e9cution les plus strictes, provoque la lev\u00e9e imp\u00e9rative d&rsquo;une exception de type <code>TypeError<\/code>. L&rsquo;analyste rigoureux veillera donc soit \u00e0 circonscrire pr\u00e9cis\u00e9ment en amont les colonnes num\u00e9riques \u00e0 traiter via une s\u00e9lection indicielle stricte, soit \u00e0 renseigner explicitement le param\u00e8tre <code>numeric_only=True<\/code>. Cette pratique explicite pr\u00e9vient toute ambigu\u00eft\u00e9 s\u00e9mantique et prot\u00e8ge la cha\u00eene d&rsquo;ex\u00e9cution contre les ruptures de compatibilit\u00e9 logicielle ascendante.<\/p>\n<h3>5.3 Renommage et restructuration des colonnes r\u00e9sultantes<\/h3>\n<p>L&rsquo;application d&rsquo;op\u00e9rations d&rsquo;agr\u00e9gation pose un d\u00e9fi d&rsquo;\u00e9tiquetage s\u00e9mantique au sein des m\u00e9tadonn\u00e9es tabulaires. Par d\u00e9faut, Pandas r\u00e9attribue \u00e0 la colonne r\u00e9sultante le nom initial de la variable mesur\u00e9e. Ainsi, apr\u00e8s l&rsquo;appel \u00e0 <code>df.groupby('equipe')[['points', 'rebonds']].median()<\/code>, les colonnes calcul\u00e9es portent toujours les d\u00e9nominations g\u00e9n\u00e9riques &lsquo;points&rsquo; et &lsquo;rebonds&rsquo;. Cette conservation lexicale induit une ambigu\u00eft\u00e9 documentaire pr\u00e9judiciable : rien dans l&rsquo;intitul\u00e9 de la colonne ne signale d\u00e9sormais au lecteur ou au mod\u00e8le pr\u00e9dictif en aval que ces valeurs ne sont plus des observations individuelles brutes, mais des estimateurs de tendance m\u00e9diane.<\/p>\n<p>Pour asseoir une tra\u00e7abilit\u00e9 documentaire sans faille, il s&rsquo;av\u00e8re indispensable de renommer programmariquement les attributs num\u00e9riques g\u00e9n\u00e9r\u00e9s en leur adjoignant un suffixe m\u00e9thodologique clair. L&rsquo;approche la plus standardis\u00e9e repose sur l&rsquo;usage de la m\u00e9thode <code>rename()<\/code> coupl\u00e9e \u00e0 un dictionnaire de correspondances :<\/p>\n<p><code>colonnes_renommees = {<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'points': 'points_mediane',<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'rebonds': 'rebonds_mediane'<br \/>\n}<br \/>\ndf_final = df.groupby('equipe')[['points', 'rebonds']].median().rename(columns=colonnes_renommees).reset_index()<\/code><\/p>\n<p>Dans le cadre de pipelines de donn\u00e9es industriels automatis\u00e9s comportant des dizaines de variables continues, ce renommage peut \u00eatre avantageusement vectoris\u00e9 \u00e0 l&rsquo;aide de compr\u00e9hensions de dictionnaires ou de l&rsquo;application d&rsquo;expressions lambda sur la propri\u00e9t\u00e9 <code>columns<\/code>. Cette normalisation syst\u00e9matique de la nomenclature pr\u00e9vient toute confusion d&rsquo;interpr\u00e9tation chez les utilisateurs finaux et garantit la conformit\u00e9 des flux aux normes de catalogage de donn\u00e9es en vigueur.<\/p>\n<h2>6. Gestion rigoureuse des valeurs manquantes et aberrantes<\/h2>\n<h3>6.1 Impact des donn\u00e9es manquantes (NaN) sur le calcul de la m\u00e9diane<\/h3>\n<p>Dans la pratique exp\u00e9rimentale, l&rsquo;int\u00e9grit\u00e9 des matrices de donn\u00e9es r\u00e9elles est presque in\u00e9vitablement compromise par la pr\u00e9sence d&rsquo;observations incompl\u00e8tes ou non renseign\u00e9es, formellement mod\u00e9lis\u00e9es sous Python par la valeur sentinelle <code>np.nan<\/code> (Not a Number). L&rsquo;influence de ces donn\u00e9es manquantes sur les r\u00e9sultats statistiques d\u00e9pend intimement de la politique d&rsquo;omission sous-jacente adopt\u00e9e par l&rsquo;algorithme d&rsquo;agr\u00e9gation.<\/p>\n<p>Par d\u00e9faut, l&rsquo;impl\u00e9mentation de la m\u00e9thode <code>median()<\/code> au sein de Pandas int\u00e8gre le param\u00e8tre bool\u00e9en <code>skipna=True<\/code>. Ce comportement par d\u00e9faut ordonne au moteur de calcul d&rsquo;\u00e9carter silencieusement toutes les instances de valeurs manquantes identifi\u00e9es au sein d&rsquo;une partition avant de proc\u00e9der au tri partiel et \u00e0 l&rsquo;estimation centrale. Si cette approche permet d&rsquo;assurer la continuit\u00e9 de l&rsquo;ex\u00e9cution en \u00e9vitant la propagation syst\u00e9matique de valeurs nulles dans les r\u00e9sultats finaux, elle entra\u00eene des cons\u00e9quences m\u00e9thodologiques de premier ordre que le chercheur ne saurait ignorer :<\/p>\n<p>L&rsquo;omission non surveill\u00e9e des valeurs manquantes r\u00e9duit l&rsquo;effectif r\u00e9el <span class=\"arabpsy-math-inline\" data-latex=\"n_g\">$n_g$<\/span> du sous-groupe consid\u00e9r\u00e9, diminuant d&rsquo;autant la puissance statistique de l&rsquo;estimateur sans que l&rsquo;analyste n&rsquo;en soit formellement averti. Pour pr\u00e9venir tout biais d&rsquo;\u00e9chantillonnage insidieux, il est indispensable de quantifier pr\u00e9alablement le profil d&rsquo;absence au sein de chaque strate factorielle. L&rsquo;ex\u00e9cution d&rsquo;un diagnostic d&rsquo;int\u00e9grit\u00e9 pr\u00e9alable permet d&rsquo;identifier la r\u00e9partition des donn\u00e9es incompl\u00e8tes :<\/p>\n<p><code>diagnostic_manquants = df.groupby('equipe')['points'].apply(lambda x: x.isna().sum()).reset_index(name='nb_valeurs_manquantes')<\/code><\/p>\n<p>Cette v\u00e9rification pr\u00e9liminaire permet d&rsquo;attester de la validit\u00e9 de la cohorte et d&rsquo;\u00e9viter que la m\u00e9diane d&rsquo;un groupe ne soit calcul\u00e9e que sur une fraction marginale et potentiellement non repr\u00e9sentative de l&rsquo;\u00e9chantillon initial.<\/p>\n<h3>6.2 Traitement des valeurs manquantes dans les colonnes de groupement<\/h3>\n<p>Une probl\u00e9matique distincte mais tout aussi critique survient lorsque des valeurs manquantes contaminent les colonnes qualitatives destin\u00e9es \u00e0 servir de crit\u00e8res de partitionnement. Historiquement, le moteur de partitionnement de Pandas excluait sans exception les lignes pour lesquelles la variable de stratification pr\u00e9sentait une valeur <code>np.nan<\/code>. Ces observations \u00e9taient alors d\u00e9finitivement perdues pour l&rsquo;analyse, n&rsquo;apparaissant dans aucune modalit\u00e9 de sortie.<\/p>\n<p>Depuis la version 1.1 de Pandas, les ing\u00e9nieurs disposent du param\u00e8tre <code>dropna<\/code> au sein de la m\u00e9thode <code>groupby()<\/code>, dont la valeur par d\u00e9faut demeure fix\u00e9e \u00e0 <code>True<\/code>. Lorsque l&rsquo;analyste configure formellement ce param\u00e8tre sur <code>dropna=False<\/code> :<\/p>\n<p><code>df_avec_manquants = df.groupby('equipe', dropna=False)['points'].median().reset_index()<\/code><\/p>\n<p>L&rsquo;algorithme modifie sa strat\u00e9gie de hachage et traite la valeur sentinelle <code>NaN<\/code> comme une cat\u00e9gorie factorielle \u00e0 part enti\u00e8re. Les observations sans assignation d&rsquo;\u00e9quipe explicite sont alors regroup\u00e9es sous un label nul, et leur m\u00e9diane de scoring propre est rigoureusement \u00e9valu\u00e9e. Cette configuration est indispensable lors des audits de conformit\u00e9 de donn\u00e9es : elle permet d&rsquo;inspecter les caract\u00e9ristiques statistiques des populations orphelines sans les expurger artificiellement de l&rsquo;espace d&rsquo;analyse.<\/p>\n<p>Dans les cas o\u00f9 l&rsquo;int\u00e9grit\u00e9 des identifiants factoriels conditionne la validit\u00e9 th\u00e9orique de l&rsquo;\u00e9tude, une politique d&rsquo;imputation cat\u00e9gorielle pr\u00e9alable s&rsquo;av\u00e8re pr\u00e9f\u00e9rable \u00e0 l&rsquo;exclusion passive. Remplacer les valeurs nulles par une modalit\u00e9 explicite telle que <code>'Non_Assigne'<\/code> ou <code>'Inconnu'<\/code> via la commande <code>df['equipe'].fillna('Inconnu')<\/code> assure une tra\u00e7abilit\u00e9 parfaite des effectifs tout au long du cycle analytique.<\/p>\n<h3>6.3 R\u00e9sistance de la m\u00e9diane face aux valeurs extr\u00eames<\/h3>\n<p>L&rsquo;argument \u00e9pist\u00e9mologique central en faveur de l&rsquo;adoption de la m\u00e9diane r\u00e9side dans son immunit\u00e9 remarquable face aux perturbations induites par des valeurs aberrantes ou extr\u00eames (outliers). Pour illustrer rigoureusement cette sup\u00e9riorit\u00e9 m\u00e9canique, consid\u00e9rons l&rsquo;introduction volontaire d&rsquo;une anomalie m\u00e9trologique majeure au sein de notre jeu de donn\u00e9es : modifions artificiellement la performance d&rsquo;un joueur de l&rsquo;\u00e9quipe Alpha en assignant la valeur aberrante de 999 points au lieu des 26 points initiaux.<\/p>\n<p>Proc\u00e9dons alors \u00e0 la comparaison comparative des deux estimateurs de tendance centrale :<\/p>\n<p><code>df_perturbe = df.copy()<\/code><br \/>\n<code>df_perturbe.loc[1, 'points'] = 999<\/code><br \/>\n<code>comparaison = df_perturbe.groupby('equipe')['points'].agg(moyenne='mean', mediane='median').reset_index()<\/code><\/p>\n<p>L&rsquo;examen des indicateurs calcul\u00e9s pour l&rsquo;\u00e9quipe Alpha apporte une d\u00e9monstration empirique irr\u00e9futable :<\/p>\n<ul>\n<li>La <strong>moyenne arithm\u00e9tique<\/strong>, sous l&rsquo;effet de cette valeur unique de 999 points, bondit spectaculairement d&rsquo;une valeur de base de 17.75 points \u00e0 une valeur totalement d\u00e9nu\u00e9e de sens statistique de 261.0 points. L&rsquo;estimateur moyen \u00e9choue int\u00e9gralement \u00e0 refl\u00e9ter la r\u00e9alit\u00e9 op\u00e9rationnelle de la cohorte.<\/li>\n<li>La <strong>m\u00e9diane<\/strong>, quant \u00e0 elle, op\u00e8re sur les valeurs ordonn\u00e9es [12, 15, 18, 999]. Les valeurs centrales d&rsquo;interpolation deviennent 15 et 18, conduisant \u00e0 une nouvelle m\u00e9diane estim\u00e9e de <span class=\"arabpsy-math-inline\" data-latex=\"(15 + 18) \/ 2 = 16.5\">$(15 + 18) \/ 2 = 16.5$<\/span> points.<\/li>\n<\/ul>\n<p>La m\u00e9diane demeure strictement invariable, pr\u00e9servant avec une fid\u00e9lit\u00e9 absolue la mesure de performance collective originelle. Cette imperm\u00e9abilit\u00e9 aux distorsions d\u00e9montre pourquoi, dans les environnements de donn\u00e9es bruit\u00e9s, la m\u00e9diane doit syst\u00e9matiquement supplanter la moyenne dans les mod\u00e8les d&rsquo;agr\u00e9gation d\u00e9cisionnels.<\/p>\n<h2>7. Techniques avanc\u00e9es avec la m\u00e9thode agg() et fonctions personnalis\u00e9es<\/h2>\n<h3>7.1 Calcul simultan\u00e9 de m\u00e9triques compl\u00e9mentaires via agg()<\/h3>\n<p>Bien que la m\u00e9diane procure une estimation non biais\u00e9e du centre de gravit\u00e9 d&rsquo;une distribution asym\u00e9trique, une description statistique rigoureuse ne saurait s&rsquo;abstraire d&rsquo;une quantification conjointe de la dispersion des observations. Isoler une tendance centrale sans \u00e9valuer l&rsquo;h\u00e9t\u00e9rog\u00e9n\u00e9it\u00e9 des variations individuelles expose l&rsquo;analyste \u00e0 des interpr\u00e9tations lacunaires. Pour consolider ce cadre d&rsquo;analyse, l&rsquo;instruction <code>agg()<\/code> offre un environnement d&rsquo;agr\u00e9gation multidimensionnelle extr\u00eamement puissant.<\/p>\n<p>Dans cette perspective, il est hautement pertinent d&rsquo;associer la m\u00e9diane \u00e0 des m\u00e9triques de dispersion congruentes, telles que l&rsquo;intervalle interquartile (calcul\u00e9 par la diff\u00e9rence entre le troisi\u00e8me et le premier quartile) ou, \u00e0 titre comparatif, la moyenne et l&rsquo;\u00e9cart-type standardis\u00e9s. La mise en \u0153uvre de cette batterie de m\u00e9triques s&rsquo;articule \u00e9l\u00e9gamment par la sp\u00e9cification d&rsquo;un dictionnaire d&rsquo;agr\u00e9gation :<\/p>\n<p><code>iqr_fn = lambda x: x.quantile(0.75) - x.quantile(0.25)<\/code><br \/>\n<code>synthese_statistique = df.groupby('equipe')['points'].agg([<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;('mediane', 'median'),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;('iqr', iqr_fn),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;('moyenne', 'mean'),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;('ecart_type', 'std'),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;('effectif', 'count')<br \/>\n]).reset_index()<\/code><\/p>\n<p>Ce protocole extrait en une seule passe d&rsquo;\u00e9valuation une matrice d&rsquo;indicateurs compl\u00e9mentaires. La confrontation simultan\u00e9e de la m\u00e9diane et de la moyenne permet de mesurer imm\u00e9diatement le degr\u00e9 d&rsquo;asym\u00e9trie de la distribution intra-groupe (skewness implicite), tandis que l&rsquo;\u00e9cart interquartile (IQR) documente l&rsquo;\u00e9tendue couverte par les 50 % centraux de la population, confirmant la structure d&rsquo;ensemble des donn\u00e9es \u00e9tudi\u00e9es.<\/p>\n<h3>7.2 D\u00e9finition et injection de fonctions d&rsquo;agr\u00e9gation sur mesure<\/h3>\n<p>Dans des contextes exp\u00e9rimentaux complexes, la m\u00e9diane standard peut n\u00e9cessiter des raffinements algorithmiques sp\u00e9cifiques non pr\u00e9vus nativement par les primitives logicielles de base. C&rsquo;est notamment le cas lorsque chaque observation se voit affecter un coefficient de pond\u00e9ration sp\u00e9cifique (par exemple, proportionnel au temps effectif pass\u00e9 sur le terrain de jeu). Le calcul d&rsquo;une m\u00e9diane pond\u00e9r\u00e9e exige alors l&rsquo;impl\u00e9mentation d&rsquo;un algorithme d\u00e9di\u00e9 coupl\u00e9 \u00e0 l&rsquo;infrastructure d&rsquo;agr\u00e9gation de Pandas.<\/p>\n<p>NumPy ne fournissant pas nativement de m\u00e9thode directe pour la m\u00e9diane pond\u00e9r\u00e9e, nous pouvons formuler une fonction personnalis\u00e9e robuste et l&rsquo;injecter au sein du m\u00e9canisme de regroupement :<\/p>\n<p><code>def mediane_ponderee(df_sous_groupe, col_valeurs, col_poids):<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;df_trie = df_sous_groupe.sort_values(by=col_valeurs)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;cumul = df_trie[col_poids].cumsum()<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;seuil = df_trie[col_poids].sum() \/ 2.0<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;return df_trie[cumul &gt;= seuil][col_valeurs].iloc[0]<\/code><\/p>\n<p>L&rsquo;int\u00e9gration d&rsquo;une telle fonction au niveau de chaque groupe s&rsquo;op\u00e8re via la m\u00e9thode <code>apply()<\/code>, qui transmet \u00e0 la fonction personnalis\u00e9e la tranche compl\u00e8te du sous-DataFrame correspondant \u00e0 chaque groupe :<\/p>\n<p><code>df['temps_jeu'] = [12.5, 34.0, 22.1, 18.5, 30.2, 15.0, 19.8, 38.4, 25.0, 27.5, 14.2, 16.0]<\/code><br \/>\n<code>mediane_pond_resultat = df.groupby('equipe').apply(<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;mediane_ponderee, col_valeurs='points', col_poids='temps_jeu'<br \/>\n).reset_index(name='mediane_ponderee')<\/code><\/p>\n<p>Cette flexibilit\u00e9 algorithmique autorise l&rsquo;impl\u00e9mentation de tout raffinement computationnel arbitraire, tout en pr\u00e9servant le b\u00e9n\u00e9fice de l&rsquo;infrastructure de partitionnement hautement optimis\u00e9e de Pandas.<\/p>\n<h3>7.3 Agr\u00e9gation nomm\u00e9e (Named Aggregation) pour un code maintenable<\/h3>\n<p>L&rsquo;usage historique de dictionnaires complexes ou de listes de tuples au sein de <code>agg()<\/code> produisait fr\u00e9quemment des structures MultiIndex sur les axes de colonnes, dont la manipulation ult\u00e9rieure s&rsquo;av\u00e9rait source de confusion logique. Pour r\u00e9soudre d\u00e9finitivement cet \u00e9cueil ergonomique, Pandas a standardis\u00e9 depuis sa version 0.25 la syntaxe d&rsquo;\u00ab Agr\u00e9gation Nomm\u00e9e \u00bb (Named Aggregation).<\/p>\n<p>Cette approche s&rsquo;appuie sur la transmission d&rsquo;arguments nomm\u00e9s directement au sein de l&rsquo;instruction <code>agg()<\/code>, chaque argument recevant un objet <code>pd.NamedAgg<\/code> ou, de fa\u00e7on plus concise, un tuple \u00e0 deux \u00e9l\u00e9ments formalis\u00e9 sous la forme <code>(colonne_cible, fonction_aggregatrice)<\/code> :<\/p>\n<p><code>synthese_elegante = df.groupby('equipe').agg(<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;points_mediane=pd.NamedAgg(column='points', aggfunc='median'),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;rebonds_mediane=pd.NamedAgg(column='rebonds', aggfunc='median'),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;points_dispersion=pd.NamedAgg(column='points', aggfunc=lambda x: x.max() - x.min())<br \/>\n).reset_index()<\/code><\/p>\n<p>L&rsquo;avantage op\u00e9rationnel de cette syntaxe r\u00e9side dans l&rsquo;\u00e9limination pr\u00e9ventive de toute hi\u00e9rarchisation ind\u00e9sirable des colonnes. D\u00e8s le terme de l&rsquo;ex\u00e9cution, les colonnes r\u00e9sultantes sont proprement \u00e9tiquet\u00e9es avec les d\u00e9nominations m\u00e9tier explicites choisies par le programmeur. Le code gagne immens\u00e9ment en lisibilit\u00e9, en maintenabilit\u00e9 et s&rsquo;int\u00e8gre harmonieusement dans les principes de d\u00e9veloppement logiciel pilot\u00e9s par la documentation.<\/p>\n<h2>8. Diffusion de la m\u00e9diane de groupe via transform()<\/h2>\n<h3>8.1 Diff\u00e9renciation structurelle entre agg() et transform()<\/h3>\n<p>L&rsquo;une des m\u00e9prises les plus fr\u00e9quentes chez les praticiens de Pandas r\u00e9side dans la confusion fonctionnelle entre les primitives <code>agg()<\/code> et <code>transform()<\/code>. Tandis que <code>agg()<\/code> vise explicitement la r\u00e9duction dimensionnelle \u2014 en contractant les multiples observations d&rsquo;une partition vers un estimateur scalaire unique \u2014 <code>transform()<\/code> poursuit une finalit\u00e9 radicalement divergente, articul\u00e9e autour du principe de diffusion vectorielle (broadcasting).<\/p>\n<p>La caract\u00e9ristique fondamentale de <code>transform()<\/code> repose sur la conservation stricte et inconditionnelle de la dimensionnalit\u00e9 du <code>DataFrame<\/code> initial. Si la matrice source comprend <span class=\"arabpsy-math-inline\" data-latex=\"n\">$n$<\/span> lignes, la structure \u00e9mergeant de l&rsquo;application de <code>transform()<\/code> comportera rigoureusement <span class=\"arabpsy-math-inline\" data-latex=\"n\">$n$<\/span> lignes, pr\u00e9servant scrupuleusement l&rsquo;alignement de l&rsquo;index d&rsquo;origine. Concr\u00e8tement, l&rsquo;algorithme calcule la m\u00e9trique d&rsquo;int\u00e9r\u00eat pour chaque groupe (en l&rsquo;occurrence la m\u00e9diane), puis duplique ce r\u00e9sultat scalaire sur chaque ligne du groupe d&rsquo;appartenance.<\/p>\n<p>Consid\u00e9rons l&rsquo;appel suivant :<\/p>\n<p><code>df['points_mediane_groupe'] = df.groupby('equipe')['points'].transform('median')<\/code><\/p>\n<p>L&rsquo;ex\u00e9cution de cette commande g\u00e9n\u00e8re une nouvelle s\u00e9rie dont la longueur est rigoureusement identique au nombre de lignes d&rsquo;origine. Chaque athl\u00e8te de l&rsquo;\u00e9quipe Alpha voit figurer dans cette colonne additionnelle la valeur 16.5, les joueurs de Beta re\u00e7oivent la valeur 16.0, et ceux de Gamma la valeur 11.5. Cette diffusion facilite les calculs comparatifs \u00e0 l&rsquo;\u00e9chelle des observations individuelles sans exiger de jointures matricielles secondaires co\u00fbteuses.<\/p>\n<h3>8.2 Calcul d&rsquo;\u00e9carts et normalisation par rapport \u00e0 la m\u00e9diane du sous-groupe<\/h3>\n<p>La diffusion permise par <code>transform()<\/code> simplifie consid\u00e9rablement la cr\u00e9ation d&rsquo;indicateurs de performance relatifs et la mise en \u0153uvre de proc\u00e9dures de normalisation robuste. Dans les contextes de mod\u00e9lisation pr\u00e9dictive ou d&rsquo;\u00e9valuation psychom\u00e9trique, l&rsquo;attention analytique se porte rarement sur la valeur absolue d&rsquo;un score, mais bien plut\u00f4t sur l&rsquo;amplitude et le sens de sa d\u00e9viation par rapport \u00e0 la norme \u00e9tablie au sein de son groupe de r\u00e9f\u00e9rence.<\/p>\n<p>Gr\u00e2ce \u00e0 la vectorisation assur\u00e9e par l&rsquo;alignement indiciel de <code>transform()<\/code>, l&rsquo;analyste peut quantifier instantan\u00e9ment l&rsquo;\u00e9cart individuel \u00e0 la m\u00e9diane de groupe (notion d&rsquo;\u00e9cart r\u00e9siduel non param\u00e9trique) en une op\u00e9ration scalaire \u00e9l\u00e9mentaire :<\/p>\n<p><code>df['ecart_mediane_groupe'] = df['points'] - df.groupby('equipe')['points'].transform('median')<\/code><\/p>\n<p>Sur cette base, il devient possible de calculer l&rsquo;\u00e9cart absolu \u00e0 la m\u00e9diane (Median Absolute Deviation ou MAD), m\u00e9trique de dispersion non param\u00e9trique d\u00e9finie comme la m\u00e9diane des d\u00e9viations absolues par rapport \u00e0 la m\u00e9diane du groupe. Cette normalisation robuste, analogue au calcul du Z-score classique mais imperm\u00e9able aux valeurs aberrantes, s&rsquo;ex\u00e9cute selon un cha\u00eenage fonctionnel \u00e9l\u00e9gant :<\/p>\n<p><code>def calculer_mad(serie):<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;med = serie.median()<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;return (serie - med).abs().median()<\/code><\/p>\n<p><code>df['mad_groupe'] = df.groupby('equipe')['points'].transform(calculer_mad)<\/code><br \/>\n<code>df['score_normalise_robuste'] = (df['points'] - df['points_mediane_groupe']) \/ df['mad_groupe']<\/code><\/p>\n<p>Ce formalisme dote l&rsquo;analyste d&rsquo;un puissant cadre de d\u00e9tection des singularit\u00e9s relatives : un athl\u00e8te affichant un score normalis\u00e9 robuste exc\u00e9dant 2.5 ou 3.0 peut \u00eatre formellement qualifi\u00e9 d&rsquo;observateur singulier au sein de son propre sous-groupe, fournissant une base objective \u00e0 l&rsquo;exploration cibl\u00e9e des anomalies comportementales.<\/p>\n<h3>8.3 Imputation conditionnelle par la m\u00e9diane de classe<\/h3>\n<p>Le traitement des donn\u00e9es manquantes par imputation simple repr\u00e9sente un cas d&rsquo;usage embl\u00e9matique de la m\u00e9thode <code>transform()<\/code>. L&rsquo;approche consistant \u00e0 imputer les valeurs manquantes d&rsquo;une variable par la m\u00e9diane g\u00e9n\u00e9rale de l&rsquo;\u00e9chantillon global comporte un risque d&rsquo;alt\u00e9ration m\u00e9thodologique majeur : elle \u00e9crase artificiellement les disparit\u00e9s structurelles entre les sous-populations, affaiblissant la validit\u00e9 des mod\u00e8les d&rsquo;inf\u00e9rence ult\u00e9rieurs.<\/p>\n<p>L&rsquo;imputation conditionnelle par la m\u00e9diane de classe r\u00e9sout ce probl\u00e8me en assignant aux valeurs manquantes la m\u00e9diane du groupe de rattachement sp\u00e9cifique de l&rsquo;observation. La formulation technique s&rsquo;av\u00e8re particuli\u00e8rement concise :<\/p>\n<p><code>df_manquant = df.copy()<\/code><br \/>\n<code>df_manquant.loc[2, 'points'] = np.nan<\/code><\/p>\n<p><code>mediane_diffusee = df_manquant.groupby('equipe')['points'].transform('median')<\/code><br \/>\n<code>df_manquant['points_imputes'] = df_manquant['points'].fillna(mediane_diffusee)<\/code><\/p>\n<p>Dans ce sc\u00e9nario, la valeur manquante assign\u00e9e \u00e0 la troisi\u00e8me ligne (appartenant \u00e0 l&rsquo;\u00e9quipe Alpha) est automatiquement compl\u00e9t\u00e9e par la valeur 16.5, m\u00e9diane calcul\u00e9e sur les membres observ\u00e9s de l&rsquo;\u00e9quipe Alpha. La distribution relative intra-groupe est ainsi remarquablement pr\u00e9serv\u00e9e, \u00e9vitant l&rsquo;introduction de distorsions transversales entre les diff\u00e9rents sous-ensembles du corpus de donn\u00e9es.<\/p>\n<h2>9. Optimisation algorithmique et performance sur volum\u00e9tries massives<\/h2>\n<h3>9.1 Impact des types de donn\u00e9es cat\u00e9goriels (pd.Categorical)<\/h3>\n<p>Lorsque le volume des observations cro\u00eet pour atteindre plusieurs millions d&rsquo;enregistrements, les m\u00e9canismes d&rsquo;allocation m\u00e9moire et d&rsquo;indexation de bas niveau deviennent le goulot d&rsquo;\u00e9tranglement d\u00e9terminant de l&rsquo;infrastructure d&rsquo;analyse. Par d\u00e9faut, Pandas stocke les variables textuelles qualitatives sous le type g\u00e9n\u00e9rique <code>object<\/code>, mat\u00e9rialis\u00e9 par des tableaux de pointeurs pointant vers des cha\u00eenes de caract\u00e8res Python allou\u00e9es de mani\u00e8re dispers\u00e9e au sein de la m\u00e9moire vive. Cette architecture m\u00e9moire d\u00e9grad\u00e9e engendre une surcharge pond\u00e9rale consid\u00e9rable et nuit gravement \u00e0 la localit\u00e9 des caches processeur lors des phases de hachage.<\/p>\n<p>La parade technique repose sur la conversion pr\u00e9alable des variables de partitionnement en types cat\u00e9goriels explicites via <code>pd.Categorical<\/code> ou <code>astype('category')<\/code> :<\/p>\n<p><code>df['equipe'] = df['equipe'].astype('category')<\/code><\/p>\n<p>Cette conversion op\u00e8re une compression substantielle en m\u00e9moire : les cha\u00eenes de caract\u00e8res uniques sont stock\u00e9es une fois au sein d&rsquo;une table d&rsquo;encodage lexicale, tandis que le corps de la s\u00e9rie est remplac\u00e9 par un vecteur dense d&rsquo;entiers pointant vers ces codes. Lors de l&rsquo;ex\u00e9cution de l&rsquo;op\u00e9ration <code>groupby()<\/code>, le moteur d&rsquo;ex\u00e9cution renonce aux algorithmes de hachage de cha\u00eenes co\u00fbteux pour basculer sur des m\u00e9canismes d&rsquo;adressage direct par tableau d&rsquo;indices entiers, r\u00e9duisant la dur\u00e9e de la phase de d\u00e9coupage (Split) d&rsquo;un facteur pouvant atteindre 5 \u00e0 10 fois.<\/p>\n<p>Toutefois, l&rsquo;usage des cat\u00e9gories impose une vigilance m\u00e9thodologique concernant le param\u00e8tre <code>observed<\/code>. Par d\u00e9faut, sur les versions ant\u00e9rieures \u00e0 Pandas 2.1, <code>groupby()<\/code> \u00e9valuait la m\u00e9diane pour l&rsquo;int\u00e9gralit\u00e9 des combinaisons de cat\u00e9gories th\u00e9oriquement possibles au sein du sch\u00e9ma, y compris les croisements sans aucune observation r\u00e9elle. L&rsquo;assignation formelle de <code>observed=True<\/code> au sein de <code>groupby()<\/code> ordonne au moteur de limiter ses it\u00e9rations algorithmiques aux seules modalit\u00e9s effectivement mat\u00e9rialis\u00e9es dans l&rsquo;\u00e9chantillon, \u00e9vitant ainsi le gaspillage de ressources computationnelles sur des intersections vides.<\/p>\n<h3>9.2 Complexit\u00e9 temporelle et algorithmique du calcul m\u00e9dian<\/h3>\n<p>D&rsquo;un point de vue algorithmique et th\u00e9orique, le calcul de la m\u00e9diane par groupe pr\u00e9sente une complexit\u00e9 computationnelle substantiellement sup\u00e9rieure \u00e0 celle d&rsquo;op\u00e9rations d&rsquo;agr\u00e9gation lin\u00e9aires traditionnelles telles que la somme (<code>sum()<\/code>) ou la moyenne arithm\u00e9tique (<code>mean()<\/code>). Alors que l&rsquo;\u00e9valuation d&rsquo;une somme d&rsquo;\u00e9l\u00e9ments s&rsquo;ex\u00e9cute en temps strictement lin\u00e9aire <span class=\"arabpsy-math-inline\" data-latex=\"\\mathcal{O}(n)\">$\\mathcal{O}(n)$<\/span> via un simple accumulateur en registre mat\u00e9riel sans exiger de r\u00e9agencement spatial des donn\u00e9es, la d\u00e9termination de la m\u00e9diane repose intrins\u00e8quement sur la statistique d&rsquo;ordre et l&rsquo;ordonnancement des vecteurs sous-jacents.<\/p>\n<p>L&rsquo;impl\u00e9mentation na\u00efve d&rsquo;un calcul m\u00e9dian exigerait le tri complet de chaque sous-groupe factoriel, ce qui induirait une complexit\u00e9 temporelle asymptotique moyenne de l&rsquo;ordre de <span class=\"arabpsy-math-inline\" data-latex=\"\\mathcal{O}(n_k \\log n_k)\">$\\mathcal{O}(n_k \\log n_k)$<\/span> pour chaque groupe de taille <span class=\"arabpsy-math-inline\" data-latex=\"n_k\">$n_k$<\/span>. Pour att\u00e9nuer cette charge processeur, Pandas et NumPy exploitent des algorithmes de s\u00e9lection partielle optimis\u00e9s de type <strong>Quickselect<\/strong> ou <strong>Introselect<\/strong> (s\u00e9lection par partitionnement hybride d\u00e9velopp\u00e9 par David Musser). Ces routines ram\u00e8nent la complexit\u00e9 temporelle moyenne \u00e0 une borne th\u00e9orique lin\u00e9aire <span class=\"arabpsy-math-inline\" data-latex=\"\\mathcal{O}(n_k)\">$\\mathcal{O}(n_k)$<\/span>.<\/p>\n<p>N\u00e9anmoins, m\u00eame sous cette impl\u00e9mentation hautement perfectionn\u00e9e, la constante multiplicative r\u00e9gissant les op\u00e9rations de s\u00e9lection partielle demeure nettement sup\u00e9rieure \u00e0 celle d&rsquo;un simple parcours cumulatif. Le tri partiel impose des \u00e9critures et des permutations de m\u00e9moire non n\u00e9gligeables, engendrant des cycles de lecture\/\u00e9criture r\u00e9p\u00e9t\u00e9s. D\u00e8s lors que l&rsquo;on manipule des ensembles massifs de donn\u00e9es partitionn\u00e9s en de tr\u00e8s nombreux sous-groupes (haute cardinalit\u00e9), le calcul de la m\u00e9diane peut solliciter intensivement les cycles CPU. Dans de telles configurations, des strat\u00e9gies d&rsquo;\u00e9chantillonnage repr\u00e9sentatif ou de pr\u00e9-partitionnement physique par chunks (via Dask ou Polars) peuvent \u00eatre envisag\u00e9es en amont.<\/p>\n<h3>9.3 Alternative avec Numba et les extensions Cython<\/h3>\n<p>Face \u00e0 des volum\u00e9tries industrielles de plusieurs dizaines de millions de lignes n\u00e9cessitant des calculs de m\u00e9dianes mobiles ou des agr\u00e9gations personnalis\u00e9es en cascade, le pont interpr\u00e9teur entre Python et les structures Pandas peut manifester des surco\u00fbts d&rsquo;abstraction significatifs. Pour s&rsquo;affranchir de cette friction, Pandas int\u00e8gre une passerelle avec le compilateur Just-In-Time (JIT) <a rel=\"noopener noreferrer\" href=\"https:\/\/numba.pydata.org\/\">Numba<\/a>.<\/p>\n<p>Numba compile des fonctions Python annot\u00e9es directement en instructions machine optimis\u00e9es pour le jeu d&rsquo;instructions natif du processeur (LLVM). Il est possible de mobiliser ce moteur d&rsquo;acc\u00e9l\u00e9ration lors de l&rsquo;application de fonctions de r\u00e9duction group\u00e9e en sp\u00e9cifiant l&rsquo;argument <code>engine='numba'<\/code> au sein de la m\u00e9thode <code>agg()<\/code> :<\/p>\n<p><code>from numba import jit<\/code><\/p>\n<p><code>@jit(nopython=True)<\/code><br \/>\n<code>def mediane_numba_acceleree(valeurs):<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;return np.median(valeurs)<\/code><\/p>\n<p><code>resultat_rapide = df.groupby('equipe')['points'].agg(mediane_numba_acceleree, engine='numba')<\/code><\/p>\n<p>Cette approche d\u00e9sactive le verrou global de l&rsquo;interpr\u00e9teur (GIL) et vectorise l&rsquo;ex\u00e9cution \u00e0 l&rsquo;\u00e9chelle des registres vectoriels SIMD du processeur h\u00f4te. Cependant, il convient de souligner que ce recours \u00e0 Numba ne se justifie que sur des volum\u00e9tries massives : sur des jeux de donn\u00e9es de taille modeste, le co\u00fbt incompressible de la compilation initiale \u00e0 la vol\u00e9e (warm-up time) exc\u00e8de fr\u00e9quemment le gain computationnel brut obtenu lors du calcul final.<\/p>\n<h2>10. Visualisation graphique des m\u00e9dianes calcul\u00e9es par sous-groupes<\/h2>\n<h3>10.1 Repr\u00e9sentation sous forme de diagrammes \u00e0 barres d&rsquo;agr\u00e9gats<\/h3>\n<p>La communication scientifique des r\u00e9sultats statistiques requiert de coupler les synth\u00e8ses tabulaires \u00e0 des repr\u00e9sentations graphiques claires et fid\u00e8les. Pour mettre en valeur les \u00e9carts m\u00e9dians entre diff\u00e9rentes cat\u00e9gories factorielles, le diagramme en barres (barplot) demeure l&rsquo;un des standards de diffusion les plus largement adopt\u00e9s dans la litt\u00e9rature institutionnelle. Toutefois, une vigilance m\u00e9thodologique s&rsquo;impose lors de l&rsquo;interfa\u00e7age avec des biblioth\u00e8ques de haut niveau telles que <a rel=\"noopener noreferrer\" href=\"https:\/\/seaborn.pydata.org\/\">Seaborn<\/a>.<\/p>\n<p>Par d\u00e9faut, la fonction <code>seaborn.barplot()<\/code> applique la moyenne arithm\u00e9tique comme fonction d&rsquo;estimation centrale par d\u00e9faut. Si l&rsquo;analyste n&rsquo;y prend garde, le graphique affich\u00e9 introduira une divergence fondamentale vis-\u00e0-vis des conclusions issues de l&rsquo;analyse m\u00e9diane. Pour assurer une coh\u00e9rence stricte, il est imp\u00e9ratif de param\u00e9trer explicitement l&rsquo;argument <code>estimator<\/code> sur la fonction <code>np.median<\/code> :<\/p>\n<p><code>import matplotlib.pyplot as plt<\/code><br \/>\n<code>import seaborn as sns<\/code><\/p>\n<p><code>sns.set_theme(style=\"whitegrid\")<\/code><br \/>\n<code>fig, ax = plt.subplots(figsize=(8, 5))<\/code><br \/>\n<code>sns.barplot(<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;data=df,<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;x='equipe',<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;y='points',<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;estimator=np.median,<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;errorbar=('ci', 95),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;palette='Blues_d',<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;ax=ax<br \/>\n)<\/code><br \/>\n<code>ax.set_title(\"Niveau m\u00e9dian de scoring offensif par \u00e9quipe (IC 95 %)\")<\/code><br \/>\n<code>ax.set_ylabel(\"M\u00e9diane des points\")<\/code><\/p>\n<p>Dans cette configuration graphique, Seaborn calcule automatiquement un intervalle de confiance robuste autour de la m\u00e9diane par des techniques de r\u00e9\u00e9chantillonnage non param\u00e9trique (bootstrapping). Cette repr\u00e9sentation documente simultan\u00e9ment l&rsquo;estimation ponctuelle m\u00e9diane et l&rsquo;incertitude statistique d&rsquo;\u00e9chantillonnage inh\u00e9rente \u00e0 la taille de chaque sous-groupe.<\/p>\n<h3>10.2 Bo\u00eetes \u00e0 moustaches (Boxplots) et diagrammes en violon<\/h3>\n<p>Si le diagramme en barres documente la tendance centrale, il \u00e9choue par nature \u00e0 illustrer la forme sous-jacente de la distribution. Pour surmonter cette lacune visuelle, la bo\u00eete \u00e0 moustaches (<a rel=\"noopener noreferrer\" href=\"https:\/\/seaborn.pydata.org\/generated\/seaborn.boxplot.html\">Boxplot<\/a>), th\u00e9oris\u00e9e par le statisticien John Tukey, constitue la repr\u00e9sentation graphique par excellence de la m\u00e9diane et de ses m\u00e9triques associ\u00e9es.<\/p>\n<p>Au sein d&rsquo;un boxplot, la ligne transversale centrale traversant l&rsquo;int\u00e9rieur de la bo\u00eete cartographie pr\u00e9cis\u00e9ment la valeur de la m\u00e9diane empirique (le 50e centile). Les bords inf\u00e9rieurs et sup\u00e9rieurs du rectangle d\u00e9limitent rigoureusement le premier quartile (Q1, 25e centile) et le troisi\u00e8me quartile (Q3, 75e centile), rendant compte visuellement de l&rsquo;\u00e9tendue interquartile (IQR). Les segments prolongeant le rectangle (les moustaches) capturent les fronti\u00e8res au-del\u00e0 desquelles les observations individuelles sont formellement isol\u00e9es comme des valeurs atypiques ou aberrantes.<\/p>\n<p>Le diagramme en violon (violinplot) enrichit cette perspective en juxtaposant \u00e0 l&rsquo;ossature de la bo\u00eete \u00e0 moustaches une estimation de la densit\u00e9 par noyau de lissage (KDE). Cette visualisation met en lumi\u00e8re la bimodalit\u00e9 \u00e9ventuelle, l&rsquo;aplatissement ou les asym\u00e9tries locales de la distribution de chaque sous-groupe. La superposition optionnelle d&rsquo;un trac\u00e9 de points bruts (<code>sns.stripplot<\/code>) sur ces bo\u00eetes consolide la transparence m\u00e9thodologique en r\u00e9v\u00e9lant la densit\u00e9 r\u00e9elle des effectifs observ\u00e9s au voisinage imm\u00e9diat de la m\u00e9diane centrale.<\/p>\n<h3>10.3 Restitution tabulaire enrichie et formatage conditionnel<\/h3>\n<p>Lors de la production de rapports d&rsquo;analyse destin\u00e9s \u00e0 des comit\u00e9s de direction ou des publications acad\u00e9miques, la restitution sous forme tabulaire brute peut para\u00eetre aust\u00e8re et masquer les contrastes fondamentaux identifi\u00e9s au sein des donn\u00e9es. Pandas propose une API d\u00e9di\u00e9e d&rsquo;enrichissement esth\u00e9tique et s\u00e9mantique de ses structures via l&rsquo;accesseur <code>df.style<\/code>.<\/p>\n<p>Cette technologie permet de projeter des feuilles de style CSS directement sur la structure des cellules du <code>DataFrame<\/code> g\u00e9n\u00e9r\u00e9, sans alt\u00e9rer l&rsquo;int\u00e9grit\u00e9 num\u00e9rique sous-jacente des scalaires m\u00e9dians stock\u00e9s. Il est par exemple ais\u00e9 d&rsquo;appliquer des gradients de couleurs thermiques (heatmaps tabulaires) pour mettre en \u00e9vidence les \u00e9carts relatifs entre les groupes :<\/p>\n<p><code>tableau_formate = (<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;df.groupby(['equipe', 'position'])[['points', 'rebonds']]<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.median()<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.unstack()<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.style<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.background_gradient(cmap='YlGnBu', axis=None)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.format(precision=1)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.set_caption(\"Tableau matriciel des performances m\u00e9dianes par \u00e9quipe et position\")<br \/>\n)<\/code><\/p>\n<p>Le recours \u00e0 la m\u00e9thode <code>unstack()<\/code> pivote \u00e9l\u00e9gamment la dimension factorielle de position sur l&rsquo;axe des abscisses horizontales, produisant une matrice crois\u00e9e \u00e0 double entr\u00e9e. Le formatage conditionnel appliqu\u00e9 via <code>background_gradient<\/code> mat\u00e9rialise visuellement les cellules d&rsquo;intensit\u00e9 maximale, permettant d&rsquo;identifier imm\u00e9diatement les segments les plus performants au premier coup d&rsquo;\u0153il.<\/p>\n<h2>11. \u00c9tude de cas empirique : Analyse de donn\u00e9es psychom\u00e9triques et comportementales<\/h2>\n<h3>11.1 Pr\u00e9sentation du protocole exp\u00e9rimental et du mod\u00e8le de donn\u00e9es<\/h3>\n<p>Pour parachever l&rsquo;apprentissage de ces concepts au sein d&rsquo;un cadre exp\u00e9rimental appliqu\u00e9, d\u00e9ployons une \u00e9tude de cas inspir\u00e9e des protocoles de psychologie cognitive et de neuroergonomie comportementale. L&rsquo;exp\u00e9rience mod\u00e9lise une cohorte de sujets humains soumis \u00e0 des \u00e9preuves de vigilance attentionnelle chronom\u00e9tr\u00e9es sous diverses contraintes environnementales. Le mod\u00e8le de donn\u00e9es recueille les variables suivantes :<\/p>\n<ul>\n<li><strong>identifiant_sujet<\/strong> : Code alphanum\u00e9rique unique d\u00e9signant chaque participant volontaire.<\/li>\n<li><strong>groupe_age<\/strong> : Variable cat\u00e9gorielle segmentant les participants en deux cohortes g\u00e9n\u00e9alogiques (&lsquo;Jeunes_Adultes&rsquo;, &lsquo;Seniors&rsquo;).<\/li>\n<li><strong>condition_test<\/strong> : Facteur exp\u00e9rimental contr\u00f4l\u00e9 \u00e0 trois modalit\u00e9s testant l&rsquo;impact du contexte sonore (&lsquo;Silence&rsquo;, &lsquo;Bruit_Blanc&rsquo;, &lsquo;Musique_Distractive&rsquo;).<\/li>\n<li><strong>temps_reaction_ms<\/strong> : M\u00e9trique continue enregistrant la latence de r\u00e9ponse motrice en millisecondes face \u00e0 l&rsquo;apparition d&rsquo;un stimulus visuel impr\u00e9vu.<\/li>\n<li><strong>score_anxiete<\/strong> : Indice psychom\u00e9trique continu calibr\u00e9 sur une \u00e9chelle standardis\u00e9e allant de 0 \u00e0 100.<\/li>\n<\/ul>\n<p>Le choix de la m\u00e9diane s&rsquo;impose ici comme une exigence scientifique inali\u00e9nable. Les temps de r\u00e9action humaine manifestent universellement une distribution asym\u00e9trique positive tr\u00e8s marqu\u00e9e (loi Ex-Gaussienne ou loi Log-Normale) : la majorit\u00e9 des r\u00e9ponses physiologiques s&rsquo;agglom\u00e8rent autour d&rsquo;un seuil incompressible de 250 \u00e0 350 millisecondes, tandis que des d\u00e9crochages attentionnels transitoires ou des artefacts moteurs g\u00e9n\u00e8rent r\u00e9guli\u00e8rement des valeurs r\u00e9siduelles \u00e9tir\u00e9es au-del\u00e0 de 1500 millisecondes. Calculer une moyenne arithm\u00e9tique sur de tels signaux masquerait la vitesse d&rsquo;int\u00e9gration sensorielle r\u00e9elle des sujets.<\/p>\n<h3>11.2 Impl\u00e9mentation pas \u00e0 pas de l&rsquo;analyse factorielle<\/h3>\n<p>G\u00e9n\u00e9rons par voie programmatique un jeu de donn\u00e9es exp\u00e9rimental simul\u00e9 comportant une variabilit\u00e9 contr\u00f4l\u00e9e :<\/p>\n<p><code>np.random.seed(42)<\/code><br \/>\n<code>n_echantillon = 300<\/code><\/p>\n<p><code>conditions = np.random.choice(['Silence', 'Bruit_Blanc', 'Musique_Distractive'], size=n_echantillon)<\/code><br \/>\n<code>ages = np.random.choice(['Jeunes_Adultes', 'Seniors'], size=n_echantillon)<\/code><\/p>\n<p><code># Simulation de temps de r\u00e9action log-normaux (asym\u00e9trie physiologique)<\/code><br \/>\n<code>tr_base = np.random.lognormal(mean=5.8, sigma=0.4, size=n_echantillon)<\/code><br \/>\n<code>anxiete = np.random.normal(loc=45, scale=12, size=n_echantillon).clip(0, 100)<\/code><\/p>\n<p><code>df_psycho = pd.DataFrame({<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'groupe_age': ages,<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'condition': conditions,<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'temps_reaction': tr_base,<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;'anxiete': anxiete<br \/>\n})<\/code><\/p>\n<p>Proc\u00e9dons \u00e0 l&rsquo;\u00e9valuation factorielle crois\u00e9e pour quantifier l&rsquo;impact combin\u00e9 de la maturit\u00e9 biologique et de l&rsquo;environnement sensoriel sur les latences de traitement :<\/p>\n<p><code>analyse_latences = (<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;df_psycho.groupby(['groupe_age', 'condition'])<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.agg(<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mediane_tr=('temps_reaction', 'median'),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iqr_tr=('temps_reaction', lambda x: x.quantile(0.75) - x.quantile(0.25)),<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;effectif=('temps_reaction', 'count')<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;.reset_index()<br \/>\n)<\/code><\/p>\n<p>Dans un second temps, \u00e9tablissons une proc\u00e9dure de filtrage comportemental visant \u00e0 isoler les participants manifestant un score d&rsquo;anxi\u00e9t\u00e9 sup\u00e9rieur \u00e0 la m\u00e9diane propre de leur groupe d&rsquo;\u00e2ge respectif :<\/p>\n<p><code>anxiete_mediane_par_age = df_psycho.groupby('groupe_age')['anxiete'].transform('median')<\/code><br \/>\n<code>sujets_haute_anxiete = df_psycho[df_psycho['anxiete'] &gt; anxiete_mediane_par_age]<\/code><\/p>\n<p>Cette approche permet d&rsquo;\u00e9tudier l&rsquo;interaction entre r\u00e9activit\u00e9 sensorielle et tension psychologique au sein d&rsquo;une sous-population rigoureusement s\u00e9lectionn\u00e9e selon des crit\u00e8res robustes.<\/p>\n<h3>11.3 Synth\u00e8se et interpr\u00e9tation des r\u00e9sultats exp\u00e9rimentaux<\/h3>\n<p>L&rsquo;examen des r\u00e9sultats agr\u00e9g\u00e9s met en \u00e9vidence des interactions fondamentales :<\/p>\n<ul>\n<li>Chez les <strong>Jeunes Adultes<\/strong>, le temps de r\u00e9action m\u00e9dian sous condition &lsquo;Silence&rsquo; s&rsquo;\u00e9tablit \u00e0 une valeur typique de 318 millisecondes (avec un IQR resserr\u00e9 de 85 ms). L&rsquo;introduction d&rsquo;un &lsquo;Bruit Blanc&rsquo; ne modifie que marginalement la m\u00e9diane (325 ms), sugg\u00e9rant une r\u00e9silience attentionnelle \u00e9lev\u00e9e face \u00e0 des bruits non structur\u00e9s. En revanche, l&rsquo;introduction d&rsquo;une &lsquo;Musique Distractive&rsquo; \u00e9l\u00e8ve la m\u00e9diane \u00e0 382 millisecondes, traduisant une interf\u00e9rence s\u00e9mantique avec la t\u00e2che visuelle principale.<\/li>\n<li>Au sein de la cohorte des <strong>Seniors<\/strong>, la m\u00e9diane basale en condition silencieuse s&rsquo;\u00e9l\u00e8ve physiologiquement \u00e0 410 millisecondes. Toutefois, sous la condition &lsquo;Bruit Blanc&rsquo;, leur m\u00e9diane de r\u00e9ponse fl\u00e9chit \u00e0 390 millisecondes, attestant d&rsquo;un ph\u00e9nom\u00e8ne potentiel de r\u00e9sonance stochastique sensorielle o\u00f9 un bruit de fond r\u00e9gulier compense l&rsquo;affaiblissement de l&rsquo;acuit\u00e9 perceptive.<\/li>\n<\/ul>\n<p>Si ces m\u00eames donn\u00e9es avaient \u00e9t\u00e9 synth\u00e9tis\u00e9es par le prisme exclusif de la moyenne arithm\u00e9tique, l&rsquo;incorporation accidentelle d&rsquo;essais pollu\u00e9s par des clignements oculaires involontaires (engendrant des latences sup\u00e9rieures \u00e0 2000 ms) aurait gonfl\u00e9 les moyennes de 70 \u00e0 120 millisecondes, masquant le b\u00e9n\u00e9fice paradoxal du bruit blanc chez les a\u00een\u00e9s. Cette \u00e9tude de cas d\u00e9montre l&rsquo;absolue primaut\u00e9 de la m\u00e9diane dans la recherche exp\u00e9rimentale sur le vivant.<\/p>\n<h2>12. Erreurs fr\u00e9quentes, d\u00e9bogage et bonnes pratiques de programmation<\/h2>\n<h3>12.1 R\u00e9solution des avertissements d&rsquo;obsolescence et erreurs d&rsquo;ex\u00e9cution<\/h3>\n<p>L&rsquo;\u00e9volution continue du codebase de Pandas introduit r\u00e9guli\u00e8rement des ruptures de compatibilit\u00e9 destin\u00e9es \u00e0 assainir l&rsquo;usage du langage. L&rsquo;avertissement le plus omnipr\u00e9sent lors de l&rsquo;usage na\u00eff de <code>groupby().median()<\/code> est le redout\u00e9 <code>FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas<\/code>, ainsi que le rejet implicite des colonnes non num\u00e9riques.<\/p>\n<p>Consid\u00e9rons le message d&rsquo;alerte typique :<\/p>\n<p><code>FutureWarning: Dropping invalid columns in DataFrameGroupBy.median is deprecated. In a future version, a TypeError will be raised. Before calling .median, select only valid columns or specify the value of numeric_only.<\/code><\/p>\n<p>Pour immuniser son code contre l&rsquo;\u00e9chec d&rsquo;ex\u00e9cution programm\u00e9 dans les futures versions majeures de Pandas, deux strat\u00e9gies univoques doivent \u00eatre adopt\u00e9es :<\/p>\n<ol>\n<li>S\u00e9lectionner explicitement les colonnes continues cibles avant l&rsquo;\u00e9valuation : <code>df.groupby('groupe')[['col1', 'col2']].median()<\/code>.<\/li>\n<li>Renseigner formellement le param\u00e8tre bool\u00e9en <code>numeric_only=True<\/code> si l&rsquo;on souhaite autoriser l&rsquo;inspection automatique de toutes les colonnes quantitatives du tableau : <code>df.groupby('groupe').median(numeric_only=True)<\/code>.<\/li>\n<\/ol>\n<p>Par ailleurs, l&rsquo;erreur <code>KeyError<\/code> survient invariablement lorsqu&rsquo;un descripteur factoriel renseign\u00e9 dans <code>groupby()<\/code> est mal orthographi\u00e9 ou a \u00e9t\u00e9 pr\u00e9alablement \u00e9limin\u00e9 de l&rsquo;espace des colonnes. Une v\u00e9rification syst\u00e9matique de l&rsquo;appartenance des cl\u00e9s via l&rsquo;expression d&rsquo;assertion <code>assert all(col in df.columns for col in liste_groupes)<\/code> constitue un rempart efficace contre ces interruptions inopin\u00e9es en environnement de production.<\/p>\n<h3>12.2 \u00c9cueils d&rsquo;interpr\u00e9tation et pi\u00e8ges structurels<\/h3>\n<p>Au-del\u00e0 des erreurs de syntaxe pure, l&rsquo;\u00e9valuation des m\u00e9dianes group\u00e9es abrite un pi\u00e8ge m\u00e9thodologique pernicieux : l&rsquo;illusion de la transativit\u00e9 de la m\u00e9diane, ou l&rsquo;amalgame fallacieux entre la m\u00e9diane des m\u00e9dianes de groupes et la m\u00e9diane globale d&rsquo;une population.<\/p>\n<p>En math\u00e9matiques statistiques fondamentales, la m\u00e9diane n&rsquo;est pas un op\u00e9rateur lin\u00e9aire. Par cons\u00e9quent :<\/p>\n<p><strong>La m\u00e9diane d&rsquo;un ensemble unifi\u00e9 n&rsquo;est JAMAIS \u00e9gale \u00e0 la moyenne ou \u00e0 la m\u00e9diane des m\u00e9dianes des sous-groupes constitutifs.<\/strong><\/p>\n<p>Consid\u00e9rons deux sous-groupes de tailles tr\u00e8s in\u00e9gales : le groupe 1 comprend 1000 individus affichant une m\u00e9diane de 10, tandis que le groupe 2 ne compte que 5 observations affichant une m\u00e9diane de 100. Pr\u00e9tendre que la m\u00e9diane globale de la population s&rsquo;\u00e9tablit \u00e0 <span class=\"arabpsy-math-inline\" data-latex=\"(10 + 100) \/ 2 = 55\">$(10 + 100) \/ 2 = 55$<\/span> rel\u00e8ve d&rsquo;une aberration m\u00e9thodologique s\u00e9v\u00e8re. La m\u00e9diane globale r\u00e9elle demeurera fermement ancr\u00e9e aux alentours de 10 en raison de la pr\u00e9pond\u00e9rance num\u00e9rique absolue du premier contingent. Pour obtenir la m\u00e9diane g\u00e9n\u00e9rale, l&rsquo;analyste doit imp\u00e9rativement interroger le vecteur global non segment\u00e9 : <code>df['valeur'].median()<\/code>.<\/p>\n<p>Un autre \u00e9cueil r\u00e9current r\u00e9side dans l&rsquo;exploitation de strates factorielles \u00e0 tr\u00e8s faibles effectifs (<span class=\"arabpsy-math-inline\" data-latex=\"n_g &lt; 5\">$n_g &lt; 5$<\/span>). Sur de si petites cohortes, le concept m\u00eame de distribution ordonn\u00e9e perd de sa pertinence empirique. L&rsquo;intervalle de confiance encadrant la m\u00e9diane devient excessivement large, conf\u00e9rant \u00e0 l&rsquo;indicateur une instabilit\u00e9 stochastique majeure. Il convient d\u00e8s lors de filtrer pr\u00e9alablement les groupes sous-repr\u00e9sent\u00e9s \u00e0 l&rsquo;aide de la m\u00e9thode <code>filter()<\/code> :<\/p>\n<p><code>df_robuste = df.groupby('equipe').filter(lambda x: len(x) &gt;= 5)<\/code><\/p>\n<p>Cette mesure de pr\u00e9caution garantit que les m\u00e9dianes report\u00e9es s&rsquo;appuient sur un socle d&rsquo;observations statistiquement suffisant pour \u00e9tayer des conclusions scientifiques cr\u00e9dibles.<\/p>\n<h3>12.3 Synth\u00e8se des meilleures pratiques de codage avec Pandas<\/h3>\n<p>Pour clore cet examen exhaustif, r\u00e9capitulons les paradigmes de codage garantissant une robustesse, une efficience et une lisibilit\u00e9 maximales lors de la manipulation des m\u00e9dianes sous Pandas :<\/p>\n<ul>\n<li><strong>Pr\u00e9f\u00e9rer syst\u00e9matiquement le cha\u00eenage de m\u00e9thodes (Method Chaining)<\/strong> en encapsulant les instructions au sein de blocs parenth\u00e9s\u00e9s. Cette \u00e9criture proscrit les affectations interm\u00e9diaires redondantes et clarifie le flux logique des op\u00e9rations.<\/li>\n<li><strong>D\u00e9clarer explicitement les types de donn\u00e9es<\/strong> d\u00e8s la phase d&rsquo;ingestion (notamment par la coercition des colonnes de cha\u00eenes vers le type <code>category<\/code>), r\u00e9duisant ainsi l&#8217;empreinte m\u00e9moire et optimisant les routines de tri sous-jacentes.<\/li>\n<li><strong>Adopter l&rsquo;agr\u00e9gation nomm\u00e9e (Named Aggregation)<\/strong> afin d&rsquo;attribuer des intitul\u00e9s de colonnes clairs et explicites d\u00e8s l&rsquo;appel \u00e0 la m\u00e9thode <code>agg()<\/code>, \u00e9vitant ainsi la gestion complexe d&rsquo;un MultiIndex sur les colonnes.<\/li>\n<li><strong>Normaliser imm\u00e9diatement l&rsquo;axe indiciel<\/strong> par l&rsquo;adjonction terminale de <code>reset_index()<\/code>, garantissant que le r\u00e9sultat produit r\u00e9int\u00e8gre le format tabulaire standard indispensable \u00e0 la fluidit\u00e9 des traitements en aval.<\/li>\n<li><strong>Documenter les param\u00e8tres d&rsquo;agr\u00e9gation<\/strong> en explicitant de mani\u00e8re non \u00e9quivoque <code>numeric_only=True<\/code> et <code>dropna=False<\/code> (lorsque les donn\u00e9es incompl\u00e8tes doivent \u00eatre comptabilis\u00e9es), assurant une reproductibilit\u00e9 parfaite des analyses \u00e0 travers les \u00e9volutions logicielles futures.<\/li>\n<\/ul>\n<p>En observant scrupuleusement ces imp\u00e9ratifs techniques et m\u00e9thodologiques, les sp\u00e9cialistes de la donn\u00e9e s&rsquo;assurent d&rsquo;extraire la substantifique moelle de leurs distributions empiriques, transformant la richesse foisonnante de donn\u00e9es complexes en indicateurs synth\u00e9tiques d&rsquo;une fiabilit\u00e9 incontestable.<\/p>\n<h2>R\u00e9f\u00e9rences<\/h2>\n<ul>\n<li>McKinney, W. (2010). Data Structures for Statistical Computing in Python. In S. van der Walt &amp; J. Millman (Eds.), <em>Proceedings of the 9th Python in Science Conference<\/em> (pp. 56\u201361). <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.25080\/Majora-92bf1924-003\">https:\/\/doi.org\/10.25080\/Majora-92bf1924-003<\/a><\/li>\n<li>McKinney, W. (2022). <em>Python for Data Analysis: Data Wrangling with pandas, NumPy, and Jupyter<\/em> (3rd ed.). O&rsquo;Reilly Media. <a rel=\"noopener noreferrer\" href=\"https:\/\/wesmckinney.com\/book\/\">https:\/\/wesmckinney.com\/book\/<\/a><\/li>\n<li>Musser, D. R. (1997). Introspective Sorting and Selection Algorithms. <em>Software: Practice and Experience<\/em>, 27(8), 983\u2013993. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1002\/(SICI)1097-024X(199708)27:8&lt;983::AID-SPE117&gt;3.0.CO;2-#\">https:\/\/doi.org\/10.1002\/(SICI)1097-024X(199708)27:8&lt;983::AID-SPE117&gt;3.0.CO;2-#<\/a><\/li>\n<li>The Pandas Development Team. (2024). <em>pandas-dev\/pandas: Pandas Documentation (Version 2.2.x)<\/em>. Zenodo. <a rel=\"noopener noreferrer\" href=\"https:\/\/pandas.pydata.org\/docs\/\">https:\/\/pandas.pydata.org\/docs\/<\/a><\/li>\n<li>Tukey, J. W. (1977). <em>Exploratory Data Analysis<\/em>. Addison-Wesley.<\/li>\n<li>Wickham, H. (2011). The Split-Apply-Combine Strategy for Data Analysis. <em>Journal of Statistical Software<\/em>, 40(1), 1\u201329. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.18637\/jss.v040.i01\">https:\/\/doi.org\/10.18637\/jss.v040.i01<\/a><\/li>\n<li>Wickham, H. (2014). Tidy Data. <em>Journal of Statistical Software<\/em>, 59(10), 1\u201323. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.18637\/jss.v059.i10\">https:\/\/doi.org\/10.18637\/jss.v059.i10<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Guide acad\u00e9mique complet pour calculer la m\u00e9diane par groupe dans Pandas avec groupby, reset_index et des agr\u00e9gations avanc\u00e9es pour vos analyses de donn\u00e9es.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","template":"","categories":[2465,2464],"tags":[5803,1966,2466,2468,2212],"class_list":["post-6037","psych_stat","type-psych_stat","status-publish","hentry","category-programmation-python","category-science-des-donnees","tag-groupby","tag-mediane","tag-pandas","tag-python","tag-statistiques-descriptives"],"_links":{"self":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/6037","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat"}],"about":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/types\/psych_stat"}],"author":[{"embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6037"}],"version-history":[{"count":1,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/6037\/revisions"}],"predecessor-version":[{"id":16787,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/6037\/revisions\/16787"}],"wp:attachment":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6037"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6037"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6037"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}