{"id":4730,"date":"2026-09-11T12:39:25","date_gmt":"2026-09-11T12:39:25","guid":{"rendered":"http:\/\/french.arabpsychology.com\/statistics\/comment-trouver-indice-valeur-tableau-numpy-exemples\/"},"modified":"2026-09-11T12:39:25","modified_gmt":"2026-09-11T12:39:25","slug":"comment-trouver-indice-valeur-tableau-numpy-exemples","status":"publish","type":"psych_stat","link":"https:\/\/french.arabpsychology.com\/?psych_stat=comment-trouver-indice-valeur-tableau-numpy-exemples","title":{"rendered":"Comment trouver l&rsquo;indice d&rsquo;une valeur dans un tableau NumPy (avec exemples)"},"content":{"rendered":"<p>Dans le paysage contemporain du calcul scientifique et de l&rsquo;ing\u00e9nierie des donn\u00e9es, la biblioth\u00e8que <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/\">NumPy<\/a> constitue le socle fondamental sur lequel repose l&rsquo;ensemble de l&rsquo;\u00e9cosyst\u00e8me analytique du langage Python. Con\u00e7ue pour pallier les lenteurs inh\u00e9rentes \u00e0 la nature interpr\u00e9t\u00e9e et au typage dynamique de Python, cette biblioth\u00e8que fournit une structure de donn\u00e9es hautement optimis\u00e9e : le tableau multidimensionnel homog\u00e8ne, formalis\u00e9 par la classe <code>ndarray<\/code>. Au c\u0153ur des flux de travail en traitement du signal, en apprentissage automatique et en mod\u00e9lisation physique, une op\u00e9ration \u00e9l\u00e9mentaire revient avec une fr\u00e9quence syst\u00e9matique : l&rsquo;identification de la position spatiale ou s\u00e9quentielle \u2014 c&rsquo;est-\u00e0-dire l&rsquo;indice \u2014 occup\u00e9e par une valeur scalaire ou un motif de valeurs au sein d&rsquo;une structure de tenseurs. Si cette op\u00e9ration semble conceptuellement triviale, son impl\u00e9mentation efficace requiert une compr\u00e9hension intime des m\u00e9canismes de bas niveau qui r\u00e9gissent l&rsquo;allocation de la m\u00e9moire, les strat\u00e9gies d&rsquo;indexation vectorielle et les compromis de complexit\u00e9 algorithmique.<\/p>\n<p>La recherche de positions au sein d&rsquo;un ensemble de donn\u00e9es vectoris\u00e9 soul\u00e8ve imm\u00e9diatement des interrogations d&rsquo;ordre computationnel. Contrairement aux listes natives de Python qui s&rsquo;appuient sur des pointeurs d&rsquo;objets h\u00e9t\u00e9rog\u00e8nes dispers\u00e9s dans la m\u00e9moire vive, les tableaux NumPy exploitent des zones de m\u00e9moire contigu\u00ebs, g\u00e9r\u00e9es au plus pr\u00e8s du silicium par des routines \u00e9crites en langage C et Fortran. Localiser un \u00e9l\u00e9ment dans ce contexte ne consiste pas simplement \u00e0 it\u00e9rer s\u00e9quentiellement le long d&rsquo;une cha\u00eene de r\u00e9f\u00e9rences, mais implique de tirer parti d&rsquo;instructions vectorielles avanc\u00e9es de type SIMD (<em>Single Instruction, Multiple Data<\/em>), de manipuler des masques bool\u00e9ens \u00e9ph\u00e9m\u00e8res et de g\u00e9rer des dimensions multiples selon des ordres de disposition spatiale stricts. Les disparit\u00e9s de performance entre une approche na\u00efve fond\u00e9e sur une boucle it\u00e9rative en pur Python et une approche vectoris\u00e9e native peuvent atteindre plusieurs ordres de grandeur, transformant un goulot d&rsquo;\u00e9tranglement de plusieurs minutes en une op\u00e9ration r\u00e9solue en une fraction de milliseconde.<\/p>\n<p>Ce guide exhaustif propose une exploration syst\u00e9matique, rigoureuse et approfondie des m\u00e9thodologies permettant d&rsquo;extraire les coordonn\u00e9es scalaires et multidimensionnelles de valeurs cibles au sein de tableaux NumPy. Nous examinerons successivement les fondements th\u00e9oriques de l&rsquo;adressage m\u00e9moire, les fonctions canoniques d&rsquo;exploration exhaustive comme <code>np.where()<\/code>, les techniques d&rsquo;arr\u00eat pr\u00e9coce pour isoler la premi\u00e8re occurrence, ainsi que les strat\u00e9gies logarithmiques ultra-performantes bas\u00e9es sur le tri indirect et la recherche dichotomique via <code>np.searchsorted()<\/code>. Les d\u00e9fis inh\u00e9rents \u00e0 l&rsquo;arithm\u00e9tique \u00e0 virgule flottante r\u00e9gie par la norme IEEE 754, la recherche multidimensionnelle par d\u00e9lin\u00e9arisation d&rsquo;indices et la gestion des cas limites en environnement de production feront l&rsquo;objet d&rsquo;analyses math\u00e9matiques et algorithmiques d\u00e9taill\u00e9es, \u00e9tay\u00e9es par des exemples concrets et des protocoles de m\u00e9trologie informatique.<\/p>\n<h2>1. Fondements th\u00e9oriques de l&rsquo;indexation vectorielle dans NumPy<\/h2>\n<h3>1.1 Architecture interne des tableaux ndarray et adressage m\u00e9moire<\/h3>\n<p>Pour appr\u00e9hender la m\u00e9canique d&rsquo;indexation dans NumPy, il est indispensable de diss\u00e9quer la constitution interne de l&rsquo;objet <code>ndarray<\/code>. Contrairement \u00e0 une liste standard de Python, qui est essentiellement un tableau dynamique de pointeurs vers des objets arbitraires encapsul\u00e9s dans des structures <code>PyObject<\/code>, le tableau NumPy est scind\u00e9 en deux entit\u00e9s distinctes : un en-t\u00eate d&rsquo;objet Python l\u00e9ger et un bloc de donn\u00e9es brutes contigu en m\u00e9moire vive. L&rsquo;en-t\u00eate h\u00e9berge les m\u00e9tadonn\u00e9es structurelles, notamment le type de donn\u00e9es atomique (<code>dtype<\/code>), le nombre de dimensions (<code>ndim<\/code>), la forme g\u00e9om\u00e9trique du tableau (<code>shape<\/code>) et, surtout, le vecteur de pas d&rsquo;avancement m\u00e9moire, d\u00e9sign\u00e9 sous le terme de <em>strides<\/em>.<\/p>\n<p>Les <em>strides<\/em> d\u00e9finissent le nombre pr\u00e9cis d&rsquo;octets que le processeur doit franchir dans la m\u00e9moire physique pour passer d&rsquo;un \u00e9l\u00e9ment \u00e0 l&rsquo;\u00e9l\u00e9ment suivant le long de chaque axe dimensionnel. Dans une disposition standard conforme \u00e0 l&rsquo;ordonnancement du langage C (ordonnancement row-major), le dernier axe varie le plus rapidement en m\u00e9moire. L&rsquo;adresse m\u00e9moire absolue d&rsquo;un \u00e9l\u00e9ment rep\u00e9r\u00e9 par le tuple d&rsquo;indices multidimensionnels (i<sub>0<\/sub>, i<sub>1<\/sub>, &#8230;, i<sub>n-1<\/sub>) est calcul\u00e9e de mani\u00e8re d\u00e9terministe via une combinaison lin\u00e9aire directe : l&rsquo;adresse de base du pointeur de donn\u00e9es est incr\u00e9ment\u00e9e de la somme des produits de chaque indice dimensionnel par son pas de m\u00e9moire respectif. Cette r\u00e9gularit\u00e9 arithm\u00e9tique permet au contr\u00f4leur de m\u00e9moire d&rsquo;anticiper les acc\u00e8s gr\u00e2ce aux m\u00e9canismes mat\u00e9riels de pr\u00e9chargement (<em>hardware prefetching<\/em>).<\/p>\n<p>La contigu\u00eft\u00e9 m\u00e9moire joue un r\u00f4le d\u00e9terminant dans l&rsquo;efficacit\u00e9 des algorithmes de scrutation d&rsquo;indices. Lorsque les \u00e9l\u00e9ments successifs d&rsquo;un tableau sont stock\u00e9s dans des adresses physiques adjacentes, le balayage s\u00e9quentiel maximise l&rsquo;exploitation de la hi\u00e9rarchie des m\u00e9moires caches du microprocesseur (L1, L2 et L3). Chaque transfert depuis la m\u00e9moire vive charge une ligne de cache enti\u00e8re (typiquement 64 octets), rendant les lectures ult\u00e9rieures des donn\u00e9es voisines quasi instantan\u00e9es. D\u00e8s lors que l&rsquo;on rompt cette contigu\u00eft\u00e9 \u2014 par exemple suite \u00e0 des op\u00e9rations de transposition ou de d\u00e9coupage non unitaire \u2014, les pas de m\u00e9moire cessent d&rsquo;\u00eatre optimaux, et le parcours n\u00e9cessaire \u00e0 la d\u00e9tection d&rsquo;une valeur cible subit des p\u00e9nalit\u00e9s de latence substantielles caus\u00e9es par des d\u00e9fauts de cache (<em>cache misses<\/em>).<\/p>\n<h3>1.2 Concepts fondamentaux de l&rsquo;indexation et masquage bool\u00e9en<\/h3>\n<p>Dans l&rsquo;architecture conceptuelle de NumPy, il existe une dichotomie formelle entre l&rsquo;indexation simple par tranches (<em>slicing<\/em>), qui produit invariablement une vue (<em>view<\/em>) sans duplication de donn\u00e9es, et l&rsquo;indexation avanc\u00e9e (<em>fancy indexing<\/em>), qui s&rsquo;active d\u00e8s lors que des tableaux d&rsquo;entiers ou des masques bool\u00e9ens sont mobilis\u00e9s pour filtrer les donn\u00e9es. L&rsquo;identification de positions requiert quasi syst\u00e9matiquement le passage par cette seconde cat\u00e9gorie d&rsquo;op\u00e9rations. Lorsqu&rsquo;une condition d&rsquo;\u00e9galit\u00e9 scalaire est \u00e9valu\u00e9e sur un tableau, NumPy ex\u00e9cute une boucle compil\u00e9e au niveau machine qui compare chaque \u00e9l\u00e9ment atomique \u00e0 la valeur de r\u00e9f\u00e9rence, g\u00e9n\u00e9rant en sortie un nouveau tableau de bool\u00e9ens de m\u00eame dimensionnalit\u00e9 que la structure d&rsquo;origine.<\/p>\n<p>Ce masque bool\u00e9en repr\u00e9sente la mat\u00e9rialisation logique de la v\u00e9rit\u00e9 du pr\u00e9dicat sur l&rsquo;ensemble du domaine d&rsquo;\u00e9chantillonnage. Chaque bit ou octet (dans NumPy, le type <code>bool_<\/code> est repr\u00e9sent\u00e9 par un octet entier pour des raisons d&rsquo;alignement m\u00e9moire) encode la concordance ou la discordance avec la cible. L&rsquo;\u00e9valuation vectorielle sous-jacente exploite les registres d&rsquo;extension vectorielle des processeurs contemporains, tels que les jeux d&rsquo;instructions AVX-2 ou AVX-512 d&rsquo;Intel et AMD, ou Neon sur les architectures ARM. Cette parall\u00e9lisation \u00e0 grain fin permet de traiter simultan\u00e9ment plusieurs dizaines d&rsquo;octets en un nombre restreint de cycles d&rsquo;horloge, conf\u00e9rant \u00e0 la g\u00e9n\u00e9ration de masques une v\u00e9locit\u00e9 sans commune mesure avec une it\u00e9ration conventionnelle en boucle <code>for<\/code> Python.<\/p>\n<p>Cependant, ce formalisme vectoriel comporte un co\u00fbt intrins\u00e8que souvent sous-estim\u00e9 : l&rsquo;allocation dynamique interm\u00e9diaire. Construire un masque logique pour un tableau contenant plusieurs centaines de millions d&rsquo;\u00e9l\u00e9ments exige la r\u00e9servation imm\u00e9diate d&rsquo;une quantit\u00e9 \u00e9quivalente de m\u00e9moire vive pour h\u00e9berger le tableau de r\u00e9sultats bool\u00e9ens, avant m\u00eame que la moindre position enti\u00e8re ne soit extraite. Ainsi, le co\u00fbt algorithmique se compose non seulement du temps de traitement requis pour l&rsquo;\u00e9valuation logique \u00e9l\u00e9mentaire (en complexit\u00e9 temporelle rigoureusement lin\u00e9aire), mais \u00e9galement du temps de gestion de la m\u00e9moire syst\u00e8me par le ramasse-miettes et le gestionnaire de tas (<em>heap<\/em>), ce qui peut s&rsquo;av\u00e9rer p\u00e9nalisant sur des architectures aux ressources contraintes.<\/p>\n<h3>1.3 Typologie des probl\u00e8mes de recherche de position scalaire<\/h3>\n<p>L&rsquo;op\u00e9ration consistant \u00e0 \u00ab trouver un indice \u00bb recouvre en r\u00e9alit\u00e9 une pluralit\u00e9 de cas d&rsquo;usage math\u00e9matiques et informatiques aux contraintes hautement divergentes. Il convient d&rsquo;\u00e9tablir une classification rigoureuse de ces probl\u00e8mes afin de s\u00e9lectionner les topologies algorithmiques adapt\u00e9es. Le premier axe de diff\u00e9renciation concerne l&rsquo;exhaustivit\u00e9 de la recherche : s&rsquo;agit-il d&rsquo;identifier l&rsquo;int\u00e9gralit\u00e9 des positions o\u00f9 la valeur cible est dupliqu\u00e9e, ou cherche-t-on exclusivement l&rsquo;indice d&rsquo;ancrage initial, c&rsquo;est-\u00e0-dire la premi\u00e8re occurrence dans l&rsquo;ordre de s\u00e9rialisation du tableau ? Cette nuance est fondamentale, car une recherche exhaustive impose n\u00e9cessairement un balayage complet de l&rsquo;espace de donn\u00e9es, alors qu&rsquo;une recherche unitaire autorise en th\u00e9orie un arr\u00eat pr\u00e9coce (<em>short-circuiting<\/em>) d\u00e8s la premi\u00e8re concordance \u00e9tablie.<\/p>\n<p>Le second axe typologique r\u00e9side dans la nature alg\u00e9brique des donn\u00e9es sous-jacentes. La recherche au sein de domaines discrets repr\u00e9sent\u00e9s par des entiers sign\u00e9s ou non sign\u00e9s (<code>int32<\/code>, <code>int64<\/code>, <code>uint8<\/code>) ou des cha\u00eenes de caract\u00e8res ob\u00e9it aux lois de l&rsquo;\u00e9galit\u00e9 stricte au sens binaire. \u00c0 l&rsquo;inverse, l&rsquo;analyse de domaines continus via des nombres \u00e0 virgule flottante (<code>float32<\/code>, <code>float64<\/code>) se heurte imm\u00e9diatement aux limites de la pr\u00e9cision finie prescrite par la <a rel=\"noopener noreferrer\" href=\"https:\/\/standards.ieee.org\/ieee\/754\/6269\/\">norme IEEE 754<\/a>. Dans ce contexte, la notion d&rsquo;indice d&rsquo;une valeur exacte perd tout sens physique et math\u00e9matique en raison des erreurs d&rsquo;arrondi accumul\u00e9es lors des calculs num\u00e9riques pr\u00e9liminaires. Le probl\u00e8me se transmute alors en une recherche d&rsquo;indices par proximit\u00e9 m\u00e9trique ou par inclusion dans un intervalle de tol\u00e9rance param\u00e9trable.<\/p>\n<p>Enfin, la dimensionnalit\u00e9 et l&rsquo;\u00e9chelle volum\u00e9trique dictent des contraintes d&rsquo;extensibilit\u00e9 sp\u00e9cifiques. Localiser une valeur dans un vecteur unidimensionnel de quelques milliers d&rsquo;\u00e9l\u00e9ments rel\u00e8ve d&rsquo;une op\u00e9ration quasi instantan\u00e9e pour laquelle n&rsquo;importe quelle m\u00e9thode vectoris\u00e9e s&rsquo;av\u00e8re satisfaisante. En revanche, localiser simultan\u00e9ment plusieurs milliers de valeurs cibles distinctes au sein d&rsquo;un tenseur quadridimensionnel mod\u00e9lisant des champs spatio-temporels de plusieurs dizaines de gigaoctets requiert d&rsquo;abandonner le balayage exhaustif lin\u00e9aire au profit de structures de donn\u00e9es index\u00e9es, de partitions tri\u00e9es et de recherches dichotomiques compos\u00e9es, sous peine d&rsquo;induire une saturation dramatique du processeur et des canaux de m\u00e9moire.<\/p>\n<h2>2. M\u00e9thode 1 : Recherche exhaustive de toutes les occurrences avec np.where()<\/h2>\n<h3>2.1 M\u00e9canisme et syntaxe formelle de np.where() pour un pr\u00e9dicat d&rsquo;\u00e9galit\u00e9<\/h3>\n<p>La fonction <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.where.html\"><code>np.where()<\/code><\/a> repr\u00e9sente l&rsquo;instrument canonique le plus universellement d\u00e9ploy\u00e9 au sein de l&rsquo;\u00e9cosyst\u00e8me NumPy pour extraire les positions r\u00e9pondant \u00e0 un crit\u00e8re logique. Lorsqu&rsquo;elle est invoqu\u00e9e avec un argument unique repr\u00e9sentant une condition bool\u00e9enne, sa s\u00e9mantique est strictement \u00e9quivalente \u00e0 celle de la fonction <code>np.nonzero()<\/code>. Le m\u00e9canisme op\u00e9rationnel repose sur l&rsquo;\u00e9valuation pr\u00e9alable d&rsquo;une expression relationnelle vectoris\u00e9e, typiquement <code>x == valeur_cible<\/code>. Cette comparaison produit un tableau \u00e9ph\u00e9m\u00e8re de bool\u00e9ens qui est imm\u00e9diatement transmis au moteur de <code>np.where()<\/code>, lequel inspecte chaque bit pour recenser la position des \u00e9l\u00e9ments \u00e9valu\u00e9s \u00e0 la valeur logique vraie.<\/p>\n<p>La signature formelle de retour de <code>np.where(condition)<\/code> est invariable et m\u00e9rite une attention architecturale particuli\u00e8re : la fonction ne renvoie pas directement un tableau simple d&rsquo;indices entiers, mais un <em>tuple<\/em> de tableaux NumPy unidimensionnels. Le nombre de tableaux contenus dans ce tuple est rigoureusement identique au nombre de dimensions (<code>ndim<\/code>) du tableau d&rsquo;entr\u00e9e examin\u00e9. Par cons\u00e9quent, si l&rsquo;on soumet un vecteur \u00e0 une dimension, le tuple r\u00e9sultant contiendra un unique tableau regroupant les indices lin\u00e9aires des \u00e9l\u00e9ments satisfaisant la condition. Si le tableau d&rsquo;entr\u00e9e est une matrice bidimensionnelle, le tuple h\u00e9bergera deux tableaux : le premier recensant les indices de lignes et le second recensant les indices de colonnes pour chaque \u00e9l\u00e9ment valid\u00e9.<\/p>\n<p>Cette structure de retour sous forme de tuple d\u00e9concerte fr\u00e9quemment les praticiens n\u00e9ophytes, mais elle proc\u00e8de d&rsquo;une coh\u00e9rence syst\u00e9mique profonde au sein de NumPy : ce tuple peut \u00eatre directement r\u00e9inject\u00e9 comme indice d&rsquo;acc\u00e8s dans le tableau original pour r\u00e9aliser une op\u00e9ration d&rsquo;indexation avanc\u00e9e, extrayant ainsi les valeurs s\u00e9lectionn\u00e9es. Pour isoler le tableau concret des indices dans le cas d&rsquo;un vecteur \u00e0 une dimension, il est imp\u00e9ratif d&rsquo;acc\u00e9der au premier \u00e9l\u00e9ment du tuple via l&rsquo;op\u00e9rateur de d\u00e9r\u00e9f\u00e9rencement d&rsquo;indice standard <code>[0]<\/code>. L&rsquo;interpr\u00e9tation rigoureuse de cette interface conditionne l&rsquo;\u00e9criture d&rsquo;un code exempt d&rsquo;anomalies de dimensionnalit\u00e9.<\/p>\n<h3>2.2 Exemple pratique pas \u00e0 pas sur un tableau 1D avec valeurs dupliqu\u00e9es<\/h3>\n<p>Afin de mat\u00e9rialiser ce comportement sur un cas concret, consid\u00e9rons l&rsquo;initialisation d&rsquo;un vecteur synth\u00e9tique unidimensionnel contenant des entiers parmi lesquels certaines valeurs se r\u00e9p\u00e8tent de fa\u00e7on stochastique. Imaginons la configuration logicielle suivante :<\/p>\n<p>Soit un tableau <code>x = np.array([14, 8, 23, 8, 42, 8, 99, 105, 8, 12])<\/code>. L&rsquo;objectif analytique consiste \u00e0 recenser l&rsquo;ensemble des coordonn\u00e9es spatiales occup\u00e9es par la valeur scalaire 8. L&rsquo;ex\u00e9cution de la directive vectorielle s&rsquo;articule comme suit :<\/p>\n<p><code>resultat = np.where(x == 8)<\/code><\/p>\n<p>Lors de cette invocation, NumPy proc\u00e8de d&rsquo;abord \u00e0 l&rsquo;\u00e9valuation \u00e9l\u00e9ment par \u00e9l\u00e9ment de l&rsquo;expression <code>x == 8<\/code>, engendrant le vecteur bool\u00e9en sous-jacent <code>[False, True, False, True, False, True, False, False, True, False]<\/code>. Dans un second temps, le noyau interne en langage C alloue un nouveau tableau d&rsquo;entiers syst\u00e8me (g\u00e9n\u00e9ralement du type <code>np.int64<\/code> sur les architectures 64 bits modernes) dont la capacit\u00e9 correspond au nombre exact d&rsquo;occurrences positives d\u00e9tect\u00e9es. Il y transcrit les positions s\u00e9quentielles o\u00f9 le pr\u00e9dicat a \u00e9t\u00e9 valid\u00e9. La variable <code>resultat<\/code> encapsule alors le tuple suivant :<\/p>\n<p><code>(array([1, 3, 5, 8], dtype=int64),)<\/code><\/p>\n<p>Pour exploiter programmatiquement ces coordonn\u00e9es, le d\u00e9veloppeur doit extraire le tableau sous-jacent en appliquant la d\u00e9structuration syntaxique appropri\u00e9e :<\/p>\n<p><code>indices = resultat[0]<\/code><\/p>\n<p>L&rsquo;affichage formel de cette variable r\u00e9v\u00e8le le vecteur <code>array([1, 3, 5, 8])<\/code>. Ce vecteur confirme avec exactitude que la valeur 8 est pr\u00e9sente aux positions d&rsquo;indice 1, 3, 5 et 8 du tableau d&rsquo;origine. Cette s\u00e9quence d&rsquo;op\u00e9rations illustre avec limpidit\u00e9 la dissociation fonctionnelle op\u00e9r\u00e9e par NumPy entre l&rsquo;\u00e9valuation de la condition d&rsquo;appartenance logique et l&rsquo;extraction g\u00e9om\u00e9trique de ses coordonn\u00e9es dans l&rsquo;espace m\u00e9moire du vecteur.<\/p>\n<h3>2.3 Analyse de la complexit\u00e9 temporelle et spatiale de l&rsquo;approche<\/h3>\n<p>L&#8217;emploi de la fonction <code>np.where()<\/code> pour localiser des occurrences implique des caract\u00e9ristiques algorithmiques pr\u00e9cises qu&rsquo;il convient de formaliser \u00e0 travers la notation du grand O. Sur le plan de la complexit\u00e9 temporelle, le m\u00e9canisme sous-jacent repose imp\u00e9rativement sur un balayage lin\u00e9aire exhaustif de la totalit\u00e9 du tableau d&rsquo;entr\u00e9e. Si l&rsquo;on d\u00e9signe par <em>n<\/em> le nombre total d&rsquo;\u00e9l\u00e9ments r\u00e9sidant dans la structure <code>ndarray<\/code>, le nombre d&rsquo;op\u00e9rations de comparaison \u00e9l\u00e9mentaires ex\u00e9cut\u00e9es est exactement \u00e9gal \u00e0 <em>n<\/em>. D\u00e8s lors, la complexit\u00e9 asymptotique temporelle est strictement lin\u00e9aire, soit O(n). Aucun m\u00e9canisme d&rsquo;\u00e9lagage n&rsquo;\u00e9tant impl\u00e9ment\u00e9 dans <code>np.where()<\/code>, la charge de calcul demeure rigoureusement identique, que la valeur recherch\u00e9e se situe au premier indice ou qu&rsquo;elle soit totalement absente de l&rsquo;ensemble de donn\u00e9es.<\/p>\n<p>Sur le plan de la complexit\u00e9 spatiale, l&#8217;empreinte m\u00e9moire de cette approche est double et requiert une quantification minutieuse. Premi\u00e8rement, l&rsquo;\u00e9valuation de l&rsquo;op\u00e9rateur d&rsquo;\u00e9galit\u00e9 relationnelle g\u00e9n\u00e8re un tableau bool\u00e9en temporaire occupant <em>n<\/em> octets en m\u00e9moire vive. Deuxi\u00e8mement, la fonction <code>np.where()<\/code> doit instancier un vecteur d&rsquo;indices dont la taille est proportionnelle au nombre d&rsquo;occurrences positives trouv\u00e9es, not\u00e9 <em>k<\/em> (avec 0 &le; <em>k<\/em> &le; <em>n<\/em>). Le type de ces indices \u00e9tant g\u00e9n\u00e9ralement un entier 64 bits (8 octets), l&rsquo;allocation finale mobilise 8 &times; <em>k<\/em> octets. Par cons\u00e9quent, la complexit\u00e9 spatiale auxiliaire de la proc\u00e9dure est de l&rsquo;ordre de O(n) dans le pire des cas, en raison du masque interm\u00e9diaire.<\/p>\n<p>Cette consommation d&rsquo;espace m\u00e9moire engendre des r\u00e9percussions tangibles sur l&rsquo;exploitation des registres de cache du microprocesseur. Lorsque la taille <em>n<\/em> du tableau d\u00e9passe la capacit\u00e9 volum\u00e9trique du cache de niveau 3 (L3) \u2014 ce qui survient d\u00e8s que le tableau atteint quelques dizaines de m\u00e9gaoctets \u2014, le balayage exhaustif sature la bande passante du bus m\u00e9moire syst\u00e8me. Les \u00e9critures du masque bool\u00e9en temporaire et les lectures ult\u00e9rieures pour l&rsquo;extraction d&rsquo;indices provoquent des expulsions massives de lignes de cache, d\u00e9gradant substantiellement le d\u00e9bit des instructions processeur lors de balayages r\u00e9p\u00e9t\u00e9s ou imbriqu\u00e9s au sein d&rsquo;une boucle analytique volumineuse.<\/p>\n<h2>3. M\u00e9thode 2 : Extraction rigoureuse de la premi\u00e8re occurrence d&rsquo;une valeur<\/h2>\n<h3>3.1 D\u00e9construction syntaxique de l&rsquo;idiome np.where(x == valeur)[0][0]<\/h3>\n<p>Dans de multiples sc\u00e9narios d&rsquo;ing\u00e9nierie logicielle, l&rsquo;unicit\u00e9 de la valeur cible est garantie par construction, ou seule la premi\u00e8re occurrence spatiale pr\u00e9sente un int\u00e9r\u00eat computationnel pour la suite du traitement algorithmique. Pour r\u00e9pondre \u00e0 cette exigence, la communaut\u00e9 scientifique a popularis\u00e9 un idiome syntaxique extr\u00eamement r\u00e9pandu dans le code de production : l&rsquo;expression doublement index\u00e9e <code>np.where(x == valeur)[0][0]<\/code>. Bien que succincte, cette construction m\u00e9rite une d\u00e9construction analytique m\u00e9ticuleuse afin d&rsquo;en saisir la m\u00e9canique interne et la nature des objets transitoires manipul\u00e9s.<\/p>\n<p>Le premier s\u00e9lecteur d&rsquo;indice, mat\u00e9rialis\u00e9 par le premier <code>[0]<\/code>, s&rsquo;applique directement au tuple de dimensionnalit\u00e9 renvoy\u00e9 par la fonction <code>np.where()<\/code>. Comme explicit\u00e9 pr\u00e9c\u00e9demment, pour un tableau unidimensionnel, ce tuple ne contient qu&rsquo;une seule entit\u00e9 : le tableau NumPy regroupant l&rsquo;ensemble des coordonn\u00e9es valid\u00e9es le long du premier et unique axe. L&rsquo;\u00e9valuation de <code>np.where(x == valeur)[0]<\/code> permet donc d&rsquo;extraire ce <code>ndarray<\/code> d&rsquo;indices entiers de son enveloppe tuple. \u00c0 ce stade du pipeline d&rsquo;ex\u00e9cution, nous disposons d&rsquo;un tableau d&rsquo;indices conventionnel de type vectoriel.<\/p>\n<p>Le second s\u00e9lecteur d&rsquo;indice, formalis\u00e9 par le deuxi\u00e8me <code>[0]<\/code>, s&rsquo;applique directement sur ce tableau NumPy d&rsquo;indices fra\u00eechement d\u00e9sencapsul\u00e9. Son r\u00f4le fonctionnel consiste \u00e0 acc\u00e9der \u00e0 la premi\u00e8re cellule scalaire positionn\u00e9e \u00e0 l&rsquo;indice z\u00e9ro de ce sous-ensemble. Sur le plan du typage, cette double s\u00e9lection extrait une valeur scalaire concr\u00e8te typ\u00e9e selon les standards internes de NumPy, le plus souvent une instance de <code>np.int64<\/code> ou <code>np.int32<\/code>, selon l&rsquo;architecture de la machine h\u00f4te. Cette valeur scalaire peut alors \u00eatre imm\u00e9diatement convertie en un entier natif Python <code>int<\/code> ou \u00eatre utilis\u00e9e directement pour indexer un tableau tiers au sein d&rsquo;un graphe de calcul scientifique complexe.<\/p>\n<h3>3.2 Impl\u00e9mentation pratique et pi\u00e8ges li\u00e9s aux tableaux vides<\/h3>\n<p>Bien que s\u00e9duisante par son apparente concision, l&rsquo;utilisation aveugle de l&rsquo;idiome <code>np.where(x == valeur)[0][0]<\/code> rec\u00e8le un pi\u00e8ge architectural critique susceptible de provoquer des arr\u00eats inopin\u00e9s du syst\u00e8me lors de d\u00e9ploiements en environnement de production. Consid\u00e9rons le cas d&rsquo;usage o\u00f9 la valeur recherch\u00e9e est pr\u00e9sente dans le tableau. L&rsquo;ex\u00e9cution du code se d\u00e9roule de mani\u00e8re parfaitement nominale :<\/p>\n<p>Soit le tableau <code>x = np.array([10, 20, 30, 40, 50])<\/code>. L&rsquo;expression <code>indice = np.where(x == 30)[0][0]<\/code> renverra avec succ\u00e8s la valeur enti\u00e8re 2. La premi\u00e8re composante <code>[0]<\/code> isole le tableau <code>array([2])<\/code>, et la seconde composante <code>[0]<\/code> extrait le scalaire 2 sans aucune friction d&rsquo;ex\u00e9cution.<\/p>\n<p>Cependant, consid\u00e9rons d\u00e9sormais le sc\u00e9nario o\u00f9 le praticien scrute une valeur totalement absente de la collection de donn\u00e9es, par exemple la valeur scalaire 99. Dans cette hypoth\u00e8se, le pr\u00e9dicat <code>x == 99<\/code> renvoie un vecteur enti\u00e8rement compos\u00e9 de valeurs logiques <code>False<\/code>. Par voie de cons\u00e9quence, l&rsquo;appel \u00e0 <code>np.where(x == 99)<\/code> retourne un tuple contenant un tableau vide, formalis\u00e9 par <code>array([], dtype=int64)<\/code>. L&rsquo;application du premier indice <code>[0]<\/code> r\u00e9ussit et extrait ce tableau de taille nulle. En revanche, la tentative d&rsquo;application imm\u00e9diate du second op\u00e9rateur d&rsquo;acc\u00e8s <code>[0]<\/code> sur un tableau ne comportant aucun \u00e9l\u00e9ment d\u00e9clenche irr\u00e9m\u00e9diablement une exception fatale de type <code>IndexError: index 0 is out of bounds for axis 0 with size 0<\/code>.<\/p>\n<p>Pour immuniser le code contre cette d\u00e9faillance majeure, il est rigoureusement imp\u00e9ratif d&rsquo;introduire une couche de validation d\u00e9fensive. La strat\u00e9gie canonique de s\u00e9curisation consiste \u00e0 interroger la propri\u00e9t\u00e9 <code>size<\/code> du tableau d&rsquo;indices avant d&rsquo;acc\u00e9der au premier \u00e9l\u00e9ment :<\/p>\n<p><code>indices = np.where(x == 99)[0]<\/code><br \/>\n<code>premier_indice = indices[0] if indices.size &gt; 0 else None<\/code><\/p>\n<p>Cette approche d\u00e9fensive conditionne l&rsquo;acc\u00e8s direct \u00e0 la pr\u00e9sence effective d&rsquo;au moins un \u00e9l\u00e9ment, offrant la possibilit\u00e9 d&rsquo;assigner une valeur sentinelle appropri\u00e9e ou de d\u00e9clencher une gestion d&rsquo;erreur applicative ma\u00eetris\u00e9e en lieu et place d&rsquo;un plantage brutal du processus d&rsquo;ex\u00e9cution.<\/p>\n<h3>3.3 Optimisations alternatives pour l&rsquo;arr\u00eat pr\u00e9coce (Short-Circuiting)<\/h3>\n<p>D&rsquo;un point de vue algorithmique fondamental, l&rsquo;idiome <code>np.where(x == valeur)[0][0]<\/code> pr\u00e9sente une inefficience structurelle majeure : il est totalement d\u00e9pourvu de m\u00e9canisme d&rsquo;arr\u00eat pr\u00e9coce (<em>short-circuiting<\/em>). Quand bien m\u00eame l&rsquo;\u00e9l\u00e9ment convoit\u00e9 se situerait \u00e0 l&rsquo;indice 0 d&rsquo;un tableau contenant un milliard d&rsquo;\u00e9l\u00e9ments, <code>np.where()<\/code> g\u00e9n\u00e9rera obligatoirement le masque complet d&rsquo;un milliard de bool\u00e9ens et inspectera l&rsquo;int\u00e9gralit\u00e9 des positions avant de renvoyer le r\u00e9sultat. Le gaspillage de cycles processeur et de m\u00e9moire vive est donc substantiel lorsque l&rsquo;on cherche uniquement la premi\u00e8re occurrence.<\/p>\n<p>Pour contourner ce handicap sur des structures volumineuses o\u00f9 la premi\u00e8re occurrence a de fortes probabilit\u00e9s d&rsquo;appara\u00eetre pr\u00e9cocement, plusieurs alternatives algorithmiques doivent \u00eatre envisag\u00e9es. La premi\u00e8re consiste \u00e0 mobiliser les it\u00e9rateurs natifs de Python coupl\u00e9s \u00e0 la fonction <code>next()<\/code> et \u00e0 la fonction int\u00e9gr\u00e9e <code>enumerate()<\/code> :<\/p>\n<p><code>premier_indice = next((i for i, elem in enumerate(x) if elem == valeur), None)<\/code><\/p>\n<p>Cette approche interrompt instantan\u00e9ment le parcours de la m\u00e9moire d\u00e8s que la concordance est av\u00e9r\u00e9e. Cependant, en raison du co\u00fbt d&rsquo;interpr\u00e9tation de la machine virtuelle Python pour chaque \u00e9l\u00e9ment it\u00e9r\u00e9 (<em>boxing<\/em> et <em>unboxing<\/em> des types), cette m\u00e9thode purement Python ne devient plus rapide que <code>np.where()<\/code> que si la valeur cible est situ\u00e9e tr\u00e8s pr\u00e8s du d\u00e9but du tableau (g\u00e9n\u00e9ralement dans les premiers pourcents de la collection). Si la valeur se trouve \u00e0 la fin ou est absente, la boucle Python peut s&rsquo;av\u00e9rer jusqu&rsquo;\u00e0 cinquante fois plus lente que l&rsquo;impl\u00e9mentation C vectoris\u00e9e de NumPy.<\/p>\n<p>La solution optimale combinant la vitesse de bas niveau et la capacit\u00e9 d&rsquo;arr\u00eat pr\u00e9coce r\u00e9side dans l&rsquo;utilisation de compilateurs Just-In-Time tels que <a rel=\"noopener noreferrer\" href=\"https:\/\/numba.pydata.org\/\">Numba<\/a>. En annotant une fonction personnalis\u00e9e avec le d\u00e9corateur <code>@numba.jit(nopython=True)<\/code>, il est possible d&rsquo;\u00e9crire une boucle lin\u00e9aire simple inspectant le tableau de donn\u00e9es contigu au niveau machine. D\u00e8s que la condition d&rsquo;\u00e9galit\u00e9 est v\u00e9rifi\u00e9e, l&rsquo;indice est imm\u00e9diatement renvoy\u00e9, terminant la fonction sans aucune allocation de m\u00e9moire interm\u00e9diaire ni \u00e9valuation superflue des \u00e9l\u00e9ments restants. Cette architecture hybride offre les performances th\u00e9oriques les plus \u00e9lev\u00e9es de l&rsquo;informatique moderne pour la d\u00e9tection unitaire.<\/p>\n<h2>4. M\u00e9thode 3 : Recherche simultan\u00e9e de la premi\u00e8re occurrence de valeurs multiples<\/h2>\n<h3>4.1 Principe combin\u00e9 de np.argsort() et np.searchsorted()<\/h3>\n<p>L&rsquo;ing\u00e9nierie des donn\u00e9es massives impose fr\u00e9quemment une exigence plus complexe : localiser de mani\u00e8re simultan\u00e9e et ultra-rapide les positions respectives d&rsquo;un ensemble de plusieurs milliers de valeurs cibles distinctes au sein d&rsquo;un vaste tableau de donn\u00e9es non ordonn\u00e9. R\u00e9it\u00e9rer de fa\u00e7on na\u00efve une boucle it\u00e9rative ex\u00e9cutant <code>np.where()<\/code> pour chaque valeur cible conduirait \u00e0 une complexit\u00e9 temporelle quadratique inacceptable de l&rsquo;ordre de O(m &times; n), o\u00f9 <em>m<\/em> repr\u00e9sente le volume des cibles et <em>n<\/em> la dimension du tableau r\u00e9cepteur. Pour surmonter cette barri\u00e8re computationnelle, il est n\u00e9cessaire de faire converger deux algorithmes fondamentaux : le tri indirect via <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.argsort.html\"><code>np.argsort()<\/code><\/a> et la recherche dichotomique via <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.searchsorted.html\"><code>np.searchsorted()<\/code><\/a>.<\/p>\n<p>La fonction <code>np.argsort()<\/code> op\u00e8re un tri indirect du tableau d&rsquo;origine. Plut\u00f4t que de r\u00e9ordonner physiquement les donn\u00e9es en m\u00e9moire vive, elle engendre un tableau d&rsquo;entiers repr\u00e9sentant la permutation d&rsquo;indices qui permettrait d&rsquo;ordonner le tableau de mani\u00e8re croissante. Ce tableau de permutation, conventionnellement nomm\u00e9 <code>sorter<\/code>, conf\u00e8re une propri\u00e9t\u00e9 d\u00e9terminante : l&rsquo;application de <code>x[sorter]<\/code> produit une vue conceptuellement tri\u00e9e des donn\u00e9es originales. La complexit\u00e9 temporelle de cette op\u00e9ration de tri pr\u00e9alable est r\u00e9gie par l&rsquo;algorithme Quicksort ou Timsort, s&rsquo;\u00e9tablissant \u00e0 O(n log n).<\/p>\n<p>Une fois cette indexation tri\u00e9e \u00e9tablie, l&rsquo;interrogation s&rsquo;effectue au moyen de <code>np.searchsorted()<\/code>. Cette primitive algorithmique impl\u00e9mente une recherche dichotomique pure (bisection logarithmique) hautement optimis\u00e9e en C. Pour chaque valeur cible qui lui est soumise, elle identifie en un temps asymptotique O(log n) la position d&rsquo;insertion th\u00e9orique pr\u00e9servant l&rsquo;ordre de tri au sein du tableau virtuellement ordonn\u00e9 via le param\u00e8tre <code>sorter=sorter<\/code>. Pour r\u00e9assigner cette position d&rsquo;insertion \u00e0 la coordonn\u00e9e spatiale r\u00e9elle occup\u00e9e dans le tableau d&rsquo;origine non tri\u00e9, il suffit de composer les indices r\u00e9sultants par indexation avanc\u00e9e dans le tableau de tri : <code>indice_reel = sorter[position_triee]<\/code>. La recherche conjointe de <em>m<\/em> valeurs s&rsquo;ex\u00e9cute ainsi en O((n + m) log n), surclassant magistralement les balayages lin\u00e9aires successifs.<\/p>\n<h3>4.2 D\u00e9monstration empirique sur un tableau non tri\u00e9<\/h3>\n<p>Afin d&rsquo;illustrer la m\u00e9canique rigoureuse de cette combinaison algorithmique, concevons une d\u00e9monstration pas \u00e0 pas sur un tableau d\u00e9sordonn\u00e9 h\u00e9bergeant des donn\u00e9es num\u00e9riques h\u00e9t\u00e9rog\u00e8nes :<\/p>\n<p>Soit le tableau de donn\u00e9es sources <code>x = np.array([45, 12, 89, 7, 33, 12, 99])<\/code>. D\u00e9finissons un ensemble vectoriel de valeurs cibles multiples dont nous d\u00e9sirons localiser la premi\u00e8re occurrence respective : <code>vals = np.array([33, 7, 12])<\/code>.<\/p>\n<p>La premi\u00e8re phase op\u00e9rationnelle consiste \u00e0 d\u00e9river le tableau de permutation ordonn\u00e9 :<\/p>\n<p><code>sorter = np.argsort(x)<\/code><\/p>\n<p>L&rsquo;inspection du tableau <code>sorter<\/code> donne la s\u00e9quence d&rsquo;indices <code>[3, 1, 5, 4, 0, 2, 6]<\/code>, correspondant \u00e0 la projection ordonn\u00e9e des valeurs de <code>x<\/code> (qui \u00e9quivaudrait \u00e0 <code>[7, 12, 12, 33, 45, 89, 99]<\/code>). La seconde phase consiste \u00e0 soumettre conjointement le tableau des cibles et le tableau de tri \u00e0 la fonction de recherche dichotomique :<\/p>\n<p><code>positions_triees = np.searchsorted(x, vals, sorter=sorter)<\/code><\/p>\n<p>La fonction <code>np.searchsorted()<\/code> applique simultan\u00e9ment l&rsquo;algorithme de bisection pour chacune des valeurs de <code>vals<\/code>. Elle localise les cibles aux rangs virtuels ordonn\u00e9s <code>[3, 0, 1]<\/code> (la valeur 33 se situe au rang tri\u00e9 3, la valeur 7 au rang 0, et la premi\u00e8re occurrence de la valeur 12 au rang 1). La derni\u00e8re \u00e9tape reconstitue les coordonn\u00e9es originelles au sein du tableau <code>x<\/code> initial non ordonn\u00e9 :<\/p>\n<p><code>indices_finaux = sorter[positions_triees]<\/code><\/p>\n<p>Le r\u00e9sultat produit est le tableau <code>array([4, 3, 1])<\/code>. Une v\u00e9rification unitaire confirme l&rsquo;infaillibilit\u00e9 de la proc\u00e9dure : <code>x[4]<\/code> vaut bien 33, <code>x[3]<\/code> est \u00e9gal \u00e0 7, et <code>x[1]<\/code> correspond parfaitement \u00e0 la premi\u00e8re occurrence de 12. L&rsquo;\u00e9l\u00e9gance structurelle de cette composition r\u00e9side dans sa vectorisation int\u00e9grale : aucune boucle it\u00e9rative n&rsquo;a \u00e9t\u00e9 instanci\u00e9e dans l&rsquo;espace utilisateur Python, le microprocesseur ayant ex\u00e9cut\u00e9 exclusivement des instructions compil\u00e9es de bas niveau.<\/p>\n<h3>4.3 Contr\u00f4le de validit\u00e9 des r\u00e9sultats et gestion des valeurs non appari\u00e9es<\/h3>\n<p>L&rsquo;utilisation de <code>np.searchsorted()<\/code> impose une vigilance algorithmique capitale, inh\u00e9rente \u00e0 la nature m\u00eame de la recherche dichotomique d&rsquo;insertion : la fonction ne v\u00e9rifie pas si la valeur cible est <em>effectivement pr\u00e9sente<\/em> dans le tableau source. Elle se borne \u00e0 identifier l&rsquo;indice o\u00f9 la valeur <em>devrait \u00eatre ins\u00e9r\u00e9e<\/em> pour maintenir l&rsquo;ordonnancement math\u00e9matique. Par cons\u00e9quent, si une valeur recherch\u00e9e est absente du tableau, <code>np.searchsorted()<\/code> retournera tout de m\u00eame un indice plausible, compris entre 0 et <em>n<\/em> (la taille du tableau).<\/p>\n<p>Pour pr\u00e9venir la survenue d&rsquo;aberrations num\u00e9riques ou d&rsquo;erreurs de faux positifs, il est math\u00e9matiquement indispensable de greffer un masque de validation crois\u00e9e syst\u00e9matique imm\u00e9diatement apr\u00e8s l&rsquo;extraction. Ce filtre de s\u00e9curit\u00e9 se d\u00e9compose en deux v\u00e9rifications logiques simultan\u00e9es :<\/p>\n<p>D&rsquo;une part, il convient de s&rsquo;assurer que l&rsquo;indice renvoy\u00e9 par la dichotomie ne correspond pas \u00e0 une insertion au-del\u00e0 de la fronti\u00e8re droite du tableau, c&rsquo;est-\u00e0-dire que <code>positions_triees &lt; len(x)<\/code>. D&rsquo;autre part, pour tous les indices n&rsquo;exc\u00e9dant pas cette borne, il est n\u00e9cessaire de v\u00e9rifier par r\u00e9indexation directe si l&rsquo;\u00e9l\u00e9ment effectivement pr\u00e9sent \u00e0 l&rsquo;indice d&rsquo;insertion th\u00e9orique co\u00efncide strictement avec la valeur cible demand\u00e9e : <code>x[sorter[positions_triees]] == vals<\/code>.<\/p>\n<p>L&rsquo;assemblage formel de cette proc\u00e9dure de v\u00e9rification s&rsquo;exprime par le vecteur de validation bool\u00e9en :<\/p>\n<p><code>masque_valide = (positions_triees &lt; len(x)) &amp; (x[sorter[np.clip(positions_triees, 0, len(x)-1)]] == vals)<\/code><\/p>\n<p>Ce masque permet de discriminer de mani\u00e8re cat\u00e9gorique les cibles r\u00e9ellement identifi\u00e9es de celles qui n&rsquo;existent pas dans le tableau d&rsquo;entr\u00e9e. Pour les cibles absentes, l&rsquo;indice calcul\u00e9 peut alors \u00eatre remplac\u00e9 par une valeur sentinelle standard, telle que -1. Cette rigueur algorithmique transforme un m\u00e9canisme d&rsquo;insertion dichotomique th\u00e9orique en un moteur de scrutation multidimensionnelle robuste pour les environnements industriels.<\/p>\n<h2>5. Gestion de la multidimensionnalit\u00e9 : Matrices et tenseurs<\/h2>\n<h3>5.1 Interpr\u00e9tation des coordonn\u00e9es cart\u00e9siennes multidimensionnelles<\/h3>\n<p>D\u00e8s lors que l&rsquo;on quitte le domaine des vecteurs unidimensionnels pour explorer des matrices bidimensionnelles, des volumes d&rsquo;imagerie tridimensionnels ou des tenseurs d&rsquo;ordre sup\u00e9rieur, la topologie de l&rsquo;indexation se complexifie consid\u00e9rablement. Lorsque la fonction <code>np.where(condition)<\/code> ou sa contrepartie <code>np.nonzero(condition)<\/code> est appliqu\u00e9e sur un tenseur de dimension <em>D<\/em>, elle renvoie invariablement un tuple compos\u00e9 de <em>D<\/em> tableaux NumPy distincts. Chacun de ces tableaux unidimensionnels encapsule la suite des coordonn\u00e9es sp\u00e9cifiques associ\u00e9es \u00e0 un axe dimensionnel particulier.<\/p>\n<p>Prenons l&rsquo;exemple d&rsquo;une matrice bidimensionnelle <em>M<\/em> repr\u00e9sentant une grille de donn\u00e9es spatiales de dimensions (<em>R<\/em>, <em>C<\/em>). L&rsquo;appel <code>indices_lignes, indices_colonnes = np.where(M == valeur)<\/code> g\u00e9n\u00e8re deux vecteurs de m\u00eame taille <em>k<\/em>, o\u00f9 <em>k<\/em> quantifie le nombre d&rsquo;\u00e9l\u00e9ments satisfaisant le crit\u00e8re. La relation spatiale n&rsquo;est pas repr\u00e9sent\u00e9e sous forme d&rsquo;une liste de couples (ligne, colonne), mais sous la forme de coordonn\u00e9es d\u00e9corr\u00e9l\u00e9es : le <em>i<\/em>-i\u00e8me \u00e9l\u00e9ment du tableau <code>indices_lignes<\/code> forme la coordonn\u00e9e cart\u00e9sienne compl\u00e8te avec le <em>i<\/em>-i\u00e8me \u00e9l\u00e9ment du tableau <code>indices_colonnes<\/code>.<\/p>\n<p>Cette s\u00e9gr\u00e9gation axiale d\u00e9concerte souvent les d\u00e9veloppeurs habitu\u00e9s aux paradigmes orient\u00e9s objet o\u00f9 les points sont mod\u00e9lis\u00e9s par des structures individualis\u00e9es. Cependant, la repr\u00e9sentation de NumPy est nettement sup\u00e9rieure pour les op\u00e9rations vectorielles d&rsquo;indexation matricielle : l&rsquo;expression <code>M[indices_lignes, indices_colonnes]<\/code> extrait directement les \u00e9l\u00e9ments cibles sans aucune reconversion structurelle. Si l&rsquo;on requiert formellement une liste de paires de coordonn\u00e9es sous forme matricielle pour des op\u00e9rations g\u00e9om\u00e9triques ou d&rsquo;exportation de donn\u00e9es, il convient de restructurer ces tableaux au moyen de la fonction <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.column_stack.html\"><code>np.column_stack()<\/code><\/a> :<\/p>\n<p><code>coordonnees_matricielles = np.column_stack((indices_lignes, indices_colonnes))<\/code><\/p>\n<p>Cette op\u00e9ration agr\u00e8ge les composantes dimensionnelles pour g\u00e9n\u00e9rer une matrice de dimensions (<em>k<\/em>, 2), o\u00f9 chaque ligne incarne pr\u00e9cis\u00e9ment le vecteur positionnel cart\u00e9sien d&rsquo;un \u00e9l\u00e9ment cible.<\/p>\n<h3>5.2 Conversion entre indices aplatis (flat) et indices de grille avec np.unravel_index()<\/h3>\n<p>Sur le plan de l&rsquo;architecture mat\u00e9rielle, la m\u00e9moire vive d&rsquo;un ordinateur est un espace d&rsquo;adressage rigoureusement lin\u00e9aire et unidimensionnel. NumPy \u00e9mule la multidimensionnalit\u00e9 en effectuant des projections g\u00e9om\u00e9triques fond\u00e9es sur ses pas de m\u00e9moire (<em>strides<\/em>). De fait, tout \u00e9l\u00e9ment au sein d&rsquo;un tenseur multidimensionnel poss\u00e8de une coordonn\u00e9e spatiale intrins\u00e8que (le n-uplet cart\u00e9sien) et une coordonn\u00e9e s\u00e9rialis\u00e9e globale, appel\u00e9e <em>indice plat<\/em> ou <em>flat index<\/em>, qui repr\u00e9sente le d\u00e9calage scalaire de l&rsquo;\u00e9l\u00e9ment depuis l&rsquo;adresse m\u00e9moire de base si le tableau \u00e9tait lin\u00e9aris\u00e9 en un vecteur continu.<\/p>\n<p>Certaines op\u00e9rations computationnelles tr\u00e8s optimis\u00e9es \u2014 \u00e0 l&rsquo;instar de la recherche d&rsquo;extremums via <code>np.argmax()<\/code> ou <code>np.argmin()<\/code> \u2014 explorent par d\u00e9faut les tenseurs multidimensionnels sous leur forme aplatie et renvoient un scalaire unique repr\u00e9sentant cet indice lin\u00e9aris\u00e9. Pour traduire cet indice plat en un v\u00e9ritable n-uplet de coordonn\u00e9es spatiales exploitable le long des axes de la matrice d&rsquo;origine, NumPy met \u00e0 disposition la fonction hautement sp\u00e9cialis\u00e9e <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.unravel_index.html\"><code>np.unravel_index()<\/code><\/a>.<\/p>\n<p>La formulation math\u00e9matique sous-jacente \u00e0 <code>np.unravel_index(indices, shape)<\/code> applique des divisions euclidiennes et des modulos successifs sur l&rsquo;indice plat selon les dimensions fournies par le param\u00e8tre <code>shape<\/code>, en respectant la convention d&rsquo;ordonnancement m\u00e9moire C (par lignes) ou Fortran (par colonnes). Par exemple, dans une matrice d&rsquo;imagerie m\u00e9dicale de r\u00e9solution (1024, 768), l&rsquo;identification du voxel d&rsquo;intensit\u00e9 maximale via <code>flat_idx = np.argmax(image)<\/code> renvoie un entier compris entre 0 et 786 431. L&rsquo;application imm\u00e9diate de :<\/p>\n<p><code>coord_y, coord_x = np.unravel_index(flat_idx, image.shape)<\/code><\/p>\n<p>permet de convertir instantan\u00e9ment cet indice s\u00e9rialis\u00e9 en une coordonn\u00e9e bidimensionnelle exacte sans requ\u00e9rir la moindre allocation de tableau interm\u00e9diaire. Ce paradigme s&rsquo;av\u00e8re infiniment plus \u00e9conome en ressources de calcul que la g\u00e9n\u00e9ration d&rsquo;un masque bool\u00e9en complet sur des grilles multidimensionnelles denses.<\/p>\n<h3>5.3 Extraction directe de sous-tableaux via les indices rep\u00e9r\u00e9s<\/h3>\n<p>La capture d&rsquo;indices multidimensionnels ne constitue g\u00e9n\u00e9ralement pas une fin en soi, mais une \u00e9tape transitoire visant soit \u00e0 extraire des sous-espaces de donn\u00e9es corr\u00e9l\u00e9s, soit \u00e0 op\u00e9rer des mutations cibl\u00e9es au sein de la structure matricielle. L&rsquo;interaction entre les indices extraits et l&rsquo;op\u00e9rateur d&rsquo;assignation de NumPy ob\u00e9it \u00e0 des r\u00e8gles s\u00e9mantiques pr\u00e9cises qu&rsquo;il est indispensable de ma\u00eetriser pour \u00e9viter des corruptions logiques de donn\u00e9es.<\/p>\n<p>Lorsque des indices sont extraits sous la forme d&rsquo;un tuple de tableaux coordonn\u00e9s <code>indices = np.where(M &gt; seuil)<\/code>, l&rsquo;injection directe de ce tuple au sein de la matrice originale <code>sous_ensemble = M[indices]<\/code> active le m\u00e9canisme d&rsquo;indexation avanc\u00e9e par tableau d&rsquo;entiers. Contrairement \u00e0 l&rsquo;indexation par tranches simples (comme <code>M[0:10, 2:5]<\/code>), l&rsquo;indexation avanc\u00e9e g\u00e9n\u00e8re <em>toujours<\/em> une copie autonome des donn\u00e9es en m\u00e9moire, et jamais une simple vue (<em>view<\/em>). Toute modification subs\u00e9quente appliqu\u00e9e sur la variable <code>sous_ensemble<\/code> ne sera donc aucunement r\u00e9percut\u00e9e sur la matrice source <code>M<\/code>.<\/p>\n<p>Inversement, si l&rsquo;objectif de l&rsquo;ing\u00e9nieur consiste \u00e0 effectuer une mutation sur place (<em>in-place<\/em>) des \u00e9l\u00e9ments cibles rep\u00e9r\u00e9s par les coordonn\u00e9es \u2014 par exemple, pour remplacer l&rsquo;ensemble des valeurs aberrantes d\u00e9tect\u00e9es par une valeur constante ou pour appliquer un facteur d&rsquo;att\u00e9nuation local \u2014, la mutation doit s&rsquo;op\u00e9rer directement sur la matrice de r\u00e9f\u00e9rence en combinant l&rsquo;indexation matricielle et l&rsquo;assignation :<\/p>\n<p><code>M[indices] = nouvelle_valeur<\/code><\/p>\n<p>Dans ce sc\u00e9nario sp\u00e9cifique, NumPy contourne l&rsquo;instanciation de la copie interm\u00e9diaire et modifie directement les adresses m\u00e9moire point\u00e9es par les coordonn\u00e9es cart\u00e9siennes au sein du bloc m\u00e9moire contigu originel, garantissant une efficience de traitement maximale et une empreinte m\u00e9moire quasi stationnaire.<\/p>\n<h2>6. Recherche approch\u00e9e et tol\u00e9rances num\u00e9riques pour flottants<\/h2>\n<h3>6.1 Probl\u00e9matique de l&rsquo;impr\u00e9cision arithm\u00e9tique de la norme IEEE 754<\/h3>\n<p>L&rsquo;une des sources de dysfonctionnements les plus pernicieuses dans le d\u00e9veloppement d&rsquo;algorithmes scientifiques r\u00e9side dans l&rsquo;application na\u00efve de tests d&rsquo;\u00e9galit\u00e9 stricte <code>x == valeur<\/code> sur des tableaux de nombres \u00e0 virgule flottante (types <code>float32<\/code> et <code>float64<\/code>). Dans les syst\u00e8mes informatiques modernes, les nombres r\u00e9els sont repr\u00e9sent\u00e9s de mani\u00e8re discr\u00e8te conform\u00e9ment \u00e0 la norme internationale IEEE 754, qui encode les grandeurs sous forme de signe, mantisse et exposant binaire. En raison de la base binaire utilis\u00e9e par le silicium, la vaste majorit\u00e9 des fractions d\u00e9cimales ne peuvent \u00eatre repr\u00e9sent\u00e9es avec une exactitude infinie.<\/p>\n<p>Par exemple, le nombre d\u00e9cimal 0.1 ne poss\u00e8de pas de d\u00e9veloppement binaire fini ; il s&rsquo;agit d&rsquo;une suite p\u00e9riodique infinie d&rsquo;octets qui subit in\u00e9luctablement une troncature lors de son assignation dans un registre m\u00e9moire 64 bits. Lors de la conduite de calculs arithm\u00e9tiques interm\u00e9diaires \u2014 tels que des sommations vectorielles, des multiplications de matrices ou des int\u00e9grations diff\u00e9rentielles \u2014, des erreurs d&rsquo;arrondi infinit\u00e9simales s&rsquo;accumulent. De fait, une op\u00e9ration \u00e9l\u00e9mentaire telle que l&rsquo;addition r\u00e9p\u00e9t\u00e9e de trois grandeurs d\u00e9cimales peut engendrer une d\u00e9rive de l&rsquo;ordre de l&rsquo;epsilon machine : math\u00e9matiquement, 0.1 + 0.2 \u00e9quivaut \u00e0 0.3, mais sur le plan arithm\u00e9tique binaire mat\u00e9riel, l&rsquo;expression <code>(0.1 + 0.2) == 0.3<\/code> s&rsquo;\u00e9value \u00e0 la valeur logique <code>False<\/code>.<\/p>\n<p>Transpos\u00e9e \u00e0 NumPy, la tentative d&rsquo;identifier l&rsquo;indice d&rsquo;une valeur flottante au moyen de l&rsquo;expression <code>np.where(x == 0.3)<\/code> est vou\u00e9e \u00e0 \u00e9chouer syst\u00e9matiquement si les donn\u00e9es de <code>x<\/code> proc\u00e8dent d&rsquo;op\u00e9rations analytiques pr\u00e9alables, g\u00e9n\u00e9rant un tableau de coordonn\u00e9es d\u00e9sesp\u00e9r\u00e9ment vide. Traiter des donn\u00e9es flottantes exige d&rsquo;abandonner le concept d&rsquo;\u00e9galit\u00e9 binaire stricte au profit de m\u00e9triques de proximit\u00e9 fond\u00e9es sur des voisinages de tol\u00e9rance analytique.<\/p>\n<h3>6.2 Identification d&rsquo;indices par seuillage avec np.isclose() et np.allclose()<\/h3>\n<p>Pour r\u00e9pondre \u00e0 cette contrainte physique fondamentale, NumPy fournit une panoplie de pr\u00e9dicats logiques approximatifs dont le fer de lance est la fonction vectoris\u00e9e <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.isclose.html\"><code>np.isclose()<\/code><\/a>. Cette fonction substitue au test d&rsquo;\u00e9galit\u00e9 une \u00e9valuation d&rsquo;in\u00e9galit\u00e9 stricte encapsulant simultan\u00e9ment une tol\u00e9rance absolue (param\u00e8tre <code>atol<\/code>) et une tol\u00e9rance relative (param\u00e8tre <code>rtol<\/code>). L&rsquo;\u00e9valuation math\u00e9matique ex\u00e9cut\u00e9e pour chaque \u00e9l\u00e9ment <em>a<\/em> par rapport \u00e0 une cible <em>b<\/em> ob\u00e9it formellement \u00e0 la formule suivante :<\/p>\n<p>|a &#8211; b| &le; (atol + rtol &times; |b|)<\/p>\n<p>La tol\u00e9rance relative permet de compenser l&rsquo;espacement croissant des nombres flottants \u00e0 mesure que l&rsquo;on progresse vers de tr\u00e8s grands ordres de grandeur, tandis que la tol\u00e9rance absolue garantit un comportement stable \u00e0 l&rsquo;approche de z\u00e9ro. Pour localiser les indices des \u00e9l\u00e9ments d&rsquo;un tableau flottant <code>x<\/code> qui concordent avec une valeur cible \u00e0 une marge d&rsquo;erreur num\u00e9rique pr\u00e9d\u00e9termin\u00e9e, le standard de codage scientifique impose la composition suivante :<\/p>\n<p><code>indices_proches = np.where(np.isclose(x, valeur_cible, atol=1e-5, rtol=1e-7))[0]<\/code><\/p>\n<p>Le dimensionnement des param\u00e8tres <code>atol<\/code> et <code>rtol<\/code> ne doit rien au hasard : il d\u00e9coule directement de la pr\u00e9cision du type de donn\u00e9es utilis\u00e9 (le seuil de pr\u00e9cision machine \u00e9tant d&rsquo;environ 10<sup>-7<\/sup> pour le type <code>float32<\/code> et de 10<sup>-16<\/sup> pour le type <code>float64<\/code>) et de l&rsquo;amplitude physique du bruit num\u00e9rique g\u00e9n\u00e9r\u00e9 par la cha\u00eene de traitement ant\u00e9rieure. \u00c0 l&rsquo;inverse de <code>np.isclose()<\/code> qui op\u00e8re \u00e9l\u00e9ment par \u00e9l\u00e9ment, la fonction connexe <code>np.allclose()<\/code> \u00e9value l&rsquo;\u00e9quivalence globale de tableaux entiers et renvoie un bool\u00e9en scalaire unique, impropre \u00e0 la vectorisation d&rsquo;indices mais crucial pour les tests d&rsquo;int\u00e9gration logiciels.<\/p>\n<h3>6.3 Recherche de la valeur la plus proche (plus proche voisin scalaire)<\/h3>\n<p>Dans de nombreuses disciplines quantitatives, les grandeurs th\u00e9oriques cibles ne figurent pas n\u00e9cessairement dans la s\u00e9rie d&rsquo;\u00e9chantillonnage exp\u00e9rimental, m\u00eame en int\u00e9grant une fen\u00eatre de tol\u00e9rance. Le probl\u00e8me computationnel consiste alors \u00e0 localiser l&rsquo;indice de l&rsquo;\u00e9l\u00e9ment empirique qui minimise la distance euclidienne avec la cible : il s&rsquo;agit du probl\u00e8me du plus proche voisin scalaire (<em>scalar nearest neighbor<\/em>).<\/p>\n<p>La formalisation algorithmique de ce probl\u00e8me s&rsquo;articule autour de la fonction de distance absolue. Pour un tableau de donn\u00e9es <code>x<\/code> et une valeur de r\u00e9f\u00e9rence scalaire <code>valeur_cible<\/code>, l&rsquo;\u00e9cart m\u00e9trique pour chaque \u00e9l\u00e9ment est calcul\u00e9 de mani\u00e8re vectoris\u00e9e via l&rsquo;expression <code>np.abs(x - valeur_cible)<\/code>. Une fois ce vecteur d&rsquo;\u00e9carts spatiaux instanci\u00e9, la localisation de l&rsquo;indice minimisant cette fonction de co\u00fbt est d\u00e9l\u00e9gu\u00e9e \u00e0 la primitive hautement performante <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.argmin.html\"><code>np.argmin()<\/code><\/a> :<\/p>\n<p><code>indice_plus_proche = np.argmin(np.abs(x - valeur_cible))<\/code><\/p>\n<p>Cette directive r\u00e9sout le probl\u00e8me en un unique passage de complexit\u00e9 lin\u00e9aire O(n), sans allouer de masques bool\u00e9ens et sans n\u00e9cessiter de tri pr\u00e9alable. L&rsquo;indice renvoy\u00e9 correspond formellement \u00e0 la premi\u00e8re position physique h\u00e9bergeant la distance absolue minimale.<\/p>\n<p>Il importe toutefois de souligner la gestion des cas d&rsquo;\u00e9quidistance parfaite. Si deux valeurs distinctes du tableau se situent \u00e0 une distance rigoureusement identique de la cible (par exemple, des valeurs 9.0 et 11.0 pour une cible positionn\u00e9e exactement \u00e0 10.0), la fonction <code>np.argmin()<\/code> adopte un comportement d\u00e9terministe strict : elle renvoie invariablement le <em>premier<\/em> indice rencontr\u00e9 dans le sens de lecture s\u00e9quentiel de la m\u00e9moire. Si le traitement analytique requiert la capture de <em>toutes<\/em> les positions \u00e9quidistantes, il devient indispensable de r\u00e9introduire une composition avec <code>np.isclose()<\/code> appliqu\u00e9e sur la distance minimale pr\u00e9alablement calcul\u00e9e.<\/p>\n<h2>7. Indexation conditionnelle avanc\u00e9e et crit\u00e8res logiques multiples<\/h2>\n<h3>7.1 Combinaisons logiques bool\u00e9ennes bit-\u00e0-bit<\/h3>\n<p>La localisation d&rsquo;\u00e9l\u00e9ments au sein de structures NumPy complexes d\u00e9passe fr\u00e9quemment le cadre de la simple \u00e9galit\u00e9 unaire pour englober des conjonctions et des disjonctions de pr\u00e9dicats logiques multiples \u2014 par exemple, l&rsquo;identification de donn\u00e9es confin\u00e9es au sein d&rsquo;un intervalle num\u00e9rique semi-ouvert [<em>a<\/em>, <em>b<\/em>[. Dans le langage Python standard, la conjonction logique s&rsquo;exprime au moyen des mots-cl\u00e9s <code>and<\/code>, <code>or<\/code> et <code>not<\/code>. Cependant, l&rsquo;usage de ces op\u00e9rateurs de court-circuit sur des tableaux NumPy d\u00e9clenche instantan\u00e9ment une erreur fatale de type <code>ValueError: The truth value of an array with more than one element is ambiguous<\/code>.<\/p>\n<p>Ce comportement d\u00e9coule du fait que les mots-cl\u00e9s natifs de Python tentent d&rsquo;\u00e9valuer la v\u00e9rit\u00e9 globale de l&rsquo;objet dans son int\u00e9gralit\u00e9 (en invoquant la m\u00e9thode magique <code>__bool__<\/code>), ce qui est d\u00e9pourvu de sens pour un ensemble multidimensionnel. Pour ordonner une \u00e9valuation \u00e9l\u00e9ment par \u00e9l\u00e9ment sur l&rsquo;int\u00e9gralit\u00e9 du domaine vectoriel, il est imp\u00e9ratif d&rsquo;utiliser exclusivement les op\u00e9rateurs logiques bit-\u00e0-bit (<em>bitwise<\/em>) surcharg\u00e9s par NumPy :<\/p>\n<ul>\n<li>L&rsquo;op\u00e9rateur esperluette <code>&amp;<\/code> pour la conjonction logique vectoris\u00e9e (ET).<\/li>\n<li>L&rsquo;op\u00e9rateur barre verticale <code>|<\/code> pour la disjonction logique vectoris\u00e9e (OU).<\/li>\n<li>L&rsquo;op\u00e9rateur tilde <code>~<\/code> pour la n\u00e9gation logique vectoris\u00e9e (NON).<\/li>\n<\/ul>\n<p>Cette contrainte syntaxique s&rsquo;accompagne d&rsquo;une r\u00e8gle absolue de priorit\u00e9 d&rsquo;op\u00e9rateurs : en langage Python, les op\u00e9rateurs bit-\u00e0-bit poss\u00e8dent une priorit\u00e9 syntaxique sup\u00e9rieure aux op\u00e9rateurs de comparaison relationnelle tels que <code>==<\/code>, <code>&lt;<\/code>, ou <code>&gt;=<\/code>. Par cons\u00e9quent, une instruction non prot\u00e9g\u00e9e telle que <code>x &gt; a &amp; x &lt; b<\/code> sera interpr\u00e9t\u00e9e par le parseur sous la forme <code>x &gt; (a &amp; x) &lt; b<\/code>, provoquant des erreurs s\u00e9mantiques ou de typage majeures. Il est donc math\u00e9matiquement indispensable de contraindre l&rsquo;ordre d&rsquo;\u00e9valuation via un parenth\u00e9sage strict de chaque proposition logique :<\/p>\n<p><code>indices_intervalle = np.where((x &gt;= borne_inf) &amp; (x &lt; borne_sup))[0]<\/code><\/p>\n<h3>7.2 Utilisation de np.isin() pour la recherche d&rsquo;ensembles d&rsquo;appartenance<\/h3>\n<p>Un cas d&rsquo;\u00e9cole r\u00e9current dans le pr\u00e9traitement de donn\u00e9es consiste \u00e0 localiser la totalit\u00e9 des indices dont les valeurs appartiennent \u00e0 un ensemble discret pr\u00e9d\u00e9termin\u00e9 de symboles ou de nombres scalaires. Tenter de formaliser ce probl\u00e8me en cha\u00eenant manuellement de multiples disjonctions bit-\u00e0-bit (par exemple <code>(x == v1) | (x == v2) | (x == v3)<\/code>) devient intenable sur le plan de la lisibilit\u00e9 et de la maintenabilit\u00e9 d\u00e8s que la taille de l&rsquo;ensemble d&rsquo;interrogation d\u00e9passe quelques unit\u00e9s. De surcro\u00eet, cette approche multiplie la cr\u00e9ation de masques interm\u00e9diaires \u00e9ph\u00e9m\u00e8res en m\u00e9moire vive.<\/p>\n<p>La biblioth\u00e8que NumPy r\u00e9sout \u00e9l\u00e9gamment ce d\u00e9fi par l&rsquo;interm\u00e9diaire de la fonction hautement optimis\u00e9e <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.isin.html\"><code>np.isin()<\/code><\/a>. Cette fonction accepte en argument le tableau cible <code>element<\/code> ainsi qu&rsquo;un ensemble de r\u00e9f\u00e9rence <code>test_elements<\/code> (qui peut se mat\u00e9rialiser sous forme de liste, d&rsquo;ensemble ou de tableau NumPy). Elle \u00e9value la relation ensembliste formelle <em>x<\/em> &isin; <em>E<\/em> de mani\u00e8re enti\u00e8rement vectoris\u00e9e et produit un masque bool\u00e9en d&rsquo;appartenance poss\u00e9dant la m\u00eame dimensionnalit\u00e9 que la structure de donn\u00e9es examin\u00e9e :<\/p>\n<p><code>ensemble_cibles = [42, 88, 105, 314]<\/code><br \/>\n<code>masque = np.isin(x, ensemble_cibles)<\/code><br \/>\n<code>indices_appartenance = np.where(masque)[0]<\/code><\/p>\n<p>Sous le capot, <code>np.isin()<\/code> applique diff\u00e9rentes strat\u00e9gies algorithmiques dynamiques en fonction du volume respectif des tableaux mis en jeu. Pour de grands tableaux, elle proc\u00e8de par tri pr\u00e9liminaire ou par des m\u00e9canismes de tables de hachage interne afin de pr\u00e9server une complexit\u00e9 quasi lin\u00e9aire, s&rsquo;affranchissant totalement du co\u00fbt combinatoire d&rsquo;une double boucle d&rsquo;inspection na\u00efve.<\/p>\n<h3>7.3 Filtrage logique complexe avec pr\u00e9dicats personnalis\u00e9s<\/h3>\n<p>La flexibilit\u00e9 des masques bool\u00e9ens permet d&rsquo;exprimer des crit\u00e8res d&rsquo;identification positionnelle d&rsquo;une haute complexit\u00e9 analytique, combinant propri\u00e9t\u00e9s arithm\u00e9tiques intrins\u00e8ques et contr\u00f4les d&rsquo;int\u00e9grit\u00e9 num\u00e9rique des donn\u00e9es. Par exemple, il est fr\u00e9quent en th\u00e9orie des signaux d&rsquo;extraire les coordonn\u00e9es des composantes d&rsquo;un vecteur satisfaisant \u00e0 la fois une r\u00e8gle de parit\u00e9 arithm\u00e9tique et un seuil d&rsquo;amplitude spatiale, tout en s&rsquo;assurant de la puret\u00e9 du signal face aux valeurs manquantes.<\/p>\n<p>Pour concevoir un tel pr\u00e9dicat, on peut conjuguer l&rsquo;arithm\u00e9tique modulo avec des fonctions de contr\u00f4le d&rsquo;\u00e9tat sp\u00e9cialis\u00e9es. L&rsquo;op\u00e9ration modulo vectoris\u00e9e s&rsquo;exprime au moyen du symbole <code>%<\/code>, g\u00e9n\u00e9rant un tableau de restes de division enti\u00e8re. Pour cibler des valeurs paires sup\u00e9rieures \u00e0 une amplitude donn\u00e9e, on \u00e9crira <code>(x % 2 == 0) &amp; (x &gt; amplitude)<\/code>.<\/p>\n<p>De surcro\u00eet, l&rsquo;analyse de flux de donn\u00e9es brutes impose fr\u00e9quemment de filtrer des anomalies num\u00e9riques telles que les ind\u00e9terminations math\u00e9matiques (not\u00e9es <code>NaN<\/code> pour <em>Not a Number<\/em>) ou les divergences vers l&rsquo;infini (not\u00e9es <code>Inf<\/code>). NumPy fournit des fonctions d&rsquo;interrogation vectoris\u00e9es d\u00e9di\u00e9es, \u00e0 l&rsquo;instar de <code>np.isnan()<\/code>, <code>np.isinf()<\/code> et <code>np.isfinite()<\/code>. L&rsquo;assemblage d&rsquo;un crit\u00e8re sophistiqu\u00e9 de localisation positionnelle s&rsquo;ex\u00e9cute ainsi :<\/p>\n<p><code>pr\u00e9dicat_complexe = np.isfinite(x) &amp; (x &gt; 50) &amp; (x % 2 == 0)<\/code><br \/>\n<code>indices_selectionnes = np.where(pr\u00e9dicat_complexe)[0]<\/code><\/p>\n<p>L&rsquo;avantage cardinal de cette formulation r\u00e9side dans son atomicit\u00e9 computationnelle : l&rsquo;ensemble des conditions est \u00e9valu\u00e9 au sein de couches C compil\u00e9es, minimisant les transitions de contexte entre la boucle d&rsquo;\u00e9valuation interne et l&rsquo;interpr\u00e9teur Python de haut niveau.<\/p>\n<h2>8. Fonctions sp\u00e9cialis\u00e9es compl\u00e9mentaires : np.nonzero, np.flatnonzero et np.argwhere<\/h2>\n<h3>8.1 R\u00f4le et sp\u00e9cificit\u00e9s de la fonction np.nonzero()<\/h3>\n<p>Bien que <code>np.where()<\/code> soit la m\u00e9thode la plus universellement identifi\u00e9e par les d\u00e9veloppeurs, il est essentiel de souligner la place occup\u00e9e par la fonction native <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.nonzero.html\"><code>np.nonzero()<\/code><\/a>. Sur le plan architectural, <code>np.nonzero()<\/code> est le v\u00e9ritable moteur algorithmique de bas niveau op\u00e9rant derri\u00e8re <code>np.where()<\/code> lorsque ce dernier est invoqu\u00e9 avec un param\u00e8tre conditionnel unique sans clauses d&rsquo;alternance ternaire. En d&rsquo;autres termes, <code>np.where(condition)<\/code> est un alias direct qui route formellement son ex\u00e9cution vers <code>condition.nonzero()<\/code>.<\/p>\n<p>La s\u00e9mantique math\u00e9matique de <code>np.nonzero(x)<\/code> consiste \u00e0 recenser l&rsquo;ensemble des indices o\u00f9 les \u00e9l\u00e9ments du tableau <em>x<\/em> sont strictement diff\u00e9rents de z\u00e9ro. Si <em>x<\/em> est un tableau num\u00e9rique, elle \u00e9quivaut \u00e0 \u00e9valuer <code>x != 0<\/code> ; si <em>x<\/em> est un tableau bool\u00e9en pr\u00e9existant \u2014 produit par exemple par une \u00e9tape ant\u00e9rieure de traitement logique \u2014, l&rsquo;appel \u00e0 <code>np.nonzero(masque)<\/code> identifie directement la position de toutes les valeurs <code>True<\/code>.<\/p>\n<p>L&rsquo;utilisation explicite de <code>np.nonzero()<\/code> pr\u00e9sente des atouts de clart\u00e9 s\u00e9mantique dans le code de calcul scientifique. Lorsque l&rsquo;on dispose d\u00e9j\u00e0 d&rsquo;un tableau d&rsquo;\u00e9tats logiques, invoquer <code>np.nonzero(masque)<\/code> \u00e9vite la redondance d&rsquo;un test relationnel superflu tel que <code>np.where(masque == True)<\/code>, qui aurait pour effet de recalculer une copie int\u00e9grale du masque bool\u00e9en, dilapidant inutilement des m\u00e9gaoctets de m\u00e9moire vive. De surcro\u00eet, sa signature de retour est strictement identique \u00e0 celle de <code>np.where()<\/code> : un tuple de tableaux dimensionnels d&rsquo;entiers, pr\u00eat pour l&rsquo;indexation avanc\u00e9e.<\/p>\n<h3>8.2 Uniformisation unidimensionnelle avec np.flatnonzero()<\/h3>\n<p>Lorsque le praticien manipule des structures de donn\u00e9es matricielles ou tensorielles mais d\u00e9sire s&rsquo;abstraire totalement de la d\u00e9composition multidimensionnelle pour ne retenir qu&rsquo;un adressage lin\u00e9aire simple, la biblioth\u00e8que met \u00e0 disposition la fonction hautement optimis\u00e9e <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.flatnonzero.html\"><code>np.flatnonzero()<\/code><\/a>. Cette directive combine conceptuellement l&rsquo;aplatissement virtuel du tableau et l&rsquo;extraction imm\u00e9diate des indices de non-nullit\u00e9.<\/p>\n<p>Consid\u00e9rons un tenseur d&rsquo;ordre 3 de dimensions (100, 100, 100). L&rsquo;application standard de <code>np.nonzero()<\/code> renverrait un tuple volumineux de trois tableaux distincts, mod\u00e9lisant les coordonn\u00e9es spatiales selon les axes X, Y et Z. Si le but du traitement consiste uniquement \u00e0 s\u00e9rialiser les positions actives ou \u00e0 quantifier les indices de mani\u00e8re s\u00e9quentielle, l&rsquo;invocation de <code>np.flatnonzero()<\/code> produit un <em>unique tableau unidimensionnel<\/em> d&rsquo;entiers repr\u00e9sentant les indices dans la version lin\u00e9aris\u00e9e (<em>flattened<\/em>) du tenseur :<\/p>\n<p><code>indices_lineaires = np.flatnonzero(tenseur == valeur_cible)<\/code><\/p>\n<p>Cette approche induit une \u00e9conomie substantielle de m\u00e9moire vive, car un seul tableau d&rsquo;entiers est instanci\u00e9 au lieu d&rsquo;un tuple multiple. De plus, elle simplifie drastiquement les traitements statistiques subs\u00e9quents, tels que les calculs d&rsquo;intervalles d&rsquo;espacement s\u00e9quentiel ou la transmission d&rsquo;adresses lin\u00e9aires \u00e0 des routines de bas niveau interfac\u00e9es en C ou CUDA. L&rsquo;indice plat ainsi obtenu peut \u00e0 tout moment \u00eatre retransform\u00e9 en coordonn\u00e9es g\u00e9om\u00e9triques compl\u00e8tes via un appel post\u00e9rieur \u00e0 <code>np.unravel_index()<\/code>.<\/p>\n<h3>8.3 Formatage g\u00e9om\u00e9trique des coordonn\u00e9es avec np.argwhere()<\/h3>\n<p>L&rsquo;une des asym\u00e9tries ergonomiques les plus courantes dans l&rsquo;exploitation de <code>np.where()<\/code> ou <code>np.nonzero()<\/code> r\u00e9side dans le format matriciel de la sortie. Comme \u00e9tabli, ces fonctions renvoient un tuple o\u00f9 chaque axe dispose de son propre vecteur de coordonn\u00e9es. Toutefois, dans de nombreux paradigmes de traitement d&rsquo;images, de g\u00e9om\u00e9trie computationnelle ou d&rsquo;inf\u00e9rence statistique, les algorithmes requi\u00e8rent que chaque point spatial soit encapsul\u00e9 sous la forme d&rsquo;un vecteur ligne distinct (par exemple, des coordonn\u00e9es spatiales [x, y, z] group\u00e9es point par point).<\/p>\n<p>La fonction <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.argwhere.html\"><code>np.argwhere()<\/code><\/a> a \u00e9t\u00e9 sp\u00e9cifiquement con\u00e7ue pour r\u00e9pondre \u00e0 cette exigence d&rsquo;agencement. Elle \u00e9value la non-nullit\u00e9 d&rsquo;un tableau ou d&rsquo;une expression relationnelle et r\u00e9organise imm\u00e9diatement le flux d&rsquo;indices sous la forme d&rsquo;une matrice bidimensionnelle de dimensions (<em>N<\/em>, <em>ndim<\/em>), o\u00f9 <em>N<\/em> quantifie le nombre d&rsquo;occurrences positives et <em>ndim<\/em> le nombre de dimensions du tableau d&rsquo;entr\u00e9e :<\/p>\n<p><code>matrice_2d = np.array([[0, 8], [8, 0]])<\/code><br \/>\n<code>points = np.argwhere(matrice_2d == 8)<\/code><\/p>\n<p>L&rsquo;affichage de la variable <code>points<\/code> met en \u00e9vidence une structure imm\u00e9diatement exploitable :<\/p>\n<p><code>array([[0, 1],<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[1, 0]])<\/code><\/p>\n<p>Chaque ligne de cette matrice repr\u00e9sente les coordonn\u00e9es spatiales directes d&rsquo;une occurrence. Ce formatage est id\u00e9al pour it\u00e9rer directement sur des listes de points via des boucles spatiales ou pour alimenter des mod\u00e8les d&rsquo;apprentissage g\u00e9om\u00e9trique (tels que l&rsquo;algorithme des k-plus proches voisins de Scikit-Learn). Il convient cependant de formuler une mise en garde technique : la sortie produite par <code>np.argwhere()<\/code> ne peut <em>pas<\/em> \u00eatre r\u00e9inject\u00e9e directement telle quelle pour indexer le tableau d&rsquo;origine <code>M[points]<\/code>, car NumPy interpr\u00e9terait cette syntaxe selon les r\u00e8gles complexes de l&rsquo;indexation avanc\u00e9e multidimensionnelle, provoquant des erreurs de conformation de dimensions.<\/p>\n<h2>9. \u00c9valuation comparative des performances et complexit\u00e9 computationnelle<\/h2>\n<h3>9.1 Protocole de benchmarking exp\u00e9rimental selon la taille des donn\u00e9es<\/h3>\n<p>Pour discriminer les domaines d&rsquo;excellence et les limites intrins\u00e8ques des diff\u00e9rentes approches explor\u00e9es, il est imp\u00e9ratif d&rsquo;\u00e9tablir un protocole de m\u00e9trologie informatique standardis\u00e9 et reproductible. Nous avons \u00e9labor\u00e9 un banc d&rsquo;essai exp\u00e9rimental ex\u00e9cut\u00e9 sous Python 3.11 avec NumPy 1.26 sur une architecture x86_64, exploitant le module standard <code>timeit<\/code> pour mesurer les latences d&rsquo;ex\u00e9cution avec une granularit\u00e9 micro-temporelle. Les tests ont \u00e9t\u00e9 men\u00e9s sur des vecteurs synth\u00e9tiques de nombres entiers de dimensions exponentielles : 10<sup>3<\/sup>, 10<sup>5<\/sup>, et 10<sup>7<\/sup> \u00e9l\u00e9ments contigus.<\/p>\n<p>Le protocole \u00e9value la performance de quatre sc\u00e9narios algorithmiques fondamentaux :<\/p>\n<ul>\n<li>Sc\u00e9nario A : Balayage exhaustif complet avec <code>np.where(x == val)[0]<\/code>.<\/li>\n<li>Sc\u00e9nario B : Recherche unitaire par court-circuit via une it\u00e9ration g\u00e9n\u00e9ratrice Python <code>next()<\/code>.<\/li>\n<li>Sc\u00e9nario C : Recherche unitaire compil\u00e9e via une boucle JIT Numba native.<\/li>\n<li>Sc\u00e9nario D : Recherche dichotomique combin\u00e9e <code>np.searchsorted()<\/code> sur tableau tri\u00e9.<\/li>\n<\/ul>\n<p>Les r\u00e9sultats empiriques r\u00e9v\u00e8lent des variations critiques de comportement. Pour un vecteur restreint de 1 000 \u00e9l\u00e9ments, l&rsquo;\u00e9cart absolu demeure infinit\u00e9simal, oscillant entre 1 microseconde (Numba) et 8 microsecondes (<code>np.where<\/code>). Cependant, d\u00e8s lors que la volum\u00e9trie atteint 10 000 000 d&rsquo;\u00e9l\u00e9ments (environ 80 m\u00e9gaoctets de donn\u00e9es), le gouffre de performance s&rsquo;\u00e9largit : le balayage exhaustif via <code>np.where()<\/code> requiert environ 9,2 millisecondes en raison du parcours complet de la m\u00e9moire et de l&rsquo;allocation du masque bool\u00e9en. Si la valeur cible se situe \u00e0 l&rsquo;indice 5, la m\u00e9thode Numba r\u00e9sout la recherche en 45 nanosecondes gr\u00e2ce \u00e0 l&rsquo;interruption pr\u00e9coce, r\u00e9alisant un gain d&rsquo;acc\u00e9l\u00e9ration d&rsquo;un facteur sup\u00e9rieur \u00e0 200 000.<\/p>\n<p>Ce protocole met \u00e9galement en lumi\u00e8re l&rsquo;impact de la taille des lignes de cache mat\u00e9rielles : lorsque la taille du tableau exc\u00e8de le cache L3 de la machine h\u00f4te (typiquement entre 16 et 32 m\u00e9gaoctets), le temps d&rsquo;ex\u00e9cution de <code>np.where()<\/code> cesse d&rsquo;\u00e9voluer de mani\u00e8re strictement lin\u00e9aire pour subir une inflexion p\u00e9nalisante induite par la saturation de la bande passante du bus m\u00e9moire syst\u00e8me.<\/p>\n<h3>9.2 Comparatif chiffr\u00e9 : Balayage lin\u00e9aire vs Tri pr\u00e9liminaire<\/h3>\n<p>Le choix architectural entre le balayage lin\u00e9aire classique (<code>np.where<\/code>) et la recherche dichotomique assist\u00e9e par tri indirect (<code>np.argsort<\/code> combin\u00e9 \u00e0 <code>np.searchsorted<\/code>) est d\u00e9termin\u00e9 par le volume de requ\u00eates concurrentes \u00e0 op\u00e9rer sur le m\u00eame r\u00e9f\u00e9rentiel de donn\u00e9es. Il existe un seuil de rentabilit\u00e9 analytique, formellement mod\u00e9lisable par l&rsquo;analyse de complexit\u00e9 algorithmique.<\/p>\n<p>Consid\u00e9rons un tableau de taille <em>n<\/em> et un contingent de <em>m<\/em> requ\u00eates d&rsquo;indices ind\u00e9pendantes :<\/p>\n<ul>\n<li>L&rsquo;approche par balayages lin\u00e9aires successifs pr\u00e9sente une complexit\u00e9 globale de O(m &times; n).<\/li>\n<li>L&rsquo;approche par tri pr\u00e9liminaire requiert une phase d&rsquo;initialisation en O(n log n) pour g\u00e9n\u00e9rer le vecteur <code>sorter<\/code>, suivie de <em>m<\/em> recherches dichotomiques ex\u00e9cut\u00e9es chacune en O(log n), soit un co\u00fbt total de O((n + m) log n).<\/li>\n<\/ul>\n<p>En r\u00e9solvant l&rsquo;in\u00e9quation th\u00e9orique des co\u00fbts temporels, le point de bascule (<em>breakeven point<\/em>) intervient lorsque :<\/p>\n<p>m &times; n &gt; (n + m) log<sub>2<\/sub>(n)<\/p>\n<p>Sur un tableau contenant un million d&rsquo;\u00e9l\u00e9ments (<em>n<\/em> = 10<sup>6<\/sup>), un balayage lin\u00e9aire \u00e9l\u00e9mentaire mobilise approximativement 1 milliseconde de calcul. Le tri complet via <code>np.argsort()<\/code> mobilise quant \u00e0 lui environ 75 millisecondes. Chaque recherche dichotomique unitaire via <code>np.searchsorted()<\/code> ne r\u00e9clame en revanche que 1,2 microseconde. D\u00e8s lors, le seuil de rentabilit\u00e9 est franchi d\u00e8s que le nombre de requ\u00eates <em>m<\/em> exc\u00e8de 76 interrogations. Au-del\u00e0 de ce chiffre, l&rsquo;investissement computationnel initial consenti pour ordonner le tableau devient hautement amorti, et l&rsquo;approche dichotomique supplante magistralement le balayage lin\u00e9aire par des gains de productivit\u00e9 de plusieurs ordres de grandeur.<\/p>\n<h3>9.3 Empreinte m\u00e9moire et strat\u00e9gies pour architectures contraintes<\/h3>\n<p>Dans le cadre du traitement de donn\u00e9es g\u00e9ospatiales, d&rsquo;imagerie satellitaire ou de physique des hautes \u00e9nergies, les structures <code>ndarray<\/code> atteignent fr\u00e9quemment des \u00e9chelles volum\u00e9triques de l&rsquo;ordre de plusieurs dizaines de gigaoctets. Dans ces conditions extr\u00eames, l&#8217;empreinte m\u00e9moire auxiliaire des fonctions d&rsquo;indexation devient le facteur dimensionnant qui pr\u00e9vient le d\u00e9clenchement des m\u00e9canismes d&rsquo;\u00e9viction du syst\u00e8me d&rsquo;exploitation (tueurs de processus OOM &#8211; <em>Out Of Memory<\/em>).<\/p>\n<p>Comme d\u00e9montr\u00e9 pr\u00e9c\u00e9demment, ex\u00e9cuter l&rsquo;instruction na\u00efve <code>indices = np.where(x == cible)[0]<\/code> sur un tableau de 16 gigaoctets engendre l&rsquo;allocation instantan\u00e9e d&rsquo;un tableau bool\u00e9en temporaire de 16 gigaoctets suppl\u00e9mentaires pour mat\u00e9rialiser le pr\u00e9dicat logique. Si la machine h\u00f4te dispose de 32 gigaoctets de m\u00e9moire vive, cette simple instruction provoque une saturation imm\u00e9diate de la m\u00e9moire disponible, risquant d&rsquo;entra\u00eener des m\u00e9canismes d&rsquo;\u00e9change sur disque (<em>swapping<\/em>) destructeurs pour les performances.<\/p>\n<p>Pour op\u00e9rer sur des architectures aux ressources contraintes, plusieurs strat\u00e9gies d&rsquo;optimisation bas niveau doivent \u00eatre d\u00e9ploy\u00e9es :<\/p>\n<p>La premi\u00e8re technique consiste \u00e0 recourir au partitionnement par blocs m\u00e9moires continus (<em>chunking<\/em>). En d\u00e9coupant virtuellement le tableau volumineux en sous-segments s\u00e9quentiels de taille calibr\u00e9e pour r\u00e9sider int\u00e9gralement dans le cache L3 du processeur, le masque bool\u00e9en est allou\u00e9 et d\u00e9sallou\u00e9 de fa\u00e7on it\u00e9rative par bloc, r\u00e9duisant l&#8217;empreinte m\u00e9moire temporaire \u00e0 quelques m\u00e9gaoctets.<\/p>\n<p>La seconde technique implique le sous-typage rigoureux des structures d&rsquo;indices. Par d\u00e9faut, NumPy g\u00e9n\u00e8re des tableaux d&rsquo;indices typ\u00e9s en <code>int64<\/code> (8 octets par position). Si la dimension maximale du tableau \u00e0 indexer ne d\u00e9passe pas 2 147 483 647 \u00e9l\u00e9ments, le type <code>int32<\/code> (4 octets) suffit amplement. Si le tableau comporte moins de 65 536 \u00e9l\u00e9ments, le type <code>uint16<\/code> (2 octets) divise l&#8217;empreinte m\u00e9moire par quatre par rapport au standard 64 bits. Ce contr\u00f4le strict du typage au moyen de l&rsquo;argument explicite <code>dtype<\/code> permet de concevoir des pipelines de calcul scientifique massifs sans saturer l&rsquo;infrastructure mat\u00e9rielle sous-jacente.<\/p>\n<h2>10. Robustesse et gestion des cas limites en environnement de production<\/h2>\n<h3>10.1 Traitement s\u00e9curis\u00e9 des valeurs absentes du tableau<\/h3>\n<p>L&rsquo;industrialisation de code scientifique exige une \u00e9tanch\u00e9it\u00e9 absolue face aux entr\u00e9es stochastiques ou aux valeurs cibles absentes de l&rsquo;espace d&rsquo;\u00e9chantillonnage. Comme explicit\u00e9 dans la section 3.2, l&rsquo;idiome <code>np.where(x == valeur)[0][0]<\/code> d\u00e9clenche une exception fatale <code>IndexError<\/code> d\u00e8s lors que la valeur recherch\u00e9e n&rsquo;existe pas dans le tableau. En environnement de production, les plantages d&rsquo;ex\u00e9cution non intercept\u00e9s nuisent directement \u00e0 la r\u00e9silience globale de l&rsquo;architecture logicielle.<\/p>\n<p>Pour garantir une robustesse logicielle irr\u00e9prochable conforme aux standards de g\u00e9nie logiciel, il est imp\u00e9ratif d&rsquo;encapsuler la d\u00e9tection d&rsquo;indices au sein de fonctions d\u00e9fensives formellement sp\u00e9cifi\u00e9es. L&rsquo;impl\u00e9mentation canonique d&rsquo;une fonction de recherche scalaire s\u00e9curis\u00e9e s&rsquo;articule autour de l&rsquo;usage des annotations de typage (<em>Type Hints<\/em>) et du renvoi explicite d&rsquo;une valeur sentinelle standardis\u00e9e lorsque la condition n&rsquo;est pas remplie :<\/p>\n<p>Consid\u00e9rons la conception de la fonction suivante :<\/p>\n<p><code>from typing import Optional<\/code><br \/>\n<code>import numpy as np<\/code><\/p>\n<p><code>def trouver_premier_indice(tableau: np.ndarray, valeur_cible: float) -&gt; Optional[int]:<\/code><br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;<code>indices = np.where(tableau == valeur_cible)[0]<\/code><br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;<code>if indices.size &gt; 0:<\/code><br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<code>return int(indices[0])<\/code><br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;<code>return None<\/code><\/p>\n<p>Cette encapsulation pr\u00e9sente de multiples vertus d&rsquo;ing\u00e9nierie : d&rsquo;une part, l&rsquo;instruction <code>indices.size &gt; 0<\/code> pr\u00e9vient tout acc\u00e8s ill\u00e9gal sur un conteneur vide. D&rsquo;autre part, la conversion explicite <code>int(indices[0])<\/code> transmute le type interne <code>np.int64<\/code> en un objet entier natif standard de Python, \u00e9liminant ainsi d&rsquo;\u00e9ventuelles incompatibilit\u00e9s de s\u00e9rialisation ult\u00e9rieure vers des formats tiers tels que JSON. Enfin, la signature <code>Optional[int]<\/code> documente sans \u00e9quivoque pour les outils d&rsquo;analyse statique de code (tels que MyPy) la possibilit\u00e9 intrins\u00e8que que l&rsquo;op\u00e9ration ne renvoie aucun indice, for\u00e7ant le d\u00e9veloppeur aval \u00e0 impl\u00e9menter un branchement conditionnel pour g\u00e9rer le cas <code>None<\/code>.<\/p>\n<h3>10.2 Gestion des valeurs manquantes (NaN) et infinis (Inf)<\/h3>\n<p>Le traitement des valeurs non d\u00e9finies (<code>np.nan<\/code>) constitue sans conteste l&rsquo;un des pi\u00e8ges logiques les plus redoutables de l&rsquo;\u00e9cosyst\u00e8me NumPy. Conform\u00e9ment aux axiomes de la norme arithm\u00e9tique IEEE 754, la valeur sp\u00e9ciale <code>NaN<\/code> repr\u00e9sente une entit\u00e9 non num\u00e9rique ind\u00e9termin\u00e9e qui poss\u00e8de la particularit\u00e9 math\u00e9matique d&rsquo;\u00eatre <em>non r\u00e9flexive<\/em> : elle n&rsquo;est \u00e9gale \u00e0 rien, pas m\u00eame \u00e0 elle-m\u00eame. Par cons\u00e9quent, l&rsquo;expression logique fondamentale <code>np.nan == np.nan<\/code> renvoie cat\u00e9goriquement la valeur bool\u00e9enne <code>False<\/code>.<\/p>\n<p>De fait, une tentative de localisation formul\u00e9e sous la forme <code>np.where(x == np.nan)[0]<\/code> est une h\u00e9r\u00e9sie algorithmique qui retournera invariablement un tableau vide, quelle que soit la quantit\u00e9 de valeurs manquantes r\u00e9sidant effectivement au sein du tableau <code>x<\/code>. Pour identifier avec certitude les positions spatiales de ces valeurs manquantes, il est imp\u00e9ratif d&rsquo;utiliser la fonction d\u00e9di\u00e9e <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.isnan.html\"><code>np.isnan()<\/code><\/a> :<\/p>\n<p><code>indices_valeurs_manquantes = np.where(np.isnan(x))[0]<\/code><\/p>\n<p>Une probl\u00e9matique connexe \u00e9merge lors de la manipulation de calculs divergents engendrant des infinis positifs (<code>np.inf<\/code>) ou n\u00e9gatifs (<code>-np.inf<\/code>), issus par exemple de divisions par z\u00e9ro non surveill\u00e9es. Contrairement \u00e0 <code>NaN<\/code>, les infinis respectent la r\u00e9flexivit\u00e9 (l&rsquo;expression <code>np.inf == np.inf<\/code> s&rsquo;\u00e9value \u00e0 <code>True<\/code>), mais leur localisation conjointe exige une rigueur m\u00e9thodique. L&rsquo;utilisation des primitives <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.isinf.html\"><code>np.isinf()<\/code><\/a> et <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.isfinite.html\"><code>np.isfinite()<\/code><\/a> permet d&rsquo;isoler avec une pr\u00e9cision absolue les singularit\u00e9s asymptotiques sans polluer les masques d&rsquo;indexation scalaires standards.<\/p>\n<h3>10.3 Stabilit\u00e9 du typage et coh\u00e9rence sur architectures 32-bit et 64-bit<\/h3>\n<p>La portabilit\u00e9 du code scientifique entre environnements de d\u00e9veloppement h\u00e9t\u00e9rog\u00e8nes (par exemple, entre des postes de prototypage locaux sous macOS ou Linux et des serveurs d&rsquo;inf\u00e9rence en conteneurs Docker ou syst\u00e8mes Windows) impose une vigilance aigu\u00eb quant \u00e0 la stabilit\u00e9 du typage des entiers d&rsquo;indexation. Dans l&rsquo;\u00e9cosyst\u00e8me NumPy, le type par d\u00e9faut des entiers allou\u00e9s pour h\u00e9berger des indices positionnels d\u00e9pend directement du mod\u00e8le de m\u00e9moire du syst\u00e8me d&rsquo;exploitation h\u00f4te et de l&rsquo;architecture du compilateur C sous-jacent.<\/p>\n<p>Sur la quasi-totalit\u00e9 des distributions Linux et macOS 64 bits contemporaines (mod\u00e8le LP64), les entiers standards correspondent au type <code>np.int64<\/code>. En revanche, sur les syst\u00e8mes d&rsquo;exploitation Microsoft Windows 64 bits (mod\u00e8le LLP64), les entiers natifs de langage C sont traditionnellement restreints \u00e0 32 bits, ce qui a longtemps induit des disparit\u00e9s de typage par d\u00e9faut lors de l&rsquo;allocation d&rsquo;indices avec <code>np.where()<\/code>. Bien que les versions r\u00e9centes de NumPy tendent \u00e0 harmoniser ce comportement vers des entiers 64 bits g\u00e9n\u00e9raux, des divergences d&rsquo;interop\u00e9rabilit\u00e9 persistent lors de l&rsquo;interfa\u00e7age avec des biblioth\u00e8ques C ou Cython tierces.<\/p>\n<p>Pour pr\u00e9munir une base de code contre tout dysfonctionnement multiplateforme, il est recommand\u00e9 de sp\u00e9cifier explicitement le type cible des indices lors des conversions ou des instanciations d&rsquo;allocations personnalis\u00e9es. L&rsquo;utilisation du type syst\u00e8me <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/arrays.scalars.html#numpy.intp\"><code>np.intp<\/code><\/a> est vivement pr\u00e9conis\u00e9e par les sp\u00e9cifications officielles de NumPy. Ce type d&rsquo;entier particulier est dimensionnellement garanti pour poss\u00e9der une capacit\u00e9 d&rsquo;adressage strictement identique \u00e0 celle des pointeurs m\u00e9moires de la plateforme h\u00f4te (32 bits sur une architecture 32 bits, et 64 bits sur une architecture 64 bits). Cette rigueur \u00e9limine tout risque de d\u00e9bordement arithm\u00e9tique (<em>integer overflow<\/em>) lors de la manipulation de coordonn\u00e9es au sein de structures volumineuses comprenant plusieurs milliards d&rsquo;\u00e9l\u00e9ments.<\/p>\n<h2>11. \u00c9tudes de cas appliqu\u00e9es : Analyse de s\u00e9ries temporelles et signaux<\/h2>\n<h3>11.1 Localisation temporelle de franchissements de seuils critiques<\/h3>\n<p>L&rsquo;analyse de s\u00e9ries temporelles biom\u00e9dicales, de flux de cours financiers ou de signaux issus de capteurs de vibration industrielle n\u00e9cessite fr\u00e9quemment de localiser avec exactitude les instants pr\u00e9cis o\u00f9 une m\u00e9trique physique franchit un seuil de criticit\u00e9 (<em>threshold crossing<\/em>). Il ne s&rsquo;agit pas simplement d&rsquo;identifier les indices o\u00f9 la valeur est sup\u00e9rieure au seuil, mais de rep\u00e9rer sp\u00e9cifiquement le moment de rupture : l&rsquo;instant pr\u00e9cis o\u00f9 le signal bascule d&rsquo;un \u00e9tat sous-critique \u00e0 un \u00e9tat supra-critique.<\/p>\n<p>Pour r\u00e9soudre ce probl\u00e8me de mani\u00e8re rigoureusement vectoris\u00e9e, l&rsquo;association de l&rsquo;\u00e9valuation relationnelle et de la diff\u00e9rentiation discr\u00e8te s\u00e9quentielle via la primitive <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/reference\/generated\/numpy.diff.html\"><code>np.diff()<\/code><\/a> s&rsquo;av\u00e8re d&rsquo;une efficacit\u00e9 chirurgicale. Consid\u00e9rons la d\u00e9marche suivante :<\/p>\n<p>Soit un vecteur temporel <code>signal<\/code> et un seuil critique scalaire <code>seuil<\/code>. L&rsquo;\u00e9valuation logique produit d&rsquo;abord un masque bool\u00e9en traduisant l&rsquo;\u00e9tat du syst\u00e8me \u00e0 chaque \u00e9chantillon : <code>etat = signal &gt; seuil<\/code>. Appliquer ensuite la fonction <code>np.diff(etat.astype(int))<\/code> calcule la d\u00e9riv\u00e9e discr\u00e8te de cette s\u00e9quence bool\u00e9enne convertie en entiers binaires (0 ou 1). Dans cette s\u00e9rie diff\u00e9rentielle :<\/p>\n<ul>\n<li>Une valeur de +1 signale une transition montante stricte (franchissement du seuil vers le haut).<\/li>\n<li>Une valeur de -1 signale une transition descendante stricte (retour sous le seuil).<\/li>\n<li>Une valeur de 0 indique une stabilit\u00e9 d&rsquo;\u00e9tat.<\/li>\n<\/ul>\n<p>L&rsquo;extraction des coordonn\u00e9es d&rsquo;impact temporel se r\u00e9sume alors \u00e0 interroger cette d\u00e9riv\u00e9e :<\/p>\n<p><code>indices_franchissements_montants = np.where(np.diff((signal &gt; seuil).astype(int)) == 1)[0] + 1<\/code><\/p>\n<p>L&rsquo;incr\u00e9ment final <code>+ 1<\/code> permet de compenser le d\u00e9calage g\u00e9om\u00e9trique introduit par <code>np.diff()<\/code> (dont le vecteur r\u00e9sultat compte un \u00e9l\u00e9ment de moins que le vecteur source), alignant ainsi parfaitement l&rsquo;indice obtenu sur l&rsquo;\u00e9chantillon exact mat\u00e9rialisant la bascule.<\/p>\n<h3>11.2 Alignement et synchronisation de plusieurs s\u00e9ries de donn\u00e9es<\/h3>\n<p>Dans les environnements industriels distribu\u00e9s ou les protocoles de surveillance IoT (<em>Internet of Things<\/em>), les donn\u00e9es physiques sont g\u00e9n\u00e9r\u00e9es par des capteurs h\u00e9t\u00e9rog\u00e8nes fonctionnant \u00e0 des fr\u00e9quences d&rsquo;\u00e9chantillonnage asynchrones et pr\u00e9sentant des d\u00e9rives d&rsquo;horloge. Pour op\u00e9rer une fusion sensorielle coh\u00e9rente, il est indispensable de synchroniser ces flux chronologiques distincts en alignant chaque \u00e9chantillon du flux secondaire sur l&rsquo;estampille temporelle la plus proche disponible dans le flux de r\u00e9f\u00e9rence.<\/p>\n<p>Cette r\u00e9conciliation chronologique se r\u00e9sout de mani\u00e8re optimale gr\u00e2ce \u00e0 la puissance conjointe de <code>np.searchsorted()<\/code> et de la correction de proximit\u00e9. Supposons que nous disposions d&rsquo;un vecteur d&rsquo;horodatages de r\u00e9f\u00e9rence rigoureusement ordonn\u00e9 <code>t_ref<\/code> et d&rsquo;un vecteur d&rsquo;horodatages asynchrones <code>t_async<\/code>. L&rsquo;appel \u00e0 :<\/p>\n<p><code>indices_insertion = np.searchsorted(t_ref, t_async)<\/code><\/p>\n<p>permet de localiser pour chaque point d&rsquo;interrogation l&rsquo;indice temporel de r\u00e9f\u00e9rence situ\u00e9 imm\u00e9diatement \u00e0 sa droite. Cependant, l&rsquo;\u00e9chantillon temporel le plus proche dans l&rsquo;absolu peut r\u00e9sider soit \u00e0 cette position d&rsquo;insertion, soit \u00e0 la position imm\u00e9diatement ant\u00e9rieure (\u00e0 sa gauche). Pour synchroniser avec une fid\u00e9lit\u00e9 maximale, il convient d&rsquo;op\u00e9rer une comparaison de distance m\u00e9trique minimale :<\/p>\n<p><code>idx_droite = np.clip(indices_insertion, 0, len(t_ref) - 1)<\/code><br \/>\n<code>idx_gauche = np.clip(indices_insertion - 1, 0, len(t_ref) - 1)<\/code><br \/>\n<code>distance_droite = np.abs(t_async - t_ref[idx_droite])<\/code><br \/>\n<code>distance_gauche = np.abs(t_async - t_ref[idx_gauche])<\/code><br \/>\n<code>indices_alignes = np.where(distance_droite &lt; distance_gauche, idx_droite, idx_gauche)<\/code><\/p>\n<p>Cette proc\u00e9dure vectoris\u00e9e compense instantan\u00e9ment la gigue temporelle (<em>jitter<\/em>) et garantit un alignement math\u00e9matique optimal de plusieurs s\u00e9ries asynchrones comportant des millions de points en quelques millisecondes seulement.<\/p>\n<h3>11.3 Rep\u00e9rage des coordonn\u00e9es spatiales d&rsquo;activations dans une matrice<\/h3>\n<p>Dans les flux de travail en traitement d&rsquo;images biom\u00e9dicales (IRM fonctionnelle, microscopie \u00e0 fluorescence) ou en vision par ordinateur, un cas d&rsquo;usage r\u00e9current r\u00e9side dans la d\u00e9tection g\u00e9om\u00e9trique et la segmentation de foyers d&rsquo;activation supra-liminaires au sein d&rsquo;une matrice bidimensionnelle ou d&rsquo;un volume vox\u00e9lique tridimensionnel.<\/p>\n<p>Consid\u00e9rons une matrice thermique ou statistique <code>carte_activation<\/code> issue d&rsquo;un syst\u00e8me d&rsquo;acquisition de donn\u00e9es physiques. L&rsquo;objectif analytique consiste \u00e0 rep\u00e9rer les coordonn\u00e9es cart\u00e9siennes de tous les pixels dont l&rsquo;intensit\u00e9 exc\u00e8de une valeur de coupure statistique <code>seuil_critique<\/code>, puis \u00e0 convertir imm\u00e9diatement ces coordonn\u00e9es spatiales discr\u00e8tes en coordonn\u00e9es m\u00e9triques r\u00e9elles (par exemple en millim\u00e8tres ou en microm\u00e8tres) calibr\u00e9es selon les sp\u00e9cifications physiques de l&rsquo;appareil de mesure.<\/p>\n<p>La mise en \u0153uvre de ce pipeline s&rsquo;articule comme suit :<\/p>\n<p><code>coords_y, coords_x = np.where(carte_activation &gt; seuil_critique)<\/code><\/p>\n<p>Une fois ces deux vecteurs coordonn\u00e9s extraits, la transformation vers l&rsquo;espace physique continu s&rsquo;ex\u00e9cute par une mise \u00e0 l&rsquo;\u00e9chelle affine directement vectoris\u00e9e :<\/p>\n<p><code>taille_pixel_x = 0.05  # r\u00e9solution de 50 microm\u00e8tres par pixel<\/code><br \/>\n<code>taille_pixel_y = 0.05<\/code><br \/>\n<code>x_physique = coords_x * taille_pixel_x<\/code><br \/>\n<code>y_physique = coords_y * taille_pixel_y<\/code><\/p>\n<p>En agr\u00e9geant ces composantes via <code>positions_metriques = np.column_stack((x_physique, y_physique))<\/code>, le chercheur obtient instantan\u00e9ment une matrice spatialis\u00e9e rigoureuse localisant les foyers d&rsquo;int\u00e9r\u00eat dans le rep\u00e8re m\u00e9trique du laboratoire. Ce processus illustre comment la manipulation directe des indices NumPy alimente les protocoles d&rsquo;analyse quantitative spatiale de haute pr\u00e9cision.<\/p>\n<h2>12. Guide d\u00e9cisionnel et bonnes pratiques de codage<\/h2>\n<h3>12.1 Arbre de d\u00e9cision algorithmique pour la recherche d&rsquo;indices<\/h3>\n<p>Face \u00e0 la diversit\u00e9 des topologies de donn\u00e9es et des objectifs computationnels, le choix d&rsquo;une m\u00e9thodologie d&rsquo;indexation ne doit jamais \u00eatre le fruit du hasard ou de l&rsquo;habitude empirique. Pour structurer ce choix de mani\u00e8re d\u00e9terministe, nous formalisons ci-dessous un arbre de d\u00e9cision algorithmique fond\u00e9 sur trois crit\u00e8res directeurs : la dimensionnalit\u00e9, la nature de la cible et le volume d&rsquo;appels r\u00e9currents.<\/p>\n<p><strong>R\u00e8gle 1 : Donn\u00e9es continues \u00e0 virgule flottante<\/strong><br \/>\nSi les donn\u00e9es analys\u00e9es appartiennent au domaine des nombres r\u00e9els (<code>float32<\/code>, <code>float64<\/code>), l&rsquo;\u00e9galit\u00e9 binaire stricte est formellement prohib\u00e9e. Deux alternatives s&rsquo;imposent :<\/p>\n<ul>\n<li>Si l&rsquo;on cherche des valeurs au sein d&rsquo;une marge d&rsquo;erreur d\u00e9termin\u00e9e, employer <code>np.where(np.isclose(x, cible, atol, rtol))[0]<\/code>.<\/li>\n<li>Si l&rsquo;on cherche l&rsquo;\u00e9l\u00e9ment le plus proche sans garantie de distance minimale, employer <code>np.argmin(np.abs(x - cible))<\/code>.<\/li>\n<\/ul>\n<p><strong>R\u00e8gle 2 : Donn\u00e9es discr\u00e8tes et recherche de premi\u00e8re occurrence unitaire<\/strong><br \/>\nSi le tableau est de type entier ou cat\u00e9goriel et que seule la premi\u00e8re position est requise :<\/p>\n<ul>\n<li>Si le volume de donn\u00e9es est mod\u00e9r\u00e9 ou si la cible est pr\u00e9sum\u00e9e lointaine, employer <code>np.where(x == cible)[0]<\/code> s\u00e9curis\u00e9 par un contr\u00f4le de taille <code>size &gt; 0<\/code>.<\/li>\n<li>Si le tableau est massif et que la cible a de fortes probabilit\u00e9s d&rsquo;appara\u00eetre pr\u00e9cocement, d\u00e9ployer une fonction compil\u00e9e avec Numba pour b\u00e9n\u00e9ficier d&rsquo;un arr\u00eat pr\u00e9coce \u00e0 co\u00fbt m\u00e9moire nul.<\/li>\n<\/ul>\n<p><strong>R\u00e8gle 3 : Recherches multiples ou tableaux d\u00e9j\u00e0 ordonn\u00e9s<\/strong><br \/>\nSi le tableau source est d\u00e9j\u00e0 tri\u00e9 par construction, ou si l&rsquo;on doit soumettre un contingent volumineux de cibles r\u00e9currentes (<em>m<\/em> &gt; 100) :<\/p>\n<ul>\n<li>Abandonner imp\u00e9rativement le balayage lin\u00e9aire.<\/li>\n<li>Mobiliser syst\u00e9matiquement <code>np.searchsorted()<\/code> coupl\u00e9 \u00e0 un tri indirect pr\u00e9alable <code>np.argsort()<\/code> si le tableau d&rsquo;origine n&rsquo;\u00e9tait pas initialement tri\u00e9.<\/li>\n<\/ul>\n<h3>12.2 R\u00e8gles d&rsquo;\u00e9criture d&rsquo;un code Python\/NumPy propre, lisible et maintenable<\/h3>\n<p>La maintenabilit\u00e9 \u00e0 long terme des bases de code scientifique requiert une discipline stylistique rigoureuse, pr\u00e9venant l&rsquo;apparition de constructions cryptiques et d&rsquo;erreurs d&rsquo;interpr\u00e9tation g\u00e9om\u00e9trique. Dans cette optique, l&rsquo;adoption des standards de codage conformes \u00e0 la sp\u00e9cification <a rel=\"noopener noreferrer\" href=\"https:\/\/peps.python.org\/pep-0008\/\">PEP 8<\/a> s&rsquo;accompagne de directives propres au calcul matriciel :<\/p>\n<p>Premi\u00e8rement, il convient de bannir formellement les noms de variables anonymes et laconiques pour stocker les sorties multidimensionnelles. Une \u00e9criture telle que <code>a, b = np.where(matrice == 1)<\/code> doit \u00eatre imp\u00e9rativement proscrite au profit d&rsquo;identifiants auto-document\u00e9s explicitant les dimensions r\u00e9elles de l&rsquo;espace de mod\u00e9lisation, \u00e0 l&rsquo;instar de <code>idx_lignes, idx_colonnes = np.where(matrice == 1)<\/code> ou <code>idx_z, idx_y, idx_x = np.where(volume == 1)<\/code>. Cette convention neutralise les inversions d&rsquo;axes qui constituent l&rsquo;une des causes les plus courantes d&rsquo;anomalies en traitement d&rsquo;images.<\/p>\n<p>Deuxi\u00e8mement, la d\u00e9composition des pr\u00e9dicats bool\u00e9ens complexes en propositions interm\u00e9diaires nomm\u00e9es est vivement recommand\u00e9e. Plut\u00f4t que de condenser une s\u00e9rie d&rsquo;op\u00e9rations logiques au sein d&rsquo;une unique ligne dense et illisible, scinder le traitement en \u00e9tapes s\u00e9mantiques renforce consid\u00e9rablement la clart\u00e9 :<\/p>\n<p><code># Construction interm\u00e9diaire claire et maintenable<\/code><br \/>\n<code>masque_validite = np.isfinite(signal)<\/code><br \/>\n<code>masque_amplitude = signal &gt; seuil_tension<\/code><br \/>\n<code>indices_evenements = np.where(masque_validite &amp; masque_amplitude)[0]<\/code><\/p>\n<p>Enfin, l&rsquo;enrichissement syst\u00e9matique des d\u00e9clarations de fonctions par des annotations de type statique pour les structures de tenseurs \u2014 par exemple en utilisant <code>numpy.typing.NDArray<\/code> \u2014 garantit une tra\u00e7abilit\u00e9 rigoureuse des signatures logicielles, facilitant la collaboration multidisciplinaire entre analystes de donn\u00e9es, chercheurs et ing\u00e9nieurs logiciels.<\/p>\n<h3>12.3 \u00c9volution vers les architectures acc\u00e9l\u00e9r\u00e9es (GPU et parall\u00e9lisation)<\/h3>\n<p>\u00c0 mesure que la volum\u00e9trie des ensembles de donn\u00e9es scientifiques progresse vers les p\u00e9taoctets, les limites physiques du traitement s\u00e9quentiel et vectoriel sur processeur central (CPU) deviennent manifestes. L&rsquo;\u00e9cosyst\u00e8me Python a vu \u00e9merger de nouvelles architectures logicielles capables de transposer les paradigmes d&rsquo;indexation vectoris\u00e9e de NumPy sur des architectures massivement parall\u00e8les, en particulier les processeurs graphiques (GPU).<\/p>\n<p>La biblioth\u00e8que <a rel=\"noopener noreferrer\" href=\"https:\/\/cupy.dev\/\">CuPy<\/a> constitue l&rsquo;h\u00e9riti\u00e8re directe de NumPy pour les architectures GPU exploitant les technologies NVIDIA CUDA et AMD ROCm. L&rsquo;un des atouts cardinaux de CuPy r\u00e9side dans son adh\u00e9sion quasi chirurgicale \u00e0 l&rsquo;interface de programmation (API) de NumPy : les fonctions <code>cp.where()<\/code>, <code>cp.nonzero()<\/code> et <code>cp.searchsorted()<\/code> s&rsquo;ex\u00e9cutent avec des signatures syntaxiques rigoureusement identiques \u00e0 leurs homologues CPU. Cependant, sous le capot, l&rsquo;\u00e9valuation du masque d&rsquo;\u00e9galit\u00e9 et la d\u00e9tection d&rsquo;indices sont distribu\u00e9es sur plusieurs milliers de c\u0153urs de calcul matriciels, r\u00e9duisant le temps de traitement de tableaux de plusieurs dizaines de gigaoctets \u00e0 quelques microsecondes.<\/p>\n<p>Pour le traitement distribu\u00e9 sur des grappes de serveurs (<em>clusters<\/em>), l&rsquo;int\u00e9gration au sein de frameworks tels que <a rel=\"noopener noreferrer\" href=\"https:\/\/www.dask.org\/\">Dask Array<\/a> permet de parall\u00e9liser l&rsquo;indexation sur des structures de donn\u00e9es r\u00e9parties qui exc\u00e8dent la m\u00e9moire physique d&rsquo;une machine individuelle. Dask d\u00e9coupe les structures massives en blocs NumPy distincts et orchestre un graphe de t\u00e2ches asynchrone pour localiser les indices locaux avant de les recomposer dans l&rsquo;espace global.<\/p>\n<p>Enfin, des biblioth\u00e8ques novatrices de calcul diff\u00e9rentiable et de compilation \u00e0 la vol\u00e9e telles que <a rel=\"noopener noreferrer\" href=\"https:\/\/jax.readthedocs.io\/\">JAX<\/a> red\u00e9finissent la recherche de valeurs en contraignant les d\u00e9veloppeurs \u00e0 abandonner l&rsquo;indexation dynamique d\u00e9pendante des donn\u00e9es (laquelle induit des variations de formes dimensionnelles incompatibles avec l&rsquo;optimisation des compilateurs XLA) au profit de masquages fonctionnels purement statiques. La ma\u00eetrise des fondamentaux th\u00e9oriques de l&rsquo;indexation vectorielle expos\u00e9s tout au long de cet ouvrage constitue ainsi le pr\u00e9requis indispensable pour appr\u00e9hender avec succ\u00e8s les futures r\u00e9volutions technologiques du calcul haute performance.<\/p>\n<h2>R\u00e9f\u00e9rences<\/h2>\n<p>Harris, C. R., Millman, K. J., van der Walt, S. J., Gommers, R., Virtanen, P., Cournapeau, D., Wieser, E., Taylor, J., Berg, S., Smith, N. J., Kern, R., Picus, M., Hoyer, S., van Kerkwijk, M. H., Brett, M., Haldane, A., del R&iacute;o, J. F., Wiebe, M., Peterson, P., &hellip; Oliphant, T. E. (2020). Array programming with NumPy. <em>Nature<\/em>, 585(7825), 357\u2013362. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1038\/s41586-020-2649-2\">https:\/\/doi.org\/10.1038\/s41586-020-2649-2<\/a><\/p>\n<p>IEEE. (2019). <em>IEEE Standard for Floating-Point Arithmetic<\/em> (IEEE Std 754-2019). IEEE. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1109\/IEEESTD.2019.8766229\">https:\/\/doi.org\/10.1109\/IEEESTD.2019.8766229<\/a><\/p>\n<p>Lam, P. K., Pitrou, A., &amp; Seibert, S. (2015). Numba: A LLVM-based Python JIT compiler. <em>Proceedings of the Second Workshop on the LLVM Compiler Infrastructure in HPC<\/em>, 1\u20136. <a rel=\"noopener noreferrer\" href=\"https:\/\/doi.org\/10.1145\/2833157.2833162\">https:\/\/doi.org\/10.1145\/2833157.2833162<\/a><\/p>\n<p>NumPy Developers. (2023). <em>NumPy user guide and reference manual (Release 1.26)<\/em>. NumPy.org. <a rel=\"noopener noreferrer\" href=\"https:\/\/numpy.org\/doc\/stable\/\">https:\/\/numpy.org\/doc\/stable\/<\/a><\/p>\n<p>Oliphant, T. E. (2006). <em>A guide to NumPy<\/em>. Trelgol Publishing.<\/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\u201330. <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 identifier l&rsquo;indice d&rsquo;une ou plusieurs valeurs dans un tableau NumPy : np.where, np.searchsorted et optimisations de calcul.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","template":"","categories":[4351,6696],"tags":[1884,2470,6697,2960,2468],"class_list":["post-4730","psych_stat","type-psych_stat","status-publish","hentry","category-programmation-scientifique","category-python-et-analyse-de-donnees","tag-analyse-de-donnees","tag-indexation","tag-np-where","tag-numpy","tag-python"],"_links":{"self":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/4730","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=4730"}],"version-history":[{"count":0,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=\/wp\/v2\/psych_stat\/4730\/revisions"}],"wp:attachment":[{"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4730"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4730"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/french.arabpsychology.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4730"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}