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.
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.
pip install numpyL'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 point | Liste Python | Tableau NumPy |
|---|---|---|
| Contenu | N'importe quels objets, mélangés | Un seul type, fixé à la création |
| L'opération a + b | Met les deux séquences bout à bout | Additionne terme à terme |
| Ajouter un élément | Immédiat, la liste s'étend | Recopie tout le tableau |
| Calcul sur un million de valeurs | Une boucle interprétée | Une opération compilée |
| Deux dimensions | Des listes de listes, gérées à la main | Une 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.
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]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
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.
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.
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.