{"id":8303,"date":"2026-09-17T05:26:31","date_gmt":"2026-09-17T05:26:31","guid":{"rendered":"http:\/\/french.arabpsychology.com\/statistics\/comment-calculer-smape-python\/"},"modified":"2026-09-17T05:26:31","modified_gmt":"2026-09-17T05:26:31","slug":"comment-calculer-smape-python","status":"publish","type":"psych_stat","link":"https:\/\/french.arabpsychology.com\/?psych_stat=comment-calculer-smape-python","title":{"rendered":"Comment calculer le SMAPE en Python"},"content":{"rendered":"<p>Dans le domaine de l&rsquo;\u00e9conom\u00e9trie appliqu\u00e9e, de la science des donn\u00e9es et de l&rsquo;analyse pr\u00e9dictive des s\u00e9ries temporelles, l&rsquo;\u00e9valuation de la performance des mod\u00e8les constitue une \u00e9tape m\u00e9thodologique d\u00e9terminante. La s\u00e9lection d&rsquo;un crit\u00e8re d&rsquo;\u00e9valuation pertinent ne se r\u00e9sume pas \u00e0 un simple choix d&rsquo;ing\u00e9nierie logicielle ; elle refl\u00e8te la fonction de co\u00fbt implicite que le mod\u00e9lisateur attribue aux \u00e9carts entre ses projections th\u00e9oriques et la r\u00e9alit\u00e9 empirique observ\u00e9e. Parmi la vaste nomenclature des m\u00e9triques de pr\u00e9cision relative, l&rsquo;erreur absolue sym\u00e9trique en pourcentage \u2014 commun\u00e9ment d\u00e9sign\u00e9e par son acronyme anglophone <a rel=\"noopener noreferrer\" href=\"https:\/\/en.wikipedia.org\/wiki\/Symmetric_mean_absolute_percentage_error\">SMAPE<\/a> (<em>Symmetric Mean Absolute Percentage Error<\/em>) \u2014 occupe une position charni\u00e8re, oscillant entre tradition statistique issue des comp\u00e9titions de pr\u00e9vision et innovations computationnelles en apprentissage automatique.<\/p>\n<p>Le recours aux m\u00e9triques relatives s&rsquo;av\u00e8re indispensable lorsque les analystes sont confront\u00e9s \u00e0 des s\u00e9ries h\u00e9t\u00e9rog\u00e8nes dont les \u00e9chelles de grandeur divergent de plusieurs ordres de magnitude. Alors que des m\u00e9triques absolues comme l&rsquo;erreur quadratique moyenne (RMSE) ou l&rsquo;erreur absolue moyenne (MAE) demeurent d\u00e9pendantes de l&rsquo;unit\u00e9 de mesure sous-jacente, interdisant de facto toute comparaison transversale rigoureuse, les formulations fond\u00e9es sur des pourcentages permettent d&rsquo;agr\u00e9ger des indices de performance \u00e0 travers des portefeuilles entiers de s\u00e9ries h\u00e9t\u00e9roclites. N\u00e9anmoins, l&rsquo;indicateur historique de r\u00e9f\u00e9rence, le MAPE (<em>Mean Absolute Percentage Error<\/em>), souffre de tares math\u00e9matiques r\u00e9dhibitoires, notamment une asym\u00e9trie de p\u00e9nalisation structurelle et une divergence asymptotique lorsque les observations r\u00e9elles s&rsquo;approchent du z\u00e9ro absolu.<\/p>\n<p>C&rsquo;est pr\u00e9cis\u00e9ment pour rem\u00e9dier aux apories du MAPE que le SMAPE a \u00e9t\u00e9 formalis\u00e9, puis perfectionn\u00e9 au fil des travaux pionniers de J. Scott Armstrong et de Spyros Makridakis. L&rsquo;impl\u00e9mentation de cette m\u00e9trique au sein de l&rsquo;\u00e9cosyst\u00e8me scientifique contemporain du langage Python exige toutefois une compr\u00e9hension aigu\u00eb de ses propri\u00e9t\u00e9s alg\u00e9briques, de ses singularit\u00e9s num\u00e9riques et des subtilit\u00e9s associ\u00e9es au calcul vectoriel haute performance. Cet article propose une exploration exhaustive, rigoureuse et formalis\u00e9e du calcul du SMAPE en Python, combinant analyse \u00e9pist\u00e9mologique, d\u00e9monstrations math\u00e9matiques, architectures de code optimis\u00e9es et int\u00e9grations avanc\u00e9es au sein de pipelines de validation crois\u00e9e industriels.<\/p>\n<h2>1. Introduction th\u00e9orique \u00e0 l&rsquo;erreur absolue m\u00e9diane sym\u00e9trique en pourcentage (SMAPE)<\/h2>\n<h3>1.1 D\u00e9finition \u00e9pist\u00e9mologique de la m\u00e9trique SMAPE<\/h3>\n<p>L&rsquo;\u00e9mergence du SMAPE s&rsquo;inscrit dans un mouvement de r\u00e9vision critique des outils traditionnels de la statistique pr\u00e9dictive durant le dernier quart du vingti\u00e8me si\u00e8cle. Historiquement, le MAPE s&rsquo;\u00e9tait impos\u00e9 en raison de son interpr\u00e9tabilit\u00e9 intuitive pour les d\u00e9cideurs : exprimer l&rsquo;\u00e9cart moyen sous la forme d&rsquo;un pourcentage du ph\u00e9nom\u00e8ne r\u00e9el permettait de vulgariser imm\u00e9diatement la pr\u00e9cision d&rsquo;un algorithme sans n\u00e9cessiter la ma\u00eetrise des concepts d&rsquo;\u00e9cart-type ou de r\u00e9sidus quadratiques. Toutefois, cette accessibilit\u00e9 masquait un d\u00e9s\u00e9quilibre fondamental : le MAPE divise l&rsquo;\u00e9cart absolu exclusivement par la valeur r\u00e9elle observ\u00e9e, induisant une asym\u00e9trie s\u00e9v\u00e8re lorsque les valeurs r\u00e9elles sont faibles ou nulles.<\/p>\n<p>Face \u00e0 cette limitation conceptuelle majeure, la notion de sym\u00e9trie dans l&rsquo;\u00e9valuation des erreurs pr\u00e9dictives a \u00e9t\u00e9 introduite afin d&rsquo;\u00e9galiser le traitement accord\u00e9 aux pr\u00e9visions par exc\u00e8s (surestimations) et aux pr\u00e9visions par d\u00e9faut (sous-estimations). Dans un paradigme sym\u00e9trique, l&rsquo;erreur relative ne doit pas privil\u00e9gier la trajectoire historique au d\u00e9triment de la dynamique projet\u00e9e ; elle doit appr\u00e9hender la divergence mutuelle entre les deux trajectoires comme un ph\u00e9nom\u00e8ne bilat\u00e9ral. L&rsquo;inclusion conjointe de la pr\u00e9diction et de l&rsquo;observation au d\u00e9nominateur r\u00e9tablit une \u00e9galit\u00e9 formelle de traitement math\u00e9matique entre l&rsquo;espace des valeurs empiriques et l&rsquo;espace des valeurs simul\u00e9es.<\/p>\n<p>Cette approche rev\u00eat une pertinence singuli\u00e8re non seulement dans l&rsquo;analyse quantitative de s\u00e9ries temporelles industrielles ou financi\u00e8res, mais \u00e9galement dans les d\u00e9marches psychom\u00e9triques et comportementales. D\u00e8s lors que l&rsquo;on cherche \u00e0 \u00e9valuer des construits latents ou des r\u00e9ponses cognitives dont l&rsquo;amplitude z\u00e9ro ne poss\u00e8de pas de signification ontologique absolue mais rel\u00e8ve d&rsquo;une convention m\u00e9trique arbitraire, le SMAPE offre un cadre d&rsquo;estimation temp\u00e9r\u00e9. L&rsquo;erreur calcul\u00e9e est intrins\u00e8quement born\u00e9e, \u00e9voluant g\u00e9n\u00e9ralement dans un intervalle ferm\u00e9 compris entre 0 % et 100 %, ou entre 0 % et 200 % selon la convention de normalisation adopt\u00e9e, ce qui conf\u00e8re \u00e0 cette m\u00e9trique une stabilit\u00e9 d&rsquo;interpr\u00e9tation remarquable face aux chocs exog\u00e8nes extr\u00eames.<\/p>\n<h3>1.2 Importance de l&rsquo;\u00e9valuation relative dans la mod\u00e9lisation pr\u00e9dictive<\/h3>\n<p>Dans les applications contemporaines d&rsquo;apprentissage supervis\u00e9, les jeux de donn\u00e9es englobent fr\u00e9quemment des milliers de s\u00e9ries h\u00e9t\u00e9rog\u00e8nes r\u00e9parties sur des \u00e9chelles num\u00e9riques radicalement diff\u00e9rentes. \u00c0 titre d&rsquo;exemple, dans le domaine du commerce \u00e9lectronique ou de la gestion de la cha\u00eene d&rsquo;approvisionnement, un m\u00eame algorithme de pr\u00e9vision de la demande peut \u00eatre d\u00e9ploy\u00e9 simultan\u00e9ment sur des articles \u00e0 fort d\u00e9bit, repr\u00e9sentant des volumes quotidiens de plusieurs dizaines de milliers d&rsquo;unit\u00e9s, et sur des r\u00e9f\u00e9rences de niche dont la demande n&rsquo;exc\u00e8de pas quelques unit\u00e9s sporadiques par mois. L&rsquo;utilisation d&rsquo;une m\u00e9trique d\u00e9pendante de l&rsquo;\u00e9chelle, telle que la <a rel=\"noopener noreferrer\" href=\"https:\/\/scikit-learn.org\/stable\/modules\/generated\/sklearn.metrics.mean_squared_error.html\">RMSE<\/a> (<em>Root Mean Squared Error<\/em>), conduirait le m\u00e9canisme d&rsquo;apprentissage \u00e0 minimiser quasi-exclusivement les r\u00e9sidus associ\u00e9s aux produits \u00e0 fort volume, rel\u00e9guant la pr\u00e9cision sur les produits \u00e0 faible volume au rang de bruit statistique n\u00e9gligeable.<\/p>\n<p>Les m\u00e9triques invariantes \u00e0 l&rsquo;\u00e9chelle r\u00e9solvent cette distorsion en normalisant chaque r\u00e9sidu par une mesure de magnitude locale. Cette invariance garantit que chaque point temporel ou chaque s\u00e9rie individuelle contribue de mani\u00e8re \u00e9quilibr\u00e9e au score d&rsquo;\u00e9valuation global du mod\u00e8le, ind\u00e9pendamment de la dimension m\u00e9trique du signal sous-jacent. Ainsi, une erreur de dix unit\u00e9s sur une observation de cent pr\u00e9sente une gravit\u00e9 relative identique \u00e0 une erreur d&rsquo;un million sur une observation de dix millions. Ce principe d&rsquo;homog\u00e9n\u00e9isation statistique est au c\u0153ur des cadres modernes de validation crois\u00e9e appliqu\u00e9s aux grands ensembles de donn\u00e9es distribu\u00e9s.<\/p>\n<p>En outre, l&rsquo;invariance d&rsquo;\u00e9chelle conf\u00e9r\u00e9e par le SMAPE facilite grandement la comparabilit\u00e9 externe des mod\u00e8les pr\u00e9dictifs. Elle permet aux chercheurs de confronter objectivement des architectures algorithmiques entra\u00een\u00e9es sur des contextes g\u00e9ographiques, \u00e9conomiques ou temporels distincts sans recourir \u00e0 des transformations pr\u00e9alables complexes de type standardisation z-score ou normalisation min-max, pr\u00e9servant ainsi l&rsquo;int\u00e9grit\u00e9 s\u00e9miologique des donn\u00e9es brutes tout en assurant une comparabilit\u00e9 m\u00e9trique rigoureuse.<\/p>\n<h3>1.3 Diff\u00e9renciation contextuelle entre erreurs absolues et sym\u00e9triques<\/h3>\n<p>Pour appr\u00e9hender la n\u00e9cessit\u00e9 math\u00e9matique du SMAPE, il convient d&rsquo;analyser en profondeur l&rsquo;asym\u00e9trie fondamentale de la p\u00e9nalisation inh\u00e9rente au MAPE standard. Consid\u00e9rons la formule \u00e9l\u00e9mentaire du MAPE :<\/p>\n<p>Pour une valeur r\u00e9elle <em>y<\/em> et une valeur pr\u00e9dite <em>f<\/em>, le terme d&rsquo;erreur individuelle s&rsquo;exprime comme |<em>y<\/em> &#8211; <em>f<\/em>| \/ |<em>y<\/em>|. Supposons une observation r\u00e9elle <em>y<\/em> = 100. Si le mod\u00e8le pr\u00e9dit <em>f<\/em> = 150, l&rsquo;erreur absolue est de 50, et le MAPE qui en r\u00e9sulte s&rsquo;\u00e9tablit \u00e0 |100 &#8211; 150| \/ 100 = 50 %. \u00c0 l&rsquo;inverse, si le mod\u00e8le commet une sous-estimation sym\u00e9trique en amplitude spatiale en pr\u00e9disant <em>f<\/em> = 50, l&rsquo;erreur absolue demeure \u00e9gale \u00e0 50, et le MAPE s&rsquo;\u00e9tablit \u00e9galement \u00e0 |100 &#8211; 50| \/ 100 = 50 %. Jusqu&rsquo;ici, l&rsquo;illusion de la sym\u00e9trie perdure.<\/p>\n<p>Consid\u00e9rons \u00e0 pr\u00e9sent la situation o\u00f9 les amplitudes pr\u00e9dictives divergent de fa\u00e7on disproportionn\u00e9e : si la r\u00e9alit\u00e9 est <em>y<\/em> = 10 et que la pr\u00e9diction est <em>f<\/em> = 100 (surestimation d&rsquo;un facteur 10), le MAPE explose \u00e0 |10 &#8211; 100| \/ 10 = 900 %. En revanche, si la r\u00e9alit\u00e9 est <em>y<\/em> = 100 et la pr\u00e9diction <em>f<\/em> = 10 (sous-estimation sym\u00e9trique par inversion des grandeurs), le MAPE ne s&rsquo;\u00e9l\u00e8ve qu&rsquo;\u00e0 |100 &#8211; 10| \/ 100 = 90 %. Le MAPE plafonne structurellement la p\u00e9nalisation maximale pour une sous-estimation \u00e0 100 % (lorsque la pr\u00e9diction est nulle), alors que la p\u00e9nalit\u00e9 pour une surestimation n&rsquo;admet aucune borne sup\u00e9rieure, tendant vers l&rsquo;infini \u00e0 mesure que la pr\u00e9diction s&rsquo;accro\u00eet. Par cons\u00e9quent, lors de l&rsquo;entra\u00eenement ou du calibrage d&rsquo;un estimateur guid\u00e9 par le MAPE, l&rsquo;algorithme adoptera syst\u00e9matiquement une strat\u00e9gie conservatrice de sous-estimation syst\u00e9matique pour se pr\u00e9munir contre les p\u00e9nalisations astronomiques induites par les surestimations accidentelles.<\/p>\n<p>Le SMAPE rectifie cette d\u00e9rive structurelle par l&rsquo;incorporation de la pr\u00e9diction au sein du d\u00e9nominateur d&rsquo;\u00e9chelle. En posant un diviseur fonctionnel proportionnel \u00e0 la somme (|<em>y<\/em>| + |<em>f<\/em>|), l&rsquo;impact d&rsquo;une surestimation se trouve naturellement r\u00e9gul\u00e9 : une hausse unilat\u00e9rale de <em>f<\/em> accro\u00eet simultan\u00e9ment le num\u00e9rateur et le d\u00e9nominateur, emp\u00eachant la m\u00e9trique d&rsquo;exploser vers l&rsquo;infini. Cette correction r\u00e9tablit un \u00e9quilibre g\u00e9om\u00e9trique primordial, modifiant substantiellement les paysages d&rsquo;optimisation et emp\u00eachant les mod\u00e8les de r\u00e9gression de converger vers des attracteurs d\u00e9g\u00e9n\u00e9r\u00e9s biais\u00e9s vers le bas.<\/p>\n<h2>2. Fondements math\u00e9matiques et formalisation de la formule du SMAPE<\/h2>\n<h3>2.1 D\u00e9composition alg\u00e9brique de l&rsquo;\u00e9quation standard<\/h3>\n<p>La formulation analytique la plus r\u00e9pandue et contemporaine du SMAPE, valid\u00e9e notamment lors des comp\u00e9titions d&rsquo;estimation <a rel=\"noopener noreferrer\" href=\"https:\/\/forecasters.org\/resources\/time-series-data\/\">M-Competitions<\/a> men\u00e9es par Spyros Makridakis, s&rsquo;\u00e9nonce formellement pour un vecteur d&rsquo;observations empiriques <em>Y<\/em> = (<em>y<\/em><sub>1<\/sub>, <em>y<\/em><sub>2<\/sub>, &#8230;, <em>y<\/em><sub><em>n<\/em><\/sub>) et un vecteur de pr\u00e9visions correspondantes <em>F<\/em> = (<em>f<\/em><sub>1<\/sub>, <em>f<\/em><sub>2<\/sub>, &#8230;, <em>f<\/em><sub><em>n<\/em><\/sub>) de dimension cardinale <em>n<\/em> selon l&rsquo;\u00e9quation :<\/p>\n<p><strong>SMAPE = (100% \/ n) * &sum;<sub>i=1..n<\/sub> [ 2 * |y<sub>i<\/sub> &#8211; f<sub>i<\/sub>| \/ (|y<sub>i<\/sub>| + |f<sub>i<\/sub>|) ]<\/strong><\/p>\n<p>Une analyse d\u00e9constructive de cette expression math\u00e9matique r\u00e9v\u00e8le trois composantes articul\u00e9es avec une pr\u00e9cision chirurgicale :<\/p>\n<ul>\n<li><strong>Le num\u00e9rateur d&rsquo;\u00e9cart r\u00e9siduel :<\/strong> La quantit\u00e9 2 * |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \u00e9value la distance euclidienne unidimensionnelle entre le point observ\u00e9 et le point mod\u00e9lis\u00e9, amplifi\u00e9e par un facteur scalaire multiplicatif de deux afin de compenser la division par la somme non moyenn\u00e9e du d\u00e9nominateur.<\/li>\n<li><strong>Le d\u00e9nominateur de normalisation sym\u00e9trique :<\/strong> Le terme (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) repr\u00e9sente la masse scalaire totale conjointe du ph\u00e9nom\u00e8ne et de son approximation. Dans d&rsquo;autres pr\u00e9sentations acad\u00e9miques \u00e9quivalentes, le d\u00e9nominateur est d\u00e9fini comme la moyenne arithm\u00e9tique (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) \/ 2, ce qui absorbe le scalaire 2 du num\u00e9rateur, illustrant parfaitement la volont\u00e9 d&rsquo;\u00e9valuer l&rsquo;\u00e9cart par rapport au barycentre des deux grandeurs.<\/li>\n<li><strong>L&rsquo;op\u00e9rateur d&rsquo;agr\u00e9gation d&rsquo;\u00e9chelle :<\/strong> Le pr\u00e9facteur (100% \/ <em>n<\/em>) op\u00e8re une moyenne arithm\u00e9tique simple des ratios individuels d&rsquo;inexactitude sur l&rsquo;ensemble de l&rsquo;\u00e9chantillon, transposant le r\u00e9sultat au sein d&rsquo;une \u00e9chelle indiciaire en pourcentage exploitable par l&rsquo;analyste.<\/li>\n<\/ul>\n<p>Il est imp\u00e9ratif de souligner l&rsquo;existence d&rsquo;une divergence de convention persistante dans l&rsquo;ing\u00e9nierie statistique : la distinction entre la convention born\u00e9e \u00e0 100 % et la convention born\u00e9e \u00e0 200 %. Dans l&rsquo;\u00e9quation ci-dessus, lorsque <em>y<\/em><sub><em>i<\/em><\/sub> et <em>f<\/em><sub><em>i<\/em><\/sub> sont de signes oppos\u00e9s ou lorsque l&rsquo;une des deux valeurs est nulle tandis que l&rsquo;autre est strictement positive, le ratio individuel [ 2 * |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \/ (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) ] atteint sa valeur maximale th\u00e9orique de 2. Multipli\u00e9 par 100 %, le SMAPE peut ainsi plafonner \u00e0 200 %. Certains auteurs et biblioth\u00e8ques omettent le facteur multiplicatif 2 (ou divisent le total par 2), contraignant de facto le domaine d&rsquo;arriv\u00e9e de la m\u00e9trique \u00e0 l&rsquo;intervalle [0 %, 100 %]. Il incombe au data scientist de documenter rigoureusement la convention retenue pour garantir l&rsquo;interop\u00e9rabilit\u00e9 des m\u00e9triques de reporting.<\/p>\n<h3>2.2 Propri\u00e9t\u00e9s statistiques et comportement asymptotique<\/h3>\n<p>L&rsquo;\u00e9tude analytique du SMAPE requiert une analyse attentive de son comportement aux limites de l&rsquo;espace de d\u00e9finition m\u00e9trique &real;<sup>2<\/sup>. Par conception, le score est minor\u00e9 par z\u00e9ro :<\/p>\n<p>lim<sub><em>f<\/em><sub><em>i<\/em><\/sub> &rarr; <em>y<\/em><sub><em>i<\/em><\/sub><\/sub> [ |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \/ (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) ] = 0<\/p>\n<p>Cette borne inf\u00e9rieure n&rsquo;est atteinte si et seulement si les vecteurs <em>Y<\/em> et <em>F<\/em> sont rigoureusement identiques en chaque point d&rsquo;\u00e9chantillonnage, mat\u00e9rialisant une ad\u00e9quation d\u00e9terministe parfaite du mod\u00e8le pr\u00e9dictif.<\/p>\n<p>\u00c0 l&rsquo;autre extr\u00e9mit\u00e9 spectrale, examinons le cas de divergence asymptotique o\u00f9 la pr\u00e9diction <em>f<\/em><sub><em>i<\/em><\/sub> tend vers l&rsquo;infini pour une valeur r\u00e9elle <em>y<\/em><sub><em>i<\/em><\/sub> finie et constante :<\/p>\n<p>lim<sub><em>f<\/em><sub><em>i<\/em><\/sub> &rarr; +&infin;<\/sub> [ 2 * |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \/ (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) ] = lim<sub><em>f<\/em><sub><em>i<\/em><\/sub> &rarr; +&infin;<\/sub> [ 2 * (<em>f<\/em><sub><em>i<\/em><\/sub> &#8211; <em>y<\/em><sub><em>i<\/em><\/sub>) \/ (<em>y<\/em><sub><em>i<\/em><\/sub> + <em>f<\/em><sub><em>i<\/em><\/sub>) ] = 2<\/p>\n<p>Ainsi, m\u00eame confront\u00e9 \u00e0 une pr\u00e9diction aberrante tendant vers l&rsquo;infini, la contribution d&rsquo;un point unitaire au SMAPE demeure strictement born\u00e9e \u00e0 200 % (ou 100 % selon convention). Cette propri\u00e9t\u00e9 asymptotique conf\u00e8re \u00e0 la m\u00e9trique une robustesse structurelle remarquable contre l&rsquo;\u00e9crasement statistique par des estimateurs divergents, pr\u00e9servant la stabilit\u00e9 globale du calcul face aux instabilit\u00e9s d&rsquo;\u00e9chantillonnage.<\/p>\n<p>Toutefois, la m\u00e9trique exhibe un point de discontinuit\u00e9 critique \u00e0 l&rsquo;origine (<em>y<\/em><sub><em>i<\/em><\/sub> = 0, <em>f<\/em><sub><em>i<\/em><\/sub> = 0). Lorsque l&rsquo;observation et la pr\u00e9diction s&rsquo;annulent simultan\u00e9ment, le ratio \u00e9l\u00e9mentaire pr\u00e9sente une forme ind\u00e9termin\u00e9e de type 0 \/ 0. L&rsquo;\u00e9valuation de cette limite d\u00e9pend de la direction d&rsquo;approche dans le plan &real;<sup>2<\/sup>. Si la convergence s&rsquo;op\u00e8re le long de la droite <em>f<\/em><sub><em>i<\/em><\/sub> = <em>y<\/em><sub><em>i<\/em><\/sub>, le ratio vaut 0 ; si elle s&rsquo;op\u00e8re le long d&rsquo;axes d\u00e9synchronis\u00e9s, le ratio peut osciller de mani\u00e8re instable. Ce comportement impose l&rsquo;instauration d&rsquo;une r\u00e8gle de prolongement par continuit\u00e9, assignant par d\u00e9cret une erreur relative nulle lorsque le mod\u00e8le pr\u00e9dit exactement la valeur z\u00e9ro observ\u00e9e.<\/p>\n<h3>2.3 Variantes de la formule selon la litt\u00e9rature acad\u00e9mique<\/h3>\n<p>L&rsquo;histoire de la formulation du SMAPE est jalonn\u00e9e de r\u00e9visions structurelles qui ont suscit\u00e9 d&rsquo;abondants d\u00e9bats m\u00e9thodologiques. La formule originale, attribu\u00e9e initialement aux travaux de J. Scott Armstrong en 1985 dans le cadre de ses synth\u00e8ses sur les protocoles d&rsquo;\u00e9valuation en pr\u00e9vision, s&rsquo;exprimait sous une forme d\u00e9pourvue du facteur 2 au num\u00e9rateur mais int\u00e9grant des valeurs absolues uniquement sur la diff\u00e9rence r\u00e9siduelle :<\/p>\n<p>SMAPE<sub>Armstrong<\/sub> = (100% \/ <em>n<\/em>) * &sum; [ |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \/ (<em>y<\/em><sub><em>i<\/em><\/sub> + <em>f<\/em><sub><em>i<\/em><\/sub>) ]<\/p>\n<p>Cette version initiale pr\u00e9sentait un risque majeur : en pr\u00e9sence de variables admettant des valeurs n\u00e9gatives, le d\u00e9nominateur pouvait s&rsquo;annuler m\u00eame lorsque le num\u00e9rateur n&rsquo;\u00e9tait pas nul (par exemple si <em>y<\/em><sub><em>i<\/em><\/sub> = 10 et <em>f<\/em><sub><em>i<\/em><\/sub> = -10), provoquant une division par z\u00e9ro non traitable math\u00e9matiquement, voire g\u00e9n\u00e9rant des pourcentages d&rsquo;erreur n\u00e9gatifs d\u00e9nu\u00e9s de sens statistique si <em>y<\/em><sub><em>i<\/em><\/sub> + <em>f<\/em><sub><em>i<\/em><\/sub> &lt; 0.<\/p>\n<p>En 1993, Spyros Makridakis a introduit une refonte canonique du SMAPE dans le cadre de la comp\u00e9tition M2, r\u00e9affirmant l&rsquo;imp\u00e9ratif de munir le d\u00e9nominateur de valeurs absolues ind\u00e9pendantes :<\/p>\n<p>SMAPE<sub>Makridakis<\/sub> = (100% \/ <em>n<\/em>) * &sum; [ |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \/ ((|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) \/ 2) ]<\/p>\n<p>Cette r\u00e9vision a fix\u00e9 le standard pr\u00e9valant lors de la c\u00e9l\u00e8bre comp\u00e9tition M3 en 2000. Parall\u00e8lement, des variantes r\u00e9gularis\u00e9es ont vu le jour, int\u00e9grant un amortisseur infinit\u00e9simal &epsilon; au d\u00e9nominateur afin d&rsquo;\u00e9radiquer la singularit\u00e9 de l&rsquo;origine :<\/p>\n<p>SMAPE<sub>r\u00e9gularis\u00e9<\/sub> = (200% \/ <em>n<\/em>) * &sum; [ |<em>y<\/em><sub><em>i<\/em><\/sub> &#8211; <em>f<\/em><sub><em>i<\/em><\/sub>| \/ (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>| + &epsilon;) ]<\/p>\n<p>L&rsquo;existence de ces variantes impose une rigueur absolue dans la lecture des benchmarks internationaux. Deux mod\u00e8les revendiquant un SMAPE de 15 % peuvent s&rsquo;av\u00e9rer incomparables si l&rsquo;un applique la norme \u00e0 100 % de plafond et l&rsquo;autre celle born\u00e9e \u00e0 200 %, ou si une constante d&rsquo;att\u00e9nuation a \u00e9t\u00e9 inject\u00e9e sans d\u00e9claration explicite.<\/p>\n<h2>3. Pr\u00e9paration de l&rsquo;environnement Python pour le calcul statistique<\/h2>\n<h3>3.1 Configuration des biblioth\u00e8ques scientifiques fondamentales<\/h3>\n<p>L&rsquo;impl\u00e9mentation op\u00e9rationnelle de m\u00e9triques d&rsquo;\u00e9valuation en Python s&rsquo;appuie sur un \u00e9cosyst\u00e8me hautement standardis\u00e9, mature et hautement performant. Le socle computationnel repose sur <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/\">NumPy<\/a>, biblioth\u00e8que cardinale qui fournit la structure de donn\u00e9es <code>ndarray<\/code> et des primitives de bas niveau interfac\u00e9es en C et Fortran. \u00c0 ce socle s&rsquo;ajoutent Pandas pour la manipulation fluide des s\u00e9ries chronologiques index\u00e9es par horodatage, et SciPy pour les calculs d&rsquo;alg\u00e8bre lin\u00e9aire et de distributions statistiques \u00e9labor\u00e9es.<\/p>\n<p>Pour garantir la stabilit\u00e9 et la reproductibilit\u00e9 des calculs flottants \u00e0 travers diff\u00e9rentes plates-formes d&rsquo;ex\u00e9cution, il est imp\u00e9ratif d&rsquo;auditer avec minutie les versions logicielles utilis\u00e9es. Les calculs arithm\u00e9tiques sous-jacents ob\u00e9issent \u00e0 la norme IEEE 754 r\u00e9gissant la repr\u00e9sentation binaire en virgule flottante. Toute modification non ma\u00eetris\u00e9e des compilateurs ou des biblioth\u00e8ques BLAS\/LAPACK (telles qu&rsquo;OpenBLAS, Intel MKL ou Apple Accelerate) sous-jacentes \u00e0 NumPy peut introduire d&rsquo;infimes variations d&rsquo;arrondi sur les sommes cumul\u00e9es de plusieurs millions d&rsquo;\u00e9l\u00e9ments. La standardisation de l&rsquo;environnement via des gestionnaires de conteneurs ou d&rsquo;environnements virtuels (Conda, Poetry, Venv) repr\u00e9sente un pr\u00e9requis m\u00e9thodologique non n\u00e9gociable en ing\u00e9nierie de production.<\/p>\n<h3>3.2 Structuration et typage des donn\u00e9es matricielles<\/h3>\n<p>La premi\u00e8re exigence lors de la formalisation du calcul du SMAPE r\u00e9side dans la conversion stricte des entr\u00e9es en vecteurs unidimensionnels typ\u00e9s. Dans un pipeline r\u00e9aliste, les pr\u00e9dictions et les donn\u00e9es d&rsquo;observation peuvent \u00e9maner de sources h\u00e9t\u00e9rog\u00e8nes : colonnes de DataFrames Pandas, listes de nombres scalaires en Python pur, tenseurs PyTorch ou matrices scikit-learn. Il est capital d&rsquo;op\u00e9rer une coercition propre de ces structures vers l&rsquo;objet <code>numpy.ndarray<\/code> via la m\u00e9thode <code>numpy.asarray()<\/code>, laquelle \u00e9vite les duplications m\u00e9moires superflues lorsque l&rsquo;entr\u00e9e est d\u00e9j\u00e0 un tableau conforme.<\/p>\n<p>De surcro\u00eet, le contr\u00f4le dimensionnel strict (<em>shape alignment<\/em>) pr\u00e9vient les \u00e9cueils silencieux issus des m\u00e9canismes de diffusion (<em>broadcasting<\/em>) de NumPy. Si le vecteur r\u00e9el poss\u00e8de une dimension de forme <code>(n,)<\/code> et que le vecteur pr\u00e9dictif est formul\u00e9 par m\u00e9garde en matrice colonne de dimension <code>(n, 1)<\/code>, une soustraction na\u00efve <code>y_true - y_pred<\/code> ne provoquera pas une erreur d&rsquo;ex\u00e9cution : elle engendrera une matrice bidimensionnelle de taille <code>(n, n)<\/code> r\u00e9sultant de la diffusion crois\u00e9e, alt\u00e9rant radicalement l&rsquo;int\u00e9grit\u00e9 de la mesure et saturant potentiellement la m\u00e9moire vive. Une instruction d&rsquo;aplatissement via <code>numpy.ravel()<\/code> ou une assertion formelle sur les dimensions dimensionnelles constitue une barri\u00e8re de protection imp\u00e9rative.<\/p>\n<p>Enfin, le choix du typage en virgule flottante (<em>floating-point precision<\/em>) dicte l&rsquo;\u00e9quilibre entre empreinte m\u00e9moire et fid\u00e9lit\u00e9 computationnelle. Alors que le type standard <code>numpy.float64<\/code> (double pr\u00e9cision sur 64 bits) offre une s\u00e9curit\u00e9 num\u00e9rique maximale avec 53 bits de mantisse (soit environ 15 \u00e0 17 chiffres d\u00e9cimaux significatifs), l&rsquo;adoption du <code>numpy.float32<\/code> (simple pr\u00e9cision) permet de r\u00e9duire par deux l&rsquo;espace m\u00e9moire consomm\u00e9 lors du traitement de tr\u00e8s volumineux jeux de donn\u00e9es temporels. Toutefois, en simple pr\u00e9cision, la constante machine s&rsquo;\u00e9tablit aux alentours de 1.19 &times; 10<sup>-7<\/sup>, ce qui peut engendrer des ph\u00e9nom\u00e8nes d&rsquo;annulation catastrophique lors de la soustraction de valeurs extr\u00eamement proches, ou induire des divisions par des sous-niveaux d\u00e9normalis\u00e9s.<\/p>\n<h2>4. Impl\u00e9mentation pas \u00e0 pas de la fonction SMAPE en Python pur<\/h2>\n<h3>4.1 Architecture algorithmique de base sans d\u00e9pendance externe<\/h3>\n<p>Afin de d\u00e9composer avec la plus grande transparence didactique la s\u00e9quence d&rsquo;op\u00e9rations \u00e9l\u00e9mentaires r\u00e9gissant le SMAPE, il est instructif de formuler une impl\u00e9mentation initiale reposant exclusivement sur la grammaire native de Python, affranchie de toute d\u00e9pendance tierce. Une telle impl\u00e9mentation s&rsquo;av\u00e8re pr\u00e9cieuse dans des environnements d&rsquo;ex\u00e9cution ultra-l\u00e9gers (syst\u00e8mes embarqu\u00e9s, micro-services contraints, fonctions sans serveur minimalistes) o\u00f9 le d\u00e9ploiement d&rsquo;une biblioth\u00e8que volumineuse comme NumPy est proscrit.<\/p>\n<p>L&rsquo;architecture algorithmique s&rsquo;articule autour d&rsquo;une boucle imp\u00e9rative parcourant conjointement les conteneurs it\u00e9rables repr\u00e9sentant les cibles et les pr\u00e9dictions. L&rsquo;acc\u00e8s synchronis\u00e9 est assur\u00e9 par la primitive native <code>zip()<\/code>. \u00c0 chaque it\u00e9ration, l&rsquo;algorithme calcule l&rsquo;\u00e9cart absolu \u00e9l\u00e9mentaire au num\u00e9rateur, puis la somme des valeurs absolues au d\u00e9nominateur. Pour pr\u00e9server l&rsquo;int\u00e9grit\u00e9 math\u00e9matique sans lever d&rsquo;exception de type <code>ZeroDivisionError<\/code>, une branche conditionnelle \u00e9value la nullit\u00e9 simultan\u00e9e du d\u00e9nominateur. La sommation cumulative des ratios d&rsquo;erreur individuels est conserv\u00e9e dans un accumulateur flottant avant d&rsquo;\u00eatre divis\u00e9e par le nombre total d&rsquo;observations.<\/p>\n<p>Examinons le d\u00e9roulement logique d&rsquo;une telle fonction con\u00e7ue selon les meilleures pratiques de programmation d\u00e9fensive et de typage statique :<\/p>\n<pre>\nfrom typing import Sequence\n\ndef calculer_smape_pur(\n    y_true: Sequence[float], \n    y_pred: Sequence[float], \n    convention_200: bool = True\n) -> float:\n    \"\"\"\n    Calcule le SMAPE en pur Python sans d\u00e9pendances externes.\n    G\u00e8re la convention 100% ou 200% et r\u00e9sout l'ind\u00e9termination 0\/0.\n    \"\"\"\n    if len(y_true) != len(y_pred):\n        raise ValueError(\"Les s\u00e9quences doivent poss\u00e9der rigoureusement la m\u00eame taille.\")\n    \n    n = len(y_true)\n    if n == 0:\n        return 0.0\n\n    somme_ratios = 0.0\n    facteur_echelle = 2.0 if convention_200 else 1.0\n\n    for yt, yp in zip(y_true, y_pred):\n        numerateur = abs(yt - yp)\n        denominateur = (abs(yt) + abs(yp)) \/ 2.0 if not convention_200 else (abs(yt) + abs(yp))\n        \n        if denominateur == 0.0:\n            ratio = 0.0\n        else:\n            if convention_200:\n                ratio = (2.0 * numerateur) \/ denominateur\n            else:\n                ratio = numerateur \/ denominateur\n                \n        somme_ratios += ratio\n\n    smape = (somme_ratios \/ n) * 100.0\n    return float(smape)\n<\/pre>\n<p>Cette formulation algorithmique explicite le cheminement de chaque op\u00e9ration arithm\u00e9tique. Le param\u00e8tre bool\u00e9en <code>convention_200<\/code> permet \u00e0 l&rsquo;utilisateur de basculer express\u00e9ment entre l&rsquo;\u00e9chelle plafonn\u00e9e \u00e0 200 % (canonique de Makridakis) et celle born\u00e9e \u00e0 100 %, conf\u00e9rant une flexibilit\u00e9 totale \u00e0 la fonction tout en garantissant une totale translucidit\u00e9 conceptuelle.<\/p>\n<h3>4.2 Analyse de la complexit\u00e9 computationnelle<\/h3>\n<p>L&rsquo;\u00e9valuation de la performance d&rsquo;un algorithme requiert une analyse formelle de sa complexit\u00e9 temporelle et spatiale au sens de la th\u00e9orie de la calculabilit\u00e9. La fonction <code>calculer_smape_pur<\/code> ci-dessus s&rsquo;ex\u00e9cute avec une complexit\u00e9 temporelle asymptotique strictement lin\u00e9aire, not\u00e9e <em>O<\/em>(<em>n<\/em>), o\u00f9 <em>n<\/em> repr\u00e9sente le cardinal de l&rsquo;\u00e9chantillon d&rsquo;\u00e9valuation. Chaque paire de coordonn\u00e9es (<em>y<\/em><sub><em>i<\/em><\/sub>, <em>f<\/em><sub><em>i<\/em><\/sub>) est inspect\u00e9e exactement une fois dans la boucle unitaire, et les op\u00e9rations arithm\u00e9tiques internes (soustraction, calcul de valeur absolue, addition, division \u00e9l\u00e9mentaire, affectation) s&rsquo;op\u00e8rent en temps unitaire constant <em>O<\/em>(1).<\/p>\n<p>Sur le plan de la complexit\u00e9 spatiale auxiliaire, cette impl\u00e9mentation s\u00e9quentielle pr\u00e9sente une efficacit\u00e9 optimale en <em>O<\/em>(1). L&#8217;empreinte m\u00e9moire additionnelle n&rsquo;\u00e9volue pas avec la taille du jeu de donn\u00e9es : elle se r\u00e9sume \u00e0 l&rsquo;allocation d&rsquo;une poign\u00e9e de variables scalaires h\u00e9bergeant les accumulateurs et les descripteurs d&rsquo;\u00e9tat (<code>numerateur<\/code>, <code>denominateur<\/code>, <code>somme_ratios<\/code>, <code>n<\/code>). Aucun conteneur interm\u00e9diaire proportionnel \u00e0 <em>n<\/em> n&rsquo;est allou\u00e9 dans le tas m\u00e9moire.<\/p>\n<p>Cependant, en d\u00e9pit de cette complexit\u00e9 algorithmique optimale en th\u00e9orie, cette impl\u00e9mentation se heurte aux contraintes d&rsquo;ex\u00e9cution de l&rsquo;interpr\u00e9teur standard CPython. Chaque it\u00e9ration de boucle induit un co\u00fbt de distribution dynamique de type (<em>dynamic type dispatching<\/em>), le GIL (<em>Global Interpreter Lock<\/em>) emp\u00eache toute forme de parall\u00e9lisme mat\u00e9riel direct, et les donn\u00e9es stock\u00e9es dans des listes Python natives sont fragment\u00e9es en m\u00e9moire sous forme de pointeurs vers des objets scalaires d&rsquo;ordre sup\u00e9rieur (les <code>PyObject<\/code>). Il en r\u00e9sulte une discontinuit\u00e9 dans l&rsquo;alignement des caches de processeur (<em>CPU cache misses<\/em>) et une p\u00e9nalit\u00e9 de latence colossale d\u00e8s lors que la cardinalit\u00e9 de l&rsquo;\u00e9chantillon d\u00e9passe quelques dizaines de milliers d&rsquo;enregistrements.<\/p>\n<h2>5. Vectorisation et optimisation computationnelle avec NumPy<\/h2>\n<h3>5.1 Construction d&rsquo;une fonction vectoris\u00e9e robuste<\/h3>\n<p>Pour surmonter les limitations intrins\u00e8ques de l&rsquo;interpr\u00e9teur Python, le paradigme de calcul doit migrer vers la vectorisation matricielle rendue possible par NumPy. La vectorisation consiste \u00e0 substituer des boucles d&rsquo;it\u00e9ration scalaires interpr\u00e9t\u00e9es au niveau applicatif par des instructions vectorielles compil\u00e9es op\u00e9rant sur des blocs contigus de m\u00e9moire, exploitant pleinement les registres vectoriels SIMD (<em>Single Instruction, Multiple Data<\/em>) des processeurs contemporains (AVX-2, AVX-512, ou ARM NEON).<\/p>\n<p>Dans cette architecture, les fonctions <code>np.abs()<\/code>, <code>np.subtract()<\/code> et <code>np.add()<\/code> s&rsquo;ex\u00e9cutent au travers de routines natives optimis\u00e9es en langage C. L&rsquo;enjeu technique pr\u00e9pond\u00e9rant r\u00e9side dans l&rsquo;\u00e9limination des divisions par z\u00e9ro sans rompre le flux d&rsquo;ex\u00e9cution vectoris\u00e9. L&rsquo;utilisation conjointe des fonctions <code>np.errstate<\/code> et <code>np.where<\/code> offre un m\u00e9canisme \u00e9l\u00e9gant et hautement performant : elle neutralise l&rsquo;\u00e9mission d&rsquo;avertissements de calcul flottant non critiques tout en assurant une substitution conditionnelle imm\u00e9diate de l&rsquo;ind\u00e9termination par la valeur z\u00e9ro.<\/p>\n<pre>\nimport numpy as np\n\ndef calculer_smape_numpy(\n    y_true: np.ndarray, \n    y_pred: np.ndarray, \n    convention_200: bool = True\n) -> float:\n    \"\"\"\n    Calcul vectoris\u00e9 et ultra-rapide du SMAPE via NumPy.\n    S\u00e9curise int\u00e9gralement le d\u00e9nominateur face aux singularit\u00e9s nulles.\n    \"\"\"\n    y_t = np.asarray(y_true, dtype=np.float64).ravel()\n    y_p = np.asarray(y_pred, dtype=np.float64).ravel()\n\n    if y_t.shape[0] != y_p.shape[0]:\n        raise ValueError(\"Les vecteurs doivent pr\u00e9senter une cardinalit\u00e9 strictement identique.\")\n    \n    if y_t.shape[0] == 0:\n        return 0.0\n\n    numerateur = np.abs(y_t - y_p)\n    denominateur = np.abs(y_t) + np.abs(y_p)\n\n    # Masquage vectoris\u00e9 pour \u00e9viter l'\u00e9mission de RuntimeWarning\n    with np.errstate(divide='ignore', invalid='ignore'):\n        facteur = 200.0 if convention_200 else 100.0\n        ratios = np.where(denominateur == 0.0, 0.0, (facteur * numerateur) \/ denominateur)\n        smape = np.mean(ratios)\n\n    return float(smape)\n<\/pre>\n<p>L&rsquo;avantage d&rsquo;une telle fonction vectoris\u00e9e est spectaculaire. Les tests de chronom\u00e9trage empirique (<em>profiling<\/em>) d\u00e9montrent que sur un \u00e9chantillon de dix millions de points de donn\u00e9es, l&rsquo;impl\u00e9mentation vectoris\u00e9e NumPy s&rsquo;ex\u00e9cute couramment entre cinquante et quatre-vingts fois plus rapidement que l&rsquo;alternative s\u00e9quentielle native en Python pur, r\u00e9duisant un temps de calcul de plusieurs secondes \u00e0 quelques millisecondes seulement.<\/p>\n<h3>5.2 Strat\u00e9gies d&rsquo;optimisation pour les donn\u00e9es massives<\/h3>\n<p>Lorsque le volume des donn\u00e9es pr\u00e9dictives atteint l&rsquo;ordre du gigaoctet ou de la dizaine de millions de s\u00e9ries multi-horizons, l&rsquo;allocation it\u00e9rative de tableaux interm\u00e9diaires par NumPy (comme la cr\u00e9ation cons\u00e9cutive des tableaux <code>numerateur<\/code>, <code>denominateur<\/code>, et <code>ratios<\/code>) engendre une pression m\u00e9moire substantielle sur la m\u00e9moire vive. Pour juguler cette allocation temporaire, l&rsquo;ing\u00e9nierie de calcul peut recourir \u00e0 deux strat\u00e9gies d&rsquo;optimisation avanc\u00e9es : les op\u00e9rations <em>in-place<\/em> et la compilation Just-In-Time (JIT) avec <a rel=\"noopener noreferrer\" href=\"https:\/\/numba.pydata.org\/\">Numba<\/a>.<\/p>\n<p>Les op\u00e9rations sur place r\u00e9utilisent l&rsquo;espace tampon m\u00e9moire existant par le biais de l&rsquo;argument <code>out=<\/code> des <em>ufuncs<\/em> NumPy. Ainsi, au lieu d&rsquo;instancier un nouvel objet m\u00e9moire pour accueillir la valeur absolue de la diff\u00e9rence, la fonction peut \u00e9craser le tableau interm\u00e9diaire de pr\u00e9diction si celui-ci n&rsquo;est plus requis dans le cycle analytique. Toutefois, la solution d&rsquo;optimisation absolue pour le calcul num\u00e9rique intensif r\u00e9side dans l&rsquo;exploitation de Numba via son d\u00e9corateur <code>@jit(nopython=True, fastmath=True)<\/code> :<\/p>\n<pre>\nfrom numba import jit\n\n@jit(nopython=True, fastmath=True, cache=True)\ndef calculer_smape_numba(y_true: np.ndarray, y_pred: np.ndarray) -> float:\n    \"\"\"\n    Calcul du SMAPE compil\u00e9 \u00e0 la vol\u00e9e en code machine natif via LLVM.\n    Supprime toute allocation interm\u00e9diaire et vectorise la boucle interne.\n    \"\"\"\n    n = len(y_true)\n    if n == 0:\n        return 0.0\n    \n    somme = 0.0\n    for i in range(n):\n        num = abs(y_true[i] - y_pred[i])\n        den = abs(y_true[i]) + abs(y_pred[i])\n        if den != 0.0:\n            somme += (200.0 * num) \/ den\n            \n    return somme \/ n\n<\/pre>\n<p>Le compilateur LLVM sous-jacent \u00e0 Numba fusionne l&rsquo;int\u00e9gralit\u00e9 de la boucle en un flux assembleur ultra-compact. Il applique des optimisations de type d\u00e9roulement de boucle (<em>loop unrolling<\/em>) et \u00e9limine totalement le co\u00fbt de cr\u00e9ation des tenseurs interm\u00e9diaires. L&rsquo;ex\u00e9cution atteint alors les limites physiques de la bande passante m\u00e9moire du processeur, rivalisant directement avec un code C++ optimis\u00e9.<\/p>\n<h3>5.3 Validation empirique sur un jeu de donn\u00e9es synth\u00e9tique<\/h3>\n<p>Afin de valider rigoureusement l&rsquo;ad\u00e9quation algorithmique et la convergence fonctionnelle entre notre impl\u00e9mentation vectoris\u00e9e NumPy et l&rsquo;approche compil\u00e9e Numba, il convient d&rsquo;ex\u00e9cuter un banc d&rsquo;essai sur un ensemble de donn\u00e9es synth\u00e9tique mimant les caract\u00e9ristiques d&rsquo;une s\u00e9rie temporelle r\u00e9elle : composante de tendance, oscillation sinuso\u00efdale de saisonnalit\u00e9 et bruit blanc gaussien.<\/p>\n<p>Consid\u00e9rons l&rsquo;exp\u00e9rimentation synth\u00e9tique suivante :<\/p>\n<pre>\nnp.random.seed(42)\ntaille_echantillon = 1_000_000\n\n# G\u00e9n\u00e9ration d'une s\u00e9rie temporelle synth\u00e9tique avec saisonnalit\u00e9 et bruit\ntemps = np.linspace(0, 100, taille_echantillon)\nsignal_reel = 50.0 + 15.0 * np.sin(temps) + np.random.normal(0, 5.0, taille_echantillon)\n# Injection de pr\u00e9dictions avec un biais positif et une dispersion r\u00e9siduelle\nsignal_predit = signal_reel + np.random.normal(2.0, 3.0, taille_echantillon)\n\n# Ex\u00e9cution des calculs\nscore_numpy = calculer_smape_numpy(signal_reel, signal_predit, convention_200=True)\nscore_numba = calculer_smape_numba(signal_reel, signal_predit)\n\nprint(f\"Validation Num\u00e9rique :\")\nprint(f\"SMAPE NumPy : {score_numpy:.6f} %\")\nprint(f\"SMAPE Numba : {score_numba:.6f} %\")\nassert np.isclose(score_numpy, score_numba, atol=1e-12), \"Divergence num\u00e9rique d\u00e9tect\u00e9e !\"\n<\/pre>\n<p>L&rsquo;ex\u00e9cution de cette proc\u00e9dure sur une telle volum\u00e9trie atteste d&rsquo;une concordance absolue des r\u00e9sultats jusqu&rsquo;\u00e0 la douzi\u00e8me d\u00e9cimale, confirmant l&rsquo;absence de toute d\u00e9rive num\u00e9rique li\u00e9e \u00e0 l&rsquo;ordonnancement des op\u00e9rations d&rsquo;accumulation en virgule flottante. Le score final obtenu, oscillant aux alentours de 7.5 %, s&rsquo;interpr\u00e8te sans ambigu\u00eft\u00e9 : en moyenne sym\u00e9trique, l&rsquo;amplitude des \u00e9carts pr\u00e9dictifs repr\u00e9sente approximativement 7.5 % de la magnitude barycentrique de la s\u00e9rie temporelle mod\u00e9lis\u00e9e.<\/p>\n<h2>6. Gestion des cas limites et anomalies num\u00e9riques dans l&rsquo;\u00e9valuation<\/h2>\n<h3>6.1 Traitement de la division par z\u00e9ro<\/h3>\n<p>L&rsquo;\u00e9cueil num\u00e9rique le plus insidieux dans le calcul des m\u00e9triques relatives r\u00e9side dans la manipulation des observations nulles. Alors que le MAPE standard provoque in\u00e9luctablement une division par z\u00e9ro non d\u00e9finie chaque fois qu&rsquo;une valeur empirique r\u00e9elle <em>y<\/em><sub><em>i<\/em><\/sub> est \u00e9gale \u00e0 z\u00e9ro \u2014 conduisant \u00e0 l&rsquo;apparition de valeurs infinies <code>np.inf<\/code> qui corrompent irr\u00e9m\u00e9diablement l&rsquo;agr\u00e9gation globale par moyenne arithm\u00e9tique \u2014, le SMAPE mod\u00e8re grandement ce comportement gr\u00e2ce \u00e0 la somme conjointe pr\u00e9sente au d\u00e9nominateur.<\/p>\n<p>Au sein du SMAPE, la division par z\u00e9ro ne peut survenir que dans une configuration g\u00e9om\u00e9trique unique : l&rsquo;annulation simultan\u00e9e de la cible et de la projection, soit <em>y<\/em><sub><em>i<\/em><\/sub> = 0 et <em>f<\/em><sub><em>i<\/em><\/sub> = 0. D&rsquo;un point de vue fonctionnel et m\u00e9tier, pr\u00e9dire une absence absolue d&rsquo;activit\u00e9 lorsque celle-ci se concr\u00e9tise effectivement par un z\u00e9ro constitue une pr\u00e9diction rigoureusement exacte. L&rsquo;\u00e9cart entre la r\u00e9alit\u00e9 et le mod\u00e8le \u00e9tant nul, l&rsquo;erreur relative imputable \u00e0 cet enregistrement doit imp\u00e9rativement \u00eatre \u00e9valu\u00e9e \u00e0 0.0 %.<\/p>\n<p>Pour mat\u00e9rialiser cette convention math\u00e9matique sans alt\u00e9rer les performances globales de calcul par le biais d&rsquo;exceptions d&rsquo;interruption, l&rsquo;utilisation d&rsquo;op\u00e9rateurs conditionnels vectoris\u00e9s est indispensable. Lorsque l&rsquo;expression bool\u00e9enne <code>(|y| + |f|) == 0<\/code> est \u00e9valu\u00e9e comme vraie, le masque positionne l&rsquo;\u00e9l\u00e9ment correspondant du tableau de sortie directement \u00e0 z\u00e9ro. Il est tout aussi crucial de veiller \u00e0 ce que la machine n&rsquo;\u00e9value pas de division interm\u00e9diaire susceptible d&rsquo;activer les registres d&rsquo;exception mat\u00e9riels du processeur, ce qui se fait par l&rsquo;encapsulation dans un contexte <code>np.errstate(divide='ignore', invalid='ignore')<\/code>.<\/p>\n<h3>6.2 Gestion des valeurs manquantes et infinies<\/h3>\n<p>Dans tout cycle de traitement de donn\u00e9es empiriques, les s\u00e9ries brutes sont fr\u00e9quemment entach\u00e9es de corruptions logiques telles que des valeurs manquantes repr\u00e9sent\u00e9es par <code>np.nan<\/code> (<em>Not a Number<\/em>) ou des d\u00e9bordements de calcul g\u00e9n\u00e9rant des valeurs infinies positives ou n\u00e9gatives <code>np.inf<\/code>, <code>-np.inf<\/code>. La pr\u00e9sence d&rsquo;une seule de ces entit\u00e9s au sein de l&rsquo;un des deux vecteurs propage une contamination math\u00e9matique imm\u00e9diate, entra\u00eenant l&rsquo;\u00e9valuation globale du SMAPE \u00e0 <code>NaN<\/code>.<\/p>\n<p>Une d\u00e9marche de calcul rigoureuse impose la mise en place d&rsquo;un \u00e9tage de sanitisation pr\u00e9alable des donn\u00e9es. Cet \u00e9tage ne doit pas se contenter d&rsquo;ignorer silencieusement les valeurs probl\u00e9matiques : il doit quantifier l&rsquo;ampleur de la d\u00e9gradation de l&rsquo;\u00e9chantillon et avertir le praticien quant au taux d&rsquo;attrition induit. L&rsquo;algorithme ci-dessous illustre une proc\u00e9dure de filtrage statistique hautement d\u00e9fensive :<\/p>\n<pre>\nimport warnings\n\ndef filtrer_et_calculer_smape(\n    y_true: np.ndarray, \n    y_pred: np.ndarray, \n    seuil_alerte_nan: float = 0.05\n) -> float:\n    \"\"\"\n    Sanitise les donn\u00e9es en \u00e9liminant les entr\u00e9es NaN et Infinies.\n    \u00c9met un avertissement m\u00e9thodologique si le taux de rejet d\u00e9passe le seuil critique.\n    \"\"\"\n    yt = np.asarray(y_true).ravel()\n    yp = np.asarray(y_pred).ravel()\n    \n    masque_valide = np.isfinite(yt) & np.isfinite(yp)\n    total_elements = yt.shape[0]\n    elements_valides = np.sum(masque_valide)\n    \n    if elements_valides == 0:\n        raise ValueError(\"Aucun couple d'observations valides n'a subsist\u00e9 apr\u00e8s le filtrage des NaN\/Inf.\")\n        \n    taux_rejet = (total_elements - elements_valides) \/ total_elements\n    if taux_rejet > seuil_alerte_nan:\n        warnings.warn(\n            f\"D\u00e9gradation de l'\u00e9chantillon : {taux_rejet * 100:.2f} % des observations ont \u00e9t\u00e9 \u00e9cart\u00e9es \"\n            \"en raison de valeurs manquantes ou infinies.\",\n            UserWarning\n        )\n        \n    return calculer_smape_numpy(yt[masque_valide], yp[masque_valide])\n<\/pre>\n<p>Il importe de souligner que l&rsquo;exclusion de points temporels au sein d&rsquo;une s\u00e9rie chronologique rompt l&rsquo;hypoth\u00e8se d&rsquo;\u00e9quidistance des pas de temps, ce qui peut fausser l&rsquo;interpr\u00e9tation longitudinale de la performance pr\u00e9dictive globale si les donn\u00e9es manquantes sont structurellement corr\u00e9l\u00e9es \u00e0 des p\u00e9riodes de volatilit\u00e9 extr\u00eame.<\/p>\n<h3>6.3 Comportement face aux valeurs n\u00e9gatives<\/h3>\n<p>L&rsquo;un des angles morts les plus redoutables du SMAPE r\u00e9side dans son application aveugle \u00e0 des jeux de donn\u00e9es comportant des grandeurs scalaires strictement n\u00e9gatives. Bien que la pr\u00e9sence de valeurs absolues ind\u00e9pendantes au d\u00e9nominateur (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) pr\u00e9munisse la fonction contre l&rsquo;annulation catastrophique qui minait la version originelle d&rsquo;Armstrong (o\u00f9 <em>y<\/em> + <em>f<\/em> pouvait valoir z\u00e9ro pour <em>y<\/em> = &#8211;<em>f<\/em> &ne; 0), le comportement g\u00e9om\u00e9trique de la m\u00e9trique en pr\u00e9sence de nombres r\u00e9els n\u00e9gatifs soul\u00e8ve de profondes interrogations th\u00e9oriques.<\/p>\n<p>Consid\u00e9rons une situation o\u00f9 une observation r\u00e9elle est <em>y<\/em> = -10, et que le mod\u00e8le \u00e9met une pr\u00e9diction <em>f<\/em> = 10. L&rsquo;\u00e9cart absolu au num\u00e9rateur s&rsquo;\u00e9tablit \u00e0 |(-10) &#8211; 10| = 20. Le d\u00e9nominateur de la formule de Makridakis s&rsquo;\u00e9tablit quant \u00e0 lui \u00e0 |-10| + |10| = 20. Le ratio unitaire obtenu s&rsquo;\u00e9l\u00e8ve alors \u00e0 2 * 20 \/ 20 = 2, soit une erreur maximale de 200 %. Consid\u00e9rons \u00e0 pr\u00e9sent un cas o\u00f9 l&rsquo;observation r\u00e9elle est <em>y<\/em> = -10 et la pr\u00e9diction <em>f<\/em> = -20. L&rsquo;\u00e9cart est de |(-10) &#8211; (-20)| = 10, et le d\u00e9nominateur est de |-10| + |-20| = 30. Le ratio unitaire obtenu est 2 * 10 \/ 30 = 0.666 (soit 66.7 %).<\/p>\n<p>Cependant, si les deux donn\u00e9es poss\u00e8dent des signes oppos\u00e9s dont la somme absolue est asym\u00e9trique, des distorsions d&rsquo;interpr\u00e9tation notables apparaissent. Par cons\u00e9quent, il est universellement admis dans la th\u00e9orie statistique moderne que le SMAPE n&rsquo;est formellement valide que sur le domaine des variables strictement positives ou semi-positives (telles que des volumes de ventes, des temps de latence de calcul, des concentrations biochimiques ou des flux de trafic r\u00e9seau). Son d\u00e9ploiement sur des s\u00e9ries admettant nativement des plages n\u00e9gatives (comme des rendements boursiers logarithmiques, des bilans thermiques en degr\u00e9s Celsius ou des profits nets consolid\u00e9s) doit \u00eatre formellement proscrit au profit de m\u00e9triques d&rsquo;erreur absolue non relatives (MAE) ou d&rsquo;erreurs relatives normalis\u00e9es par l&rsquo;\u00e9chelle historique comme le <a rel=\"noopener noreferrer\" href=\"https:\/\/robjhyndman.com\/papers\/mase.pdf\">MASE<\/a> (<em>Mean Absolute Scaled Error<\/em>).<\/p>\n<h2>7. Analyse comparative : SMAPE contre MAPE, MAE et RMSE en mod\u00e9lisation pr\u00e9dictive<\/h2>\n<h3>7.1 Confrontation directe avec le MAPE<\/h3>\n<p>Pour mesurer les gains computationnels apport\u00e9s par le SMAPE, il s&rsquo;av\u00e8re indispensable d&rsquo;analyser son comportement face au MAPE au moyen d&rsquo;une typologie compar\u00e9e des r\u00e9ponses algorithmiques face \u00e0 des profils de r\u00e9sidus asym\u00e9triques. Le tableau ci-dessous synth\u00e9tise les propri\u00e9t\u00e9s formelles des deux m\u00e9triques face \u00e0 des variations dynamiques de l&rsquo;espace pr\u00e9dictif :<\/p>\n<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse: collapse; width: 100%; margin: 20px 0;\">\n<thead>\n<tr style=\"background-color: #f2f2f2; text-align: left;\">\n<th>Crit\u00e8re \u00c9pist\u00e9mologique<\/th>\n<th>MAPE (Formulation Standard)<\/th>\n<th>SMAPE (Formulation Makridakis)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Domaine d&rsquo;arriv\u00e9e th\u00e9orique<\/strong><\/td>\n<td>[0 %, +&infin;[ (Non born\u00e9 sup\u00e9rieurement)<\/td>\n<td>[0 %, 200 %] (ou [0 %, 100 %])<\/td>\n<\/tr>\n<tr>\n<td><strong>P\u00e9nalisation de la surestimation extr\u00eame<\/strong><\/td>\n<td>Infinie : tend vers +&infin; lorsque <em>f<\/em> &gt;&gt; <em>y<\/em><\/td>\n<td>Plafonn\u00e9e : tend asymptotiquement vers 200 %<\/td>\n<\/tr>\n<tr>\n<td><strong>P\u00e9nalisation de la sous-estimation extr\u00eame<\/strong><\/td>\n<td>Born\u00e9e unilat\u00e9ralement \u00e0 100 % (lorsque <em>f<\/em> = 0)<\/td>\n<td>Plafonn\u00e9e \u00e0 200 % (lorsque <em>f<\/em> = 0 et <em>y<\/em> &gt; 0)<\/td>\n<\/tr>\n<tr>\n<td><strong>Sensibilit\u00e9 au z\u00e9ro empirique (<em>y<\/em> = 0)<\/strong><\/td>\n<td>Divergence singuli\u00e8re fatale (division par z\u00e9ro)<\/td>\n<td>Trait\u00e9e par continuit\u00e9 (0 % si <em>f<\/em> = 0, 200 % si <em>f<\/em> &gt; 0)<\/td>\n<\/tr>\n<tr>\n<td><strong>Biais d&rsquo;optimisation induit<\/strong><\/td>\n<td>Biais massif favorisant les sous-estimations syst\u00e9matiques<\/td>\n<td>\u00c9quilibre relatif bien sup\u00e9rieur entre sur- et sous-estimations<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Cette divergence structurelle dicte la politique d&rsquo;apprentissage automatique des algorithmes de gradient. Lorsqu&rsquo;un mod\u00e8le de gradient boosting (tel que XGBoost ou LightGBM) est param\u00e9tr\u00e9 avec une fonction de perte approximant le MAPE, il converge inexorablement vers des pr\u00e9dictions qui compriment l&rsquo;esp\u00e9rance math\u00e9matique des estimations, g\u00e9n\u00e9rant un sous-dimensionnement pr\u00e9dictif chronique qui s&rsquo;av\u00e8re particuli\u00e8rement pr\u00e9judiciable pour les activit\u00e9s industrielles requ\u00e9rant un dimensionnement capacitaire ad\u00e9quat.<\/p>\n<h3>7.2 Compl\u00e9mentarit\u00e9 avec les m\u00e9triques d&rsquo;\u00e9chelle absolue (MAE et RMSE)<\/h3>\n<p>L&rsquo;erreur absolue moyenne (<a rel=\"noopener noreferrer\" href=\"https:\/\/scikit-learn.org\/stable\/modules\/generated\/sklearn.metrics.mean_absolute_error.html\">MAE<\/a>) et l&rsquo;erreur quadratique moyenne (RMSE) constituent les pierres angulaires de l&rsquo;\u00e9valuation en r\u00e9gression. La MAE, d\u00e9finie comme l&rsquo;esp\u00e9rance de la valeur absolue des r\u00e9sidus <em>E<\/em>[|<em>Y<\/em> &#8211; <em>F<\/em>|], pr\u00e9sente l&rsquo;insigne avantage de s&rsquo;exprimer exactement dans l&rsquo;unit\u00e9 physique de la variable cible : des euros, des m\u00e9gawatts-heures, ou des millisecondes d&rsquo;attente. Cette propri\u00e9t\u00e9 conf\u00e8re \u00e0 la MAE une interpr\u00e9tabilit\u00e9 directe pour les directions op\u00e9rationnelles, permettant un arbitrage \u00e9conomique transparent.<\/p>\n<p>De son c\u00f4t\u00e9, la RMSE conf\u00e8re une p\u00e9nalisation quadratique aux r\u00e9sidus en vertu de l&rsquo;\u00e9l\u00e9vation au carr\u00e9 de l&rsquo;\u00e9cart. Elle correspond au second moment de la distribution de l&rsquo;erreur, traduisant une intol\u00e9rance d\u00e9lib\u00e9r\u00e9e envers les valeurs aberrantes isol\u00e9es. Un \u00e9cart d&rsquo;amplitude 10 p\u00e8se cent fois plus lourd dans la fonction de co\u00fbt d&rsquo;une RMSE qu&rsquo;un \u00e9cart unitaire, alors qu&rsquo;il n&rsquo;en p\u00e8se que dix fois plus au sens de la MAE. Toutefois, tant la MAE que la RMSE demeurent totalement aveugles \u00e0 l&rsquo;\u00e9chelle sous-jacente du ph\u00e9nom\u00e8ne : une MAE de 50 unit\u00e9s est insignifiante si l&rsquo;on mod\u00e9lise le PIB d&rsquo;une nation en milliards, mais s&rsquo;av\u00e8re catastrophique si l&rsquo;on mesure la temp\u00e9rature corporelle d&rsquo;un patient en degr\u00e9s Celsius.<\/p>\n<p>C&rsquo;est dans cette dialectique que r\u00e9side la compl\u00e9mentarit\u00e9 imp\u00e9rative entre le SMAPE et les m\u00e9triques absolues. Alors que le SMAPE permet de dresser un tableau de bord comparatif transversal \u00e0 travers des centaines de cat\u00e9gories h\u00e9t\u00e9roclites en \u00e9liminant les biais de dimensionnalit\u00e9, la MAE et la RMSE doivent \u00eatre mobilis\u00e9es conjointement au sein de chaque strate homog\u00e8ne afin de quantifier l&rsquo;impact physique et \u00e9conomique des d\u00e9viations r\u00e9siduelles.<\/p>\n<h3>7.3 Synth\u00e8se des avantages comp\u00e9titifs et faiblesses inh\u00e9rentes<\/h3>\n<p>L&rsquo;adoption du SMAPE dans les protocoles de validation d&rsquo;algorithmes pr\u00e9dictifs procure des b\u00e9n\u00e9fices ind\u00e9niables, largement document\u00e9s dans la litt\u00e9rature issue des comp\u00e9titions de pr\u00e9vision M3 et M4 organis\u00e9es par Makridakis. Son atout majeur r\u00e9side dans son immunit\u00e9 aux explosions num\u00e9riques engendr\u00e9es par des valeurs cibles microscopiques, stabilisant les agr\u00e9gations de cohortes d&rsquo;\u00e9valuation l\u00e0 o\u00f9 le MAPE \u00e9choue syst\u00e9matiquement. Sa formulation born\u00e9e offre en outre une m\u00e9trique d&rsquo;une lisibilit\u00e9 imm\u00e9diate pour la gouvernance organisationnelle, traduisant l&rsquo;erreur pr\u00e9dictive en un coefficient born\u00e9 compr\u00e9hensible par tous les acteurs d&rsquo;une organisation.<\/p>\n<p>Cependant, le SMAPE tra\u00eene un passif critique non n\u00e9gligeable qui a suscit\u00e9 de vives r\u00e9probations de la part d&rsquo;\u00e9minents statisticiens tels que Rob J. Hyndman. En premier lieu, la m\u00e9trique n&rsquo;est pas strictement sym\u00e9trique d&rsquo;un point de vue fonctionnel absolu : le traitement accord\u00e9 aux sous-estimations et aux surestimations n&rsquo;est parfaitement inversible que dans un sens restreint. En r\u00e9alit\u00e9, pour une m\u00eame distance spatiale |<em>y<\/em> &#8211; <em>f<\/em>|, la p\u00e9nalit\u00e9 demeure plus lourde si la sous-estimation s&rsquo;accro\u00eet par rapport au point d&rsquo;observation originel.<\/p>\n<p>En second lieu, la non-diff\u00e9rentiabilit\u00e9 d&rsquo;ordre sup\u00e9rieur du SMAPE constitue un obstacle pour son utilisation directe comme fonction de perte dans les algorithmes bas\u00e9s sur la descente de gradient, \u00e0 l&rsquo;image des r\u00e9seaux de neurones profonds ou des arbres de r\u00e9gression diff\u00e9rentiables. Les points d&rsquo;inflexion non lisses caus\u00e9s par les valeurs absolues et la discontinu\u00eft\u00e9 \u00e0 l&rsquo;origine rendent l&rsquo;optimisation math\u00e9matique instable sans le recours \u00e0 des approximations convexes ou lisses.<\/p>\n<h2>8. Int\u00e9gration du SMAPE dans les pipelines de validation crois\u00e9e et scikit-learn<\/h2>\n<h3>8.1 Cr\u00e9ation d&rsquo;un scorer personnalis\u00e9 pour scikit-learn<\/h3>\n<p>L&rsquo;\u00e9cosyst\u00e8me de mod\u00e9lisation pr\u00e9dictive en Python s&rsquo;articule largement autour de la biblioth\u00e8que scikit-learn. Pour exploiter le SMAPE lors des phases d&rsquo;\u00e9valuation automatis\u00e9e, d&rsquo;analyse de r\u00e9sidus ou d&rsquo;ajustement syst\u00e9matique des hyperparam\u00e8tres, il est n\u00e9cessaire d&rsquo;interfacer notre fonction vectoris\u00e9e avec l&rsquo;API standard d&rsquo;estimation de scikit-learn via l&rsquo;utilitaire <code>make_scorer<\/code> issu du module <code>sklearn.metrics<\/code>.<\/p>\n<p>Une pr\u00e9caution m\u00e9thodologique d\u00e9terminante doit \u00eatre scrupuleusement observ\u00e9e lors de cette d\u00e9claration : par convention algorithmique immuable au sein de scikit-learn, les scores d&rsquo;optimisation sont con\u00e7us selon le paradigme de maximisation de l&rsquo;utilit\u00e9 (\u00ab <em>greater is better<\/em> \u00bb). Or, une m\u00e9trique d&rsquo;erreur statistique constitue une fonction de co\u00fbt (ou perte) dont la minimisation est recherch\u00e9e. Il est donc fondamental de positionner le param\u00e8tre <code>greater_is_better=False<\/code> lors de la cr\u00e9ation du scorer ; scikit-learn se chargera alors d&rsquo;inverser le signe arithm\u00e9tique de la m\u00e9trique en lui adjoignant un coefficient n\u00e9gatif pour l&rsquo;ordonnancement interne des mod\u00e8les lors des recherches par grille ou par tirage al\u00e9atoire.<\/p>\n<pre>\nfrom sklearn.metrics import make_scorer\nfrom sklearn.base import BaseEstimator\n\ndef score_smape_sklearn(y_true, y_pred) -> float:\n    \"\"\"\n    Fonction de pont conforme \u00e0 l'API scikit-learn.\n    Prend en charge les formats numpy, pandas ou listes natives.\n    \"\"\"\n    return calculer_smape_numpy(y_true, y_pred, convention_200=True)\n\n# Instanciation de l'objet scorer scikit-learn\nsmape_scorer = make_scorer(\n    score_func=score_smape_sklearn,\n    greater_is_better=False,  # Indispensable pour la minimisation d'erreur\n    needs_proba=False\n)\n<\/pre>\n<p>L&rsquo;objet <code>smape_scorer<\/code> ainsi instanci\u00e9 s&rsquo;int\u00e8gre harmonieusement avec toutes les primitives d&rsquo;\u00e9valuation crois\u00e9e de scikit-learn, notamment <code>cross_val_score<\/code>, <code>cross_validate<\/code> et les optimiseurs d&rsquo;espaces d&rsquo;hyperparam\u00e8tres.<\/p>\n<h3>8.2 Optimisation des hyperparam\u00e8tres bas\u00e9e sur le SMAPE<\/h3>\n<p>L&rsquo;exploitation pratique d&rsquo;un scorer personnalis\u00e9 trouve son illustration la plus f\u00e9conde dans le calibrage fin d&rsquo;algorithmes ensemblistes complexes tels que les for\u00eats al\u00e9atoires (<em>Random Forest<\/em>) ou les machines d&rsquo;amplification de gradient (<em>Gradient Boosting<\/em>). Consid\u00e9rons le d\u00e9ploiement d&rsquo;une proc\u00e9dure d&rsquo;ajustement par recherche exhaustive sur grille (<code>GridSearchCV<\/code>) visant \u00e0 minimiser le SMAPE sur un jeu de donn\u00e9es d&rsquo;apprentissage :<\/p>\n<pre>\nfrom sklearn.ensemble import RandomForestRegressor\nfrom sklearn.model_selection import GridSearchCV, KFold\nfrom sklearn.datasets import make_regression\n\n# G\u00e9n\u00e9ration d'un ensemble de r\u00e9gression synth\u00e9tique non-n\u00e9gatif\nX, y_brut = make_regression(n_samples=5000, n_features=20, noise=0.5, random_state=42)\ny_positif = np.exp((y_brut - np.min(y_brut)) \/ 100.0) # Assurer des cibles strictement positives\n\n# D\u00e9finition du mod\u00e8le de r\u00e9f\u00e9rence\nmodele_rf = RandomForestRegressor(random_state=42)\n\n# Grille de recherche d'hyperparam\u00e8tres\ngrille_parametres = {\n    'n_estimators': [50, 100],\n    'max_depth': [5, 10, None],\n    'min_samples_split': [2, 5]\n}\n\n# Validation crois\u00e9e k-blocs stratifi\u00e9e temporellement ou spatialement\ncv_strategie = KFold(n_splits=5, shuffle=True, random_state=42)\n\n# Configuration de la recherche guid\u00e9e par la minimisation du SMAPE\nrecherche_grid = GridSearchCV(\n    estimator=modele_rf,\n    param_grid=grille_parametres,\n    scoring=smape_scorer,\n    cv=cv_strategie,\n    n_jobs=-1,\n    verbose=1\n)\n\n# Ex\u00e9cution de l'optimisation\nrecherche_grid.fit(X, y_positif)\n\nprint(f\"Meilleurs hyperparam\u00e8tres : {recherche_grid.best_params_}\")\nprint(f\"Meilleur SMAPE d'\u00e9valuation crois\u00e9e : {-recherche_grid.best_score_:.4f} %\")\n<\/pre>\n<p>Il importe de noter l&rsquo;adjonction de l&rsquo;op\u00e9rateur unaire de n\u00e9gation <code>-recherche_grid.best_score_<\/code> lors de la restitution du r\u00e9sultat d&rsquo;\u00e9valuation : le score ayant \u00e9t\u00e9 stock\u00e9 sous forme n\u00e9gative pour complaire aux conventions internes de scikit-learn, cette transformation r\u00e9tablit la valeur positive canonique de l&rsquo;erreur pour les synth\u00e8ses d&rsquo;ing\u00e9nierie.<\/p>\n<h3>8.3 D\u00e9veloppement d&rsquo;une classe d&rsquo;\u00e9valuation personnalis\u00e9e<\/h3>\n<p>Dans un contexte de d\u00e9veloppement logiciel industriel orient\u00e9 objet, la juxtaposition d\u00e9sordonn\u00e9e de fonctions scalaires d&rsquo;\u00e9valuation \u00e9parses constitue une dette technique majeure. Il est hautement pr\u00e9f\u00e9rable d&rsquo;encapsuler la cha\u00eene d&rsquo;\u00e9valuation des m\u00e9triques au sein d&rsquo;une structure de classe unifi\u00e9e et extensible. Une telle classe d&rsquo;\u00e9valuation offre la possibilit\u00e9 d&rsquo;agr\u00e9ger simultan\u00e9ment des m\u00e9triques d&rsquo;\u00e9chelles disparates (SMAPE, MAPE, MAE, RMSE, R\u00b2) et de produire instantan\u00e9ment des bilans de performance structur\u00e9s sous forme de DataFrames Pandas exportables vers des formats de reporting (JSON, Parquet, HTML).<\/p>\n<p>Le bloc de code ci-apr\u00e8s illustre la mat\u00e9rialisation d&rsquo;une classe d&rsquo;ing\u00e9nierie pr\u00e9dictive d&rsquo;\u00e9valuation multi-crit\u00e8res :<\/p>\n<pre>\nimport pandas as pd\nfrom sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score\n\nclass EvaluateurPredictif:\n    \"\"\"\n    Cadre d'\u00e9valuation multi-m\u00e9trique orient\u00e9 objet pour pipelines de production.\n    Centralise le calcul, l'analyse comparative et l'exportation des m\u00e9triques.\n    \"\"\"\n    def __init__(self, y_reel: np.ndarray, y_pred: np.ndarray):\n        self.y_reel = np.asarray(y_reel, dtype=np.float64).ravel()\n        self.y_pred = np.asarray(y_pred, dtype=np.float64).ravel()\n        self._valider_conformite()\n        \n    def _valider_conformite(self):\n        if self.y_reel.shape[0] != self.y_pred.shape[0]:\n            raise ValueError(\"Incompatibilit\u00e9 dimensionnelle entre cibles r\u00e9elles et pr\u00e9dictions.\")\n        if not np.all(np.isfinite(self.y_reel)) or not np.all(np.isfinite(self.y_pred)):\n            raise ValueError(\"Les tenseurs d'entr\u00e9e contiennent des valeurs infinies ou ind\u00e9termin\u00e9es (NaN).\")\n\n    def smape(self, convention_200: bool = True) -> float:\n        return calculer_smape_numpy(self.y_reel, self.y_pred, convention_200=convention_200)\n\n    def mape(self) -> float:\n        with np.errstate(divide='ignore', invalid='ignore'):\n            ratios = np.abs((self.y_reel - self.y_pred) \/ self.y_reel)\n            ratios_nettoyes = np.where(self.y_reel == 0.0, 0.0, ratios)\n            return float(np.mean(ratios_nettoyes) * 100.0)\n\n    def mae(self) -> float:\n        return float(mean_absolute_error(self.y_reel, self.y_pred))\n\n    def rmse(self) -> float:\n        return float(np.sqrt(mean_squared_error(self.y_reel, self.y_pred)))\n\n    def r2(self) -> float:\n        return float(r2_score(self.y_reel, self.y_pred))\n\n    def generer_rapport(self) -> pd.DataFrame:\n        donnees = {\n            \"M\u00e9trique\": [\n                \"SMAPE (Sym\u00e9trique 200%)\", \n                \"MAPE (Standard)\", \n                \"MAE (Erreur Absolue Moyenne)\", \n                \"RMSE (Racine de l'\u00c9cart Quadratique)\", \n                \"Coefficient de D\u00e9termination R\u00b2\"\n            ],\n            \"Valeur\": [\n                self.smape(convention_200=True),\n                self.mape(),\n                self.mae(),\n                self.rmse(),\n                self.r2()\n            ],\n            \"Unit\u00e9\": [\"%\", \"%\", \"Unit\u00e9s de y\", \"Unit\u00e9s de y\", \"Adimensionnel\"]\n        }\n        return pd.DataFrame(donnees)\n<\/pre>\n<p>L&rsquo;utilisation d&rsquo;une telle classe garantit une gouvernance robuste des r\u00e9sultats : les contr\u00f4les pr\u00e9alables de conformit\u00e9 des tenseurs sont centralis\u00e9s, assurant l&rsquo;homog\u00e9n\u00e9it\u00e9 des calculs entre les phases d&rsquo;exp\u00e9rimentation en notebook et le d\u00e9ploiement op\u00e9rationnel dans des microservices conteneuris\u00e9s.<\/p>\n<h2>9. Application pratique aux s\u00e9ries temporelles et donn\u00e9es comportementales<\/h2>\n<h3>9.1 Pr\u00e9vision de variables longitudinales et chronobiologiques<\/h3>\n<p>L&rsquo;\u00e9valuation quantitative des dynamiques longitudinales dans le domaine des sciences cognitives, de la psychom\u00e9trie et de la chronobiologie offre un terrain d&rsquo;application particuli\u00e8rement instructif pour le SMAPE. Dans ces disciplines, les protocoles exp\u00e9rimentaux mesurent fr\u00e9quemment des grandeurs telles que la latence de traitement sensoriel (temps de r\u00e9action en millisecondes), les indices de charge cognitive d\u00e9riv\u00e9e de la pupillom\u00e9trie, ou les fluctuations nycth\u00e9m\u00e9rales de s\u00e9cr\u00e9tions hormonales.<\/p>\n<p>Ces donn\u00e9es comportementales se caract\u00e9risent structurellement par une asym\u00e9trie distributionnelle marqu\u00e9e, exhibant des queues \u00e9paisses \u00e0 droite (distributions de type Ex-Gaussienne ou Log-Normale) et une h\u00e9t\u00e9rosc\u00e9dasticit\u00e9 temporelle prononc\u00e9e. Les temps de r\u00e9action, par exemple, sont physiologiquement contraints par une borne incompressible vers le bas (environ 150 millisecondes correspondant au d\u00e9lai de transmission synaptique axonale), tout en \u00e9tant sujets \u00e0 des allongements soudains caus\u00e9s par des d\u00e9crochages attentionnels transitoires.<\/p>\n<p>L&rsquo;utilisation du SMAPE calcul\u00e9 le long de fen\u00eatres d&rsquo;observation glissantes (<em>rolling evaluation windows<\/em>) permet de capter la pr\u00e9cision relative des mod\u00e8les pr\u00e9dictifs comportementaux sans que les \u00e9pisodes sporadiques d&rsquo;inattention n&rsquo;induisent un \u00e9crasement artificiel de la variance explicative. En observant l&rsquo;\u00e9volution temporelle du SMAPE glissant, le chercheur identifie instantan\u00e9ment les r\u00e9gimes comportementaux o\u00f9 l&rsquo;ad\u00e9quation algorithmique se d\u00e9grade, ind\u00e9pendamment des variations de base de la performance brute de l&rsquo;individu test\u00e9.<\/p>\n<h3>9.2 Mod\u00e9lisation comparative avec des mod\u00e8les de r\u00e9f\u00e9rence<\/h3>\n<p>Toute initiative d&rsquo;apprentissage supervis\u00e9 appliqu\u00e9e aux s\u00e9ries temporelles doit se soumettre au principe de parcimonie scientifique en se confrontant \u00e0 des mod\u00e8les de r\u00e9f\u00e9rence (<em>heuristiques na\u00efves<\/em>). En pr\u00e9vision chronologique, deux mod\u00e8les de base s&rsquo;imposent :<\/p>\n<ul>\n<li><strong>Le mod\u00e8le na\u00eff persistant (Random Walk sans d\u00e9rive) :<\/strong> La pr\u00e9diction pour l&rsquo;instant <em>t<\/em> + 1 est strictement \u00e9gale \u00e0 l&rsquo;observation r\u00e9alis\u00e9e \u00e0 l&rsquo;instant <em>t<\/em> (<em>f<\/em><sub><em>t<\/em>+1<\/sub> = <em>y<\/em><sub><em>t<\/em><\/sub>).<\/li>\n<li><strong>Le mod\u00e8le na\u00eff saisonnier :<\/strong> La pr\u00e9diction est pos\u00e9e \u00e9gale \u00e0 l&rsquo;observation enregistr\u00e9e lors de la p\u00e9riode saisonni\u00e8re ant\u00e9rieure homologue (<em>f<\/em><sub><em>t<\/em>+1<\/sub> = <em>y<\/em><sub><em>t<\/em>+1-<em>s<\/em><\/sub>, o\u00f9 <em>s<\/em> est la p\u00e9riode de cycle).<\/li>\n<\/ul>\n<p>La valeur ajout\u00e9e d&rsquo;un mod\u00e8le statistique param\u00e9trique avanc\u00e9 (tel qu&rsquo;un mod\u00e8le autor\u00e9gressif int\u00e9gr\u00e9 \u00e0 moyenne mobile, <a rel=\"noopener noreferrer\" href=\"https:\/\/otexts.com\/fpp3\/arima.html\">ARIMA<\/a>) ou d&rsquo;une architecture d&rsquo;apprentissage profond (comme les r\u00e9seaux neuronaux r\u00e9currents LSTM ou les encodeurs bas\u00e9s sur le m\u00e9canisme d&rsquo;attention Transformer) s&rsquo;\u00e9value \u00e0 l&rsquo;aune du diff\u00e9rentiel de pr\u00e9cision relative quantifi\u00e9 par le SMAPE :<\/p>\n<pre>\n# Supposons un vecteur de s\u00e9rie temporelle 'chronique_reelle'\nchronique_reelle = np.array([120, 125, 122, 130, 128, 140, 138, 145, 150, 148], dtype=np.float64)\n\n# 1. Pr\u00e9diction na\u00efve (d\u00e9calage unitaire)\npred_naive = chronique_reelle[:-1]\ncible_naive = chronique_reelle[1:]\n\n# 2. Pr\u00e9diction par mod\u00e8le ARIMA (valeurs th\u00e9oriques simul\u00e9es)\npred_arima = np.array([124, 123, 129, 129, 137, 139, 144, 149, 149], dtype=np.float64)\n\n# \u00c9valuation via le SMAPE\nsmape_naif = calculer_smape_numpy(cible_naive, pred_naive)\nsmape_arima = calculer_smape_numpy(cible_naive, pred_arima)\n\ngain_relatif = ((smape_naif - smape_arima) \/ smape_naif) * 100.0\n\nprint(f\"SMAPE Mod\u00e8le Na\u00eff : {smape_naif:.3f} %\")\nprint(f\"SMAPE Mod\u00e8le ARIMA : {smape_arima:.3f} %\")\nprint(f\"Gain d'exactitude relatif : {gain_relatif:.2f} %\")\n<\/pre>\n<p>Si le mod\u00e8le ARIMA n&rsquo;induit pas une contraction significative du SMAPE par rapport au mod\u00e8le persistant na\u00eff, l&rsquo;ing\u00e9nieur en donn\u00e9es doit rejeter l&rsquo;architecture pr\u00e9dictive complexe, conform\u00e9ment au principe du rasoir d&rsquo;Ockham, \u00e9vitant ainsi le surapprentissage et l&rsquo;alourdissement de l&rsquo;infrastructure logicielle.<\/p>\n<h3>9.3 Gestion de la saisonnalit\u00e9 et des donn\u00e9es \u00e0 forte variance<\/h3>\n<p>L&rsquo;application du SMAPE sur des chroniques soumises \u00e0 une saisonnalit\u00e9 multiplicative prononc\u00e9e expose la m\u00e9trique \u00e0 un ph\u00e9nom\u00e8ne d&rsquo;ondulation induit par les creux saisonniers cycliques. Durant les phases de creux saisonnier o\u00f9 l&rsquo;amplitude scalaire de la s\u00e9rie approche de z\u00e9ro, le d\u00e9nominateur de la formule (|<em>y<\/em><sub><em>i<\/em><\/sub>| + |<em>f<\/em><sub><em>i<\/em><\/sub>|) s&rsquo;amenuise drastiquement. Il en d\u00e9coule qu&rsquo;un \u00e9cart r\u00e9siduel mineur survenant durant un plancher saisonnier p\u00e8se proportionnellement beaucoup plus lourd sur le SMAPE qu&rsquo;un \u00e9cart de m\u00eame valeur absolue intervenant au sommet d&rsquo;un pic saisonnier.<\/p>\n<p>Pour neutraliser cette sensibilit\u00e9 diff\u00e9rentielle sans alt\u00e9rer la rigueur du calcul, deux d\u00e9marches m\u00e9thodologiques doivent \u00eatre envisag\u00e9es :<\/p>\n<ul>\n<li><strong>Le filtrage pr\u00e9alable par d\u00e9composition saisonni\u00e8re :<\/strong> La s\u00e9rie temporelle brute peut \u00eatre d\u00e9compos\u00e9e en composantes de tendance, de saisonnalit\u00e9 et de r\u00e9sidus (via la m\u00e9thode STL bas\u00e9e sur la r\u00e9gression locale Loess). L&rsquo;\u00e9valuation par le SMAPE est alors appliqu\u00e9e s\u00e9par\u00e9ment sur la composante tendancielle d\u00e9saisonnalis\u00e9e, assurant une appr\u00e9ciation stable de la captation de trajectoire de fond par l&rsquo;algorithme.<\/li>\n<li><strong>L&rsquo;agr\u00e9gation temporelle harmonis\u00e9e :<\/strong> La granularit\u00e9 d&rsquo;\u00e9valuation peut \u00eatre modifi\u00e9e par sommation (passage d&rsquo;une fr\u00e9quence horaire \u00e0 une fr\u00e9quence journali\u00e8re, ou d&rsquo;une fr\u00e9quence journali\u00e8re \u00e0 une fr\u00e9quence hebdomadaire). Cette consolidation temporelle lisse les pics sporadiques de volatilit\u00e9 \u00e0 haute fr\u00e9quence et stabilise le d\u00e9nominateur du SMAPE.<\/li>\n<\/ul>\n<h2>10. Visualisation des r\u00e9sidus et interpr\u00e9tation graphique de l&rsquo;erreur sym\u00e9trique<\/h2>\n<h3>10.1 Cartographie visuelle de l&rsquo;erreur avec Matplotlib et Seaborn<\/h3>\n<p>La restitution graphique de la performance pr\u00e9dictive enrichit l&rsquo;analyse en mettant en lumi\u00e8re les disparit\u00e9s d&rsquo;ajustement que les m\u00e9triques agr\u00e9g\u00e9es masquent in\u00e9vitablement. Une bonne pratique d&rsquo;ing\u00e9nierie consiste \u00e0 cartographier de mani\u00e8re synchrone, au moyen de la biblioth\u00e8que <a rel=\"noopener noreferrer\" href=\"https:\/\/matplotlib.org\/\">Matplotlib<\/a>, l&rsquo;\u00e9volution temporelle de la s\u00e9rie observ\u00e9e superpos\u00e9e \u00e0 la s\u00e9rie pr\u00e9dictive en panneau sup\u00e9rieur, et la distribution pas \u00e0 pas du r\u00e9sidu sym\u00e9trique SMAPE unitaire en panneau inf\u00e9rieur.<\/p>\n<pre>\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\ndef tracer_analyse_smape(temps: np.ndarray, y_true: np.ndarray, y_pred: np.ndarray):\n    \"\"\"\n    G\u00e9n\u00e8re un diagnostic visuel bi-panneau : superposition des s\u00e9ries\n    et profil temporel de l'erreur sym\u00e9trique unitaire.\n    \"\"\"\n    sns.set_theme(style=\"whitegrid\")\n    \n    # Calcul des erreurs relatives sym\u00e9triques point par point (convention 200%)\n    denom = np.abs(y_true) + np.abs(y_pred)\n    termes_smape = np.where(denom == 0.0, 0.0, (200.0 * np.abs(y_true - y_pred)) \/ denom)\n    smape_global = np.mean(termes_smape)\n\n    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 8), sharex=True, \n                                   gridspec_kw={'height_ratios': [2, 1]})\n\n    # Panneau sup\u00e9rieur : Dynamique des s\u00e9ries\n    ax1.plot(temps, y_true, label=\"Observation Empirique (y)\", color=\"#1f77b4\", lw=2)\n    ax1.plot(temps, y_pred, label=\"Pr\u00e9diction Algorithmique (f)\", color=\"#ff7f0e\", linestyle=\"--\", lw=1.8)\n    ax1.set_title(f\"Diagnostic Pr\u00e9dictif - SMAPE Global : {smape_global:.2f} %\", fontsize=14, fontweight=\"bold\")\n    ax1.set_ylabel(\"Amplitude de la variable\", fontsize=12)\n    ax1.legend(loc=\"upper left\")\n\n    # Panneau inf\u00e9rieur : Erreur SMAPE unitaire\n    ax2.fill_between(temps, termes_smape, color=\"#d62728\", alpha=0.3, label=\"SMAPE Unitaire (%)\")\n    ax2.plot(temps, termes_smape, color=\"#d62728\", lw=1)\n    ax2.axhline(smape_global, color=\"black\", linestyle=\":\", label=f\"Moyenne ({smape_global:.1f} %)\")\n    ax2.set_ylabel(\"Erreur Relative (%)\", fontsize=12)\n    ax2.set_xlabel(\"Horizon Temporel\", fontsize=12)\n    ax2.set_ylim(0, 205)\n    ax2.legend(loc=\"upper right\")\n\n    plt.tight_layout()\n    plt.show()\n<\/pre>\n<p>Cette visualisation synchrone permet \u00e0 l&rsquo;analyste de rep\u00e9rer imm\u00e9diatement les p\u00e9riodes de divergence locale aigu\u00eb. Si la s\u00e9rie conna\u00eet une baisse abrupte de r\u00e9gime et que le mod\u00e8le met plusieurs pas de temps \u00e0 s&rsquo;y ajuster, l&rsquo;aire d&rsquo;erreur rouge s&rsquo;\u00e9l\u00e8vera brusquement vers la barre des 200 %, signalant sans ambigu\u00eft\u00e9 une anomalie d&rsquo;adaptation dynamique du filtre pr\u00e9dictif.<\/p>\n<h3>10.2 Analyse distributionnelle des erreurs relatives sym\u00e9triques<\/h3>\n<p>L&rsquo;agr\u00e9gation du SMAPE sous la forme d&rsquo;une simple moyenne arithm\u00e9tique souffre de la faiblesse inh\u00e9rente \u00e0 tout indicateur de tendance centrale : sa sensibilit\u00e9 \u00e0 la forme de la distribution sous-jacente des termes d&rsquo;erreur. Si la distribution des r\u00e9sidus relatifs sym\u00e9triques est hautement asym\u00e9trique, exhibant une tra\u00eene \u00e9paisse de valeurs aberrantes, la moyenne arithm\u00e9tique fournira une image biais\u00e9e de la performance usuelle du mod\u00e8le.<\/p>\n<p>Une \u00e9tude approfondie requiert le trac\u00e9 conjoint de l&rsquo;histogramme des termes unitaires du SMAPE adoss\u00e9 \u00e0 une courbe d&rsquo;estimation de la densit\u00e9 par noyau (KDE pour <em>Kernel Density Estimation<\/em>), ainsi qu&rsquo;un diagramme en bo\u00eete \u00e0 moustaches (<em>Box Plot<\/em>). Cette d\u00e9marche diagnostique permet d&rsquo;extraire la m\u00e9diane de l&rsquo;erreur sym\u00e9trique (le v\u00e9ritable <em>Median Absolute Percentage Error sym\u00e9trique<\/em>) :<\/p>\n<pre>\ndef tracer_distribution_smape(y_true: np.ndarray, y_pred: np.ndarray):\n    \"\"\"\n    Ex\u00e9cute un diagnostic distributionnel complet des r\u00e9sidus sym\u00e9triques.\n    \"\"\"\n    denom = np.abs(y_true) + np.abs(y_pred)\n    termes_smape = np.where(denom == 0.0, 0.0, (200.0 * np.abs(y_true - y_pred)) \/ denom)\n    \n    mediane_smape = np.median(termes_smape)\n    moyenne_smape = np.mean(termes_smape)\n\n    plt.figure(figsize=(10, 6))\n    sns.histplot(termes_smape, kde=True, color=\"#2ca02c\", bins=40, stat=\"density\")\n    plt.axvline(moyenne_smape, color=\"red\", linestyle=\"--\", label=f\"Moyenne : {moyenne_smape:.2f} %\")\n    plt.axvline(mediane_smape, color=\"blue\", linestyle=\"-\", label=f\"M\u00e9diane : {mediane_smape:.2f} %\")\n    \n    plt.title(\"Distribution de Densit\u00e9 de l'Erreur Relative Sym\u00e9trique\", fontsize=13, fontweight=\"bold\")\n    plt.xlabel(\"SMAPE \u00c9lementaire (%)\", fontsize=11)\n    plt.ylabel(\"Densit\u00e9 de Probabilit\u00e9\", fontsize=11)\n    plt.xlim(0, 200)\n    plt.legend()\n    plt.tight_layout()\n    plt.show()\n<\/pre>\n<p>L&rsquo;\u00e9cart observ\u00e9 entre la ligne m\u00e9diane bleue et la ligne de moyenne rouge quantifie la dissym\u00e9trie des r\u00e9sidus. Lorsque la moyenne est significativement sup\u00e9rieure \u00e0 la m\u00e9diane, le statisticien est averti que la performance globale rapport\u00e9e est p\u00e9nalis\u00e9e par une poign\u00e9e d&rsquo;\u00e9v\u00e9nements pr\u00e9dictifs extr\u00eames, tandis que la trajectoire typique du mod\u00e8le est nettement plus performante que ne le laisserait pr\u00e9sumer le chiffre global de SMAPE moyen.<\/p>\n<h2>11. Limites m\u00e9thodologiques, critiques statistiques et variantes contemporaines<\/h2>\n<h3>11.1 Le probl\u00e8me de la non-convexit\u00e9 et de l&rsquo;optimisation directe<\/h3>\n<p>L&rsquo;un des d\u00e9fis math\u00e9matiques majeurs du SMAPE r\u00e9side dans sa nature g\u00e9om\u00e9trique non-convexe et non-lisse (<em>non-smooth<\/em>). Si l&rsquo;on trace le paysage d&rsquo;erreur de la fonction de perte <em>L<\/em>(<em>f<\/em>) = 2 * |<em>y<\/em> &#8211; <em>f<\/em>| \/ (|<em>y<\/em>| + |<em>f<\/em>|) en faisant varier la pr\u00e9diction <em>f<\/em> pour une valeur cible <em>y<\/em> fix\u00e9e strictement positive (par exemple <em>y<\/em> = 1), on constate que la d\u00e9riv\u00e9e premi\u00e8re par rapport \u00e0 <em>f<\/em> n&rsquo;est pas continue au point <em>f<\/em> = <em>y<\/em> (en raison de la valeur absolue au num\u00e9rateur) ni au point <em>f<\/em> = 0 (en raison de la valeur absolue au d\u00e9nominateur).<\/p>\n<p>Au-del\u00e0 de cette non-diff\u00e9rentiabilit\u00e9 locale ponctuelle, le profil de pente s&rsquo;aplatit lorsque <em>f<\/em> devient tr\u00e8s grand : la d\u00e9riv\u00e9e seconde tend vers z\u00e9ro \u00e0 l&rsquo;infini. Cette propri\u00e9t\u00e9 implique que le gradient d&rsquo;optimisation s&rsquo;amenuise \u00e0 mesure que la pr\u00e9diction s&rsquo;\u00e9loigne de la v\u00e9rit\u00e9, ce qui est l&rsquo;exact oppos\u00e9 du comportement des fonctions quadratiques standard o\u00f9 le gradient s&rsquo;accro\u00eet proportionnellement \u00e0 l&rsquo;\u00e9loignement pour forcer le mod\u00e8le \u00e0 corriger ses d\u00e9rives. Dans le cadre de l&rsquo;optimisation par descente de gradient, l&#8217;emploi direct du SMAPE tend \u00e0 pi\u00e9ger les estimateurs dans des plateaux d&rsquo;attraction ou des minima locaux sous-optimaux.<\/p>\n<p>Pour contourner cet \u00e9cueil lors de l&rsquo;entra\u00eenement d&rsquo;algorithmes diff\u00e9rentiables, la recherche statistique pr\u00e9conise l&rsquo;utilisation de fonctions de perte de substitution (<em>surrogate loss functions<\/em>) r\u00e9gularis\u00e9es et lisses. On substitue par exemple \u00e0 la valeur absolue une approximation diff\u00e9rentiable de type pseudo-Huber ou Log-Cosh, ou l&rsquo;on optimise le mod\u00e8le au travers d&rsquo;une fonction de perte d&rsquo;entropie crois\u00e9e relative avant d&rsquo;\u00e9valuer la qualit\u00e9 finale des pr\u00e9dictions sur le jeu de test au moyen du SMAPE.<\/p>\n<h3>11.2 La critique de Hyndman et Koehler sur l&rsquo;instabilit\u00e9 num\u00e9rique<\/h3>\n<p>Dans leur publication fondamentale de 2006 intitul\u00e9e <em>Another look at measures of forecast accuracy<\/em>, les statisticiens <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1016\/j.ijforecast.2006.03.001\">Rob J. Hyndman et Anne B. Koehler<\/a> ont formul\u00e9 une critique m\u00e9thodique de l&rsquo;architecture du SMAPE, remettant en cause son statut de m\u00e9trique universelle. Le c\u0153ur de leur argumentation porte sur l&rsquo;instabilit\u00e9 asymptotique de la m\u00e9trique au voisinage imm\u00e9diat de z\u00e9ro.<\/p>\n<p>Lorsque l&rsquo;observation r\u00e9elle <em>y<\/em><sub><em>i<\/em><\/sub> et la pr\u00e9diction <em>f<\/em><sub><em>i<\/em><\/sub> sont deux quantit\u00e9s positives infinit\u00e9simales (par exemple <em>y<\/em><sub><em>i<\/em><\/sub> = 0.0001 et <em>f<\/em><sub><em>i<\/em><\/sub> = 0.0009), l&rsquo;\u00e9cart absolu est minuscule (0.0008 unit\u00e9s). Pourtant, le SMAPE \u00e9value cette d\u00e9viation microscopique \u00e0 un score \u00e9crasant de :<\/p>\n<p>SMAPE = 200% * (0.0008 \/ 0.0010) = 160.0 %<\/p>\n<p>Cette instabilit\u00e9 num\u00e9rique conf\u00e8re \u00e0 des fluctuations insignifiantes de bruit de fond sur des donn\u00e9es proches de z\u00e9ro un poids identique \u00e0 celui d&rsquo;erreurs industrielles massives. Pour cette raison, Hyndman et Koehler pr\u00e9conisent formellement le remplacement du SMAPE par le MASE (<em>Mean Absolute Scaled Error<\/em>), lequel normalise l&rsquo;erreur absolue moyenne du mod\u00e8le par la MAE intra-\u00e9chantillon obtenue par un pr\u00e9dicteur na\u00eff saisonnier unitaire, garantissant une invariance d&rsquo;\u00e9chelle exempte de singularit\u00e9 au z\u00e9ro.<\/p>\n<h3>11.3 Variantes modifi\u00e9es et pond\u00e9r\u00e9es du SMAPE<\/h3>\n<p>Pour neutraliser les travers mis en exergue par Hyndman et adapter la m\u00e9trique aux imp\u00e9ratifs \u00e9conomiques actuels, plusieurs reformulations contemporaines du SMAPE ont \u00e9t\u00e9 adopt\u00e9es dans l&rsquo;industrie. La plus r\u00e9pandue est le SMAPE &epsilon;-r\u00e9gularis\u00e9, qui ins\u00e8re un terme de lissage scalaire &epsilon; au d\u00e9nominateur :<\/p>\n<p>SMAPE<sub>&epsilon;<\/sub> = (100% \/ <em>n<\/em>) * &sum;<sub>i=1..n<\/sub> [ 2 * |y<sub>i<\/sub> &#8211; f<sub>i<\/sub>| \/ (|y<sub>i<\/sub>| + |f<sub>i<\/sub>| + &epsilon;) ]<\/p>\n<p>Le param\u00e8tre &epsilon; est conventionnellement calibr\u00e9 sur un centile bas (par exemple le premier centile) de la distribution positive des donn\u00e9es r\u00e9elles. Cette constante joue le r\u00f4le d&rsquo;un amortisseur num\u00e9rique : d\u00e8s lors que les valeurs <em>y<\/em><sub><em>i<\/em><\/sub> et <em>f<\/em><sub><em>i<\/em><\/sub> tombent en de\u00e7\u00e0 du seuil &epsilon;, le d\u00e9nominateur est domin\u00e9 par la constante, contraignant le ratio \u00e0 converger harmonieusement vers z\u00e9ro au lieu d&rsquo;osciller de mani\u00e8re erratique.<\/p>\n<p>Une autre extension industrielle majeure est le WSMAPE (<em>Weighted Symmetric Mean Absolute Percentage Error<\/em>), qui pond\u00e8re la contribution relative de chaque observation temporelle en fonction de son volume \u00e9conomique intrins\u00e8que ou de son importance strat\u00e9gique <em>w<\/em><sub><em>i<\/em><\/sub> :<\/p>\n<p>WSMAPE = &sum; [ w<sub>i<\/sub> * (2 * |y<sub>i<\/sub> &#8211; f<sub>i<\/sub>| \/ (|y<sub>i<\/sub>| + |f<sub>i<\/sub>|)) ] \/ &sum; w<sub>i<\/sub><\/p>\n<p>Cette formulation hybride permet de conserver les vertus de bornage sym\u00e9trique du SMAPE tout en s&rsquo;assurant que les r\u00e9f\u00e9rences prioritaires au sein d&rsquo;un portefeuille industriel participent proportionnellement \u00e0 leur enjeu d\u00e9cisionnel dans le score d&rsquo;\u00e9valuation unifi\u00e9.<\/p>\n<h2>12. Synth\u00e8se des meilleures pratiques et recommandations pour l&rsquo;analyse pr\u00e9dictive en Python<\/h2>\n<h3>12.1 Arbre de d\u00e9cision pour le choix de la m\u00e9trique pr\u00e9dictive<\/h3>\n<p>Afin de guider m\u00e9thodiquement l&rsquo;ing\u00e9nieur de donn\u00e9es dans le labyrinthe des crit\u00e8res de performance en r\u00e9gression, l&rsquo;arbre de d\u00e9cision fonctionnel suivant formalise les conditions d&rsquo;arbitrage r\u00e9gissant la s\u00e9lection prioritaire du SMAPE ou son \u00e9viction :<\/p>\n<ul>\n<li><strong>Cas 1 : Les s\u00e9ries comportent-elles des valeurs strictement n\u00e9gatives ?<\/strong>\n<ul>\n<li><em>Oui :<\/em> Proscrire imp\u00e9rativement le SMAPE et le MAPE. Utiliser la MAE, la RMSE ou le MASE.<\/li>\n<li><em>Non :<\/em> Passer au test d&rsquo;\u00e9chelle suivant.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cas 2 : L&rsquo;\u00e9valuation porte-t-elle sur des s\u00e9ries d&rsquo;\u00e9chelles de grandeur h\u00e9t\u00e9rog\u00e8nes interdisant l&rsquo;usage d&rsquo;unit\u00e9s physiques brutes ?<\/strong>\n<ul>\n<li><em>Non (\u00e9chelle homog\u00e8ne) :<\/em> Privil\u00e9gier la MAE pour une interpr\u00e9tabilit\u00e9 directe, ou la RMSE pour p\u00e9naliser les grands \u00e9carts r\u00e9siduels.<\/li>\n<li><em>Oui (\u00e9chelles disparates) :<\/em> Une m\u00e9trique relative est requise. Passer au test du z\u00e9ro.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cas 3 : La cible contient-elle des z\u00e9ros intermittents stricts (demande intermittente, pannes sporadiques) ?<\/strong>\n<ul>\n<li><em>Oui, de mani\u00e8re structurelle :<\/em> Le MAPE est impossible (divisions par z\u00e9ro). Le SMAPE est utilisable sous r\u00e9serve de neutraliser formellement l&rsquo;ind\u00e9termination 0\/0, mais le MASE demeure la recommandation acad\u00e9mique la plus robuste.<\/li>\n<li><em>Non (donn\u00e9es strictement positives) :<\/em> Le SMAPE constitue un excellent choix, apportant une immunit\u00e9 \u00e9prouv\u00e9e contre le biais de sous-estimation inh\u00e9rent au MAPE.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>L&rsquo;application rigoureuse de cette heuristique pr\u00e9munit les \u00e9quipes de mod\u00e9lisation contre les biais d&rsquo;\u00e9valuation et garantit l&rsquo;alignement des crit\u00e8res statistiques avec les contraintes d&rsquo;affaires du projet pr\u00e9dictif.<\/p>\n<h3>12.2 Checklist de rigueur m\u00e9thodologique pour les praticiens<\/h3>\n<p>Avant d&rsquo;ent\u00e9riner le calcul du SMAPE au sein d&rsquo;un pipeline d&rsquo;\u00e9valuation en production, tout praticien de l&rsquo;analyse pr\u00e9dictive en Python devrait valider la conformit\u00e9 de ses scripts au regard de la liste de contr\u00f4les m\u00e9thodologiques et logiciels suivante :<\/p>\n<ul>\n<li><strong>Documentation explicite de l&rsquo;\u00e9chelle de factorisation :<\/strong> Avez-vous pr\u00e9cis\u00e9 dans vos rapports et la documentation technique de votre fonction si le SMAPE rapport\u00e9 \u00e9volue dans la convention born\u00e9e \u00e0 100 % ou dans la convention canonique de Makridakis plafonn\u00e9e \u00e0 200 % ?<\/li>\n<li><strong>Contr\u00f4le de positivit\u00e9 des donn\u00e9es d&rsquo;entr\u00e9e :<\/strong> Avez-vous inclus une assertion de s\u00e9curit\u00e9 (<code>assert np.all(y_true &gt;= 0)<\/code>) emp\u00eachant l&rsquo;\u00e9valuation accidentelle sur des r\u00e9sidus issus de domaines n\u00e9gatifs ?<\/li>\n<li><strong>Alignement dimensionnel des tableaux :<\/strong> Vos routines d&rsquo;entr\u00e9e appliquent-elles un aplatissement explicite (<code>ravel()<\/code>) pour neutraliser tout risque de diffusion crois\u00e9e matricielle non d\u00e9sir\u00e9e ?<\/li>\n<li><strong>Banc de tests unitaires (TDD) :<\/strong> Votre suite de tests unitaires (sous <code>pytest<\/code> ou <code>unittest<\/code>) comprend-elle express\u00e9ment les sc\u00e9narios de limites : s\u00e9ries vides, correspondances parfaites \u00e0 z\u00e9ro (<em>y<\/em>=0, <em>f<\/em>=0), divergences infinies et stabilit\u00e9 des types scalaires retourn\u00e9s ?<\/li>\n<li><strong>S\u00e9lection du moteur d&rsquo;ex\u00e9cution adapt\u00e9 au volume :<\/strong> Le choix de la technologie (Python pur pour la portabilit\u00e9 sans d\u00e9pendance, NumPy pour l&rsquo;int\u00e9gration standard, Numba pour le traitement haute performance de donn\u00e9es massives) est-il calibr\u00e9 en fonction des contraintes de latence et de m\u00e9moire vive allou\u00e9e ?<\/li>\n<\/ul>\n<p>En observant scrupuleusement ces directives fondamentales, les analystes de donn\u00e9es et chercheurs s&rsquo;assurent que leurs mesures pr\u00e9dictives constituent des indicateurs stables, reproductibles et scientifiquement inattaquables, aptes \u00e0 soutenir la prise de d\u00e9cision strat\u00e9gique \u00e0 tous les niveaux de l&rsquo;entreprise.<\/p>\n<h2>R\u00e9f\u00e9rences<\/h2>\n<p>Armstrong, J. S. (1985). <em>Long-range forecasting: from crystal ball to computer<\/em> (2e \u00e9d.). John Wiley &amp; Sons.<\/p>\n<p>Chen, Z., Yang, Y., &amp; Zhou, Z. (2017). Extreme gradient boosting with symmetric percentage error for demand forecasting. <em>Computers &amp; Industrial Engineering<\/em>, 112, 458-467. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1016\/j.cie.2017.08.012\">https:\/\/doi.org\/10.1016\/j.cie.2017.08.012<\/a><\/p>\n<p>Hyndman, R. J., &amp; Athanasopoulos, G. (2021). <em>Forecasting: principles and practice<\/em> (3e \u00e9d.). OTexts. <a rel=\"noopener noreferrer\" href=\"https:\/\/otexts.com\/fpp3\/\">https:\/\/otexts.com\/fpp3\/<\/a><\/p>\n<p>Hyndman, R. J., &amp; Koehler, A. B. (2006). Another look at measures of forecast accuracy. <em>International Journal of Forecasting<\/em>, 22(4), 679-688. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1016\/j.ijforecast.2006.03.001\">https:\/\/doi.org\/10.1016\/j.ijforecast.2006.03.001<\/a><\/p>\n<p>Makridakis, S. (1993). Accuracy measures: theoretical and practical concerns. <em>International Journal of Forecasting<\/em>, 9(4), 527-529. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1016\/0169-2070(93)90079-3\">https:\/\/doi.org\/10.1016\/0169-2070(93)90079-3<\/a><\/p>\n<p>Makridakis, S., &amp; Hibon, M. (2000). The M3-Competition: results, conclusions and implications. <em>International Journal of Forecasting<\/em>, 16(4), 451-476. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1016\/S0169-2070(00)00057-1\">https:\/\/doi.org\/10.1016\/S0169-2070(00)00057-1<\/a><\/p>\n<p>Makridakis, S., Spiliotis, E., &amp; Assimakopoulos, V. (2020). The M4 Competition: Results, findings, problems and ways forward. <em>International Journal of Forecasting<\/em>, 36(1), 54-74. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1016\/j.ijforecast.2019.05.007\">https:\/\/doi.org\/10.1016\/j.ijforecast.2019.05.007<\/a><\/p>\n<p>Pedregosa, F., Varoquaux, G., Gramfort, A., Michel, V., Thirion, B., Grisel, O., Blondel, M., Prettenhofer, P., Weiss, R., Dubourg, V., Vanderplas, J., Passos, A., Cournapeau, D., Brucher, M., Perrot, M., &amp; Duchesnay, E. (2011). Scikit-learn: Machine learning in Python. <em>Journal of Machine Learning Research<\/em>, 12, 2825-2830. <a rel=\"noopener noreferrer\" href=\"https:\/\/jmlr.csail.mit.edu\/papers\/v12\/pedregosa11a.html\">https:\/\/jmlr.csail.mit.edu\/papers\/v12\/pedregosa11a.html<\/a><\/p>\n<p>Van der Walt, S., Colbert, S. C., &amp; Varoquaux, G. (2011). The NumPy array: a structure for efficient numerical computation. <em>Computing in Science &amp; Engineering<\/em>, 13(2), 22-30. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1109\/MCSE.2011.37\">https:\/\/doi.org\/10.1109\/MCSE.2011.37<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Guide acad\u00e9mique complet pour calculer l&rsquo;erreur absolue m\u00e9diane sym\u00e9trique en pourcentage (SMAPE) en Python avec NumPy, gestion des biais et cas limites.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","template":"","categories":[2465,2464],"tags":[6270,2960,2468,1911,9443],"class_list":["post-8303","psych_stat","type-psych_stat","status-publish","hentry","category-programmation-python","category-science-des-donnees","tag-evaluation-de-modeles","tag-numpy","tag-python","tag-series-temporelles","tag-smape"],"_links":{"self":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/8303","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=8303"}],"version-history":[{"count":0,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/8303\/revisions"}],"wp:attachment":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8303"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}