NumPy : calculer sur des tableaux de nombres en Python

NumPy ajoute à Python le tableau de nombres : un même calcul appliqué à des millions de valeurs d'un coup, sans écrire la moindre boucle.
6 min de lecture
Believemy logo

Définition

Additionner deux colonnes de mille nombres avec une boucle Python fonctionne bien ; sur un million, la même boucle ralentit. NumPy ajoute à Python ce qui manquait pour ce genre de calcul : un tableau de nombres, homogène et de taille fixe, dont toutes les valeurs partagent le même type et occupent un seul bloc de mémoire. Une opération s'applique alors à des millions d'éléments d'un coup, sans écrire la moindre boucle.

PYTHON
import numpy as np

celsius = np.array([18.2, 21.5, 19.8, 23.1])
fahrenheit = celsius * 9 / 5 + 32

print(fahrenheit)
# [64.76 70.7  67.64 73.58]

La ligne de conversion vaut pour quatre valeurs comme pour quatre millions : décrite une fois, elle s'applique à tout le tableau. Cette écriture porte un nom, la vectorisation, la raison d'être de la bibliothèque.

NumPy ne fait pas partie de la bibliothèque standard. Elle s'installe avec pip, de préférence dans un environnement virtuel propre au projet.

BASH
pip install numpy

L'import sous le surnom np, avec le mot-clé as, est une convention si universelle que tout le code publié la suit. L'écrire autrement rend une documentation entière difficile à relire.


Le problème qu'il résout

Une liste Python accepte n'importe quoi : des entiers, des chaînes, d'autres listes. Chaque nombre y est un objet complet posé quelque part en mémoire, et la liste ne retient que son adresse. Additionner deux listes d'un million de valeurs oblige donc l'interpréteur à suivre deux millions d'adresses, à vérifier le type de chaque objet rencontré, puis à fabriquer un objet neuf pour chaque résultat : trois étapes, répétées un million de fois, pour une seule addition.

Un tableau NumPy supprime ces trois coûts en même temps. Le type est décidé une fois pour toutes, les valeurs se suivent dans un bloc contigu, et la boucle réelle tourne en C, hors de l'interpréteur. Sur un million d'éléments, l'écart avec une boucle écrite en Python se compte en dizaines de fois. Ce n'est pas une optimisation de confort : c'est la différence entre un résultat immédiat et un calcul qu'on lance avant d'aller chercher un café.


Face à la liste Python

La comparaison honnête ne désigne pas un vainqueur : les deux structures répondent à des besoins différents, et le tableau perd ce que la liste avait de commode. Le détail, point par point :

Sur ce pointListe PythonTableau NumPy
ContenuN'importe quels objets, mélangésUn seul type, fixé à la création
L'opération a + bMet les deux séquences bout à boutAdditionne terme à terme
Ajouter un élémentImmédiat, la liste s'étendRecopie tout le tableau
Calcul sur un million de valeursUne boucle interprétéeUne opération compilée
Deux dimensionsDes listes de listes, gérées à la mainUne forme native, lignes et colonnes

La deuxième ligne est celle qui surprend le plus : sur des listes, le signe plus met bout à bout, sur des tableaux, il additionne terme à terme. Le même symbole, deux opérations sans aucun rapport. C'est la première chose à vérifier quand un résultat sort deux fois trop long, et la longueur mesurée avec len dit tout de suite lequel des deux comportements s'est appliqué.


Le piège du premier jour

Découper un tableau ne le recopie pas : là où le slicing d'une liste rend une liste neuve, celui d'un tableau rend une vue, une fenêtre ouverte sur les mêmes données. Modifier la vue modifie donc l'original.

PYTHON
import numpy as np

mesures = np.array([10, 20, 30, 40])
extrait = mesures[1:3]
extrait[0] = 999  # modifie aussi mesures, pas une copie

print(mesures)
# [ 10 999  30  40]
Attention

Une fonction qui reçoit un extrait de tableau et le modifie « pour travailler dessus » modifie en réalité les données d'origine. Le bogue apparaît plus loin, là où l'original est relu, pas ici.

Le comportement est voulu : il évite de dupliquer la mémoire d'un tableau énorme pour n'en travailler qu'une portion. La méthode copy donne une vraie copie, le réflexe à prendre avant de modifier un extrait.

Le second contretemps vient du type fixe : un tableau d'entiers reste un tableau d'entiers, et y ranger 3.7 ne lève aucune ValueError, la valeur est tronquée à 3 en silence. Créer le tableau à partir de nombres à virgule évite de chercher longtemps d'où viennent des résultats faux mais plausibles.


Quand il ne sert à rien

Sur quelques centaines de valeurs, la conversion en tableau coûte plus cher que le calcul économisé : une boucle ordinaire suffit et se lit mieux. La bibliothèque n'apporte rien non plus sur des données hétérogènes, comme un fichier dont chaque ligne mêle un nom, une date et un montant. Pour cela, pandas est la bonne porte d'entrée : les colonnes d'un tableau de données pandas sont des tableaux NumPy déguisés.

Calculer n'est pas montrer, non plus. NumPy ne trace aucune courbe : c'est le rôle de matplotlib, qui accepte directement les tableaux et forme avec lui le duo le plus courant. Sur du texte, des dictionnaires imbriqués ou une réponse venue d'une interface web, la bibliothèque standard reste plus directe. L'erreur classique consiste à installer NumPy par réflexe, pour finir par ranger des chaînes de caractères dans un tableau conçu pour des nombres.


Questions fréquentes

Question

Faut-il apprendre NumPy avant pandas ?

Pas obligatoirement, mais quelques heures y sont vite rentabilisées. Tableau, forme, type fixe, sélection par condition : ces notions reviennent presque à l'identique dans pandas, sur des colonnes nommées.

Question

Pourquoi parcourir un tableau élément par élément est-il plus lent qu'une liste ?

Parce que chaque accès reconstruit un objet Python autour d'une valeur brute, un travail que la liste n'a pas puisqu'elle stockait déjà des objets. La réponse n'est donc pas de déplacer la boucle mais de la supprimer, en écrivant l'opération sur le tableau entier.

Question

NumPy remplace-t-il le module math de la bibliothèque standard ?

Non, les deux ne visent pas la même échelle. Le module math travaille sur un nombre à la fois et reste le plus rapide dans ce cas précis. Les fonctions de NumPy portent souvent le même nom, mais s'appliquent à un tableau entier : inutilement lourdes sur une valeur isolée.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.