Tutoriel pour apprendre NumPy

La bibliothèque de calcul numérique de Python

Gabor Laszlo Hajba

Traducteur

Laurent Rosenfeld

Ce tutoriel est le premier d'une série de tutoriels présentant NumPy, la bibliothèque de calcul numérique de Python.

Commentez

- Titre : Tutoriel pour apprendre NumPy

- Auteur : Gabor Laszlo Hajba

- Traducteur : Laurent Rosenfeld

- Parution : 15 mai 2017

- Mise à jour : 15 mai 2017

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2017 Gabor Laszlo Hajba. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Introduction

- 1-1. Installation de NumPy

- 2. Création de tableaux NumPy

- 3. Accéder aux éléments d'un tableau

- 4. Chargement des données

- 5. Conclusion

- 6. Remerciements

1 - Introduction

Dans ce tutoriel, nous allons commencer à étudier NumPy, une bibliothèque destinée aux calculs numériques en Python.

Pour quelles raisons pourriez-vous vouloir vous intéresser à NumPy ? D'abord, si vous désirez faire de la science des données ou de l'apprentissage automatique (machine learning), NumPy est d'une valeur inestimable. NumPy est utilisé pour effectuer des calculs sur de gros volumes de données. Mais pour comprendre des sujets tels que l'apprentissage automatique, il faut d'abord comprendre quelques notions sous-jacentes fondamentales.

Ce tutoriel est le premier d'une série de tutoriels qui vous guideront depuis les bases jusqu'au sommet de la science des données.

1-1 - Installation de NumPy

Il est très simple d'installer NumPy. Il suffit l'appeler l'utilitaire Python Package Index (PIP) :

pip install numpy

C'est tout ! Si vous désirez vous former à l'apprentissage automatique, alors vous pouvez vous procurer l'un des paquetages précompilés comme Anaconda qui comprend les bibliothèques nécessaires, y compris NumPy.

2 - Création de tableaux NumPy

Après cette brève introduction, il est temps de voir de quoi est capable NumPy. Nous allons passer en revue quelques exemples en commençant avec des choses simples, puis nous progresserons afin d'obtenir l'expérience nécessaire pour comprendre la puissance de NumPy.

Pour commencer, définissons une simple liste Python contenant quelques valeurs :

>>> values = [1, 2.4, 234, 112, 345]

Pour l'instant, ce tableau est une simple liste Python traditionnelle, inutilisable avec NumPy. Convertissons-la au bon format :

>>> import numpy as np
>>> array = np.array(values)

Comme vous pouvez le constater, on utilise simplement la fonction np.array. Vous remarquerez également que la terminologie diffère : NumPy utilise le terme array (tableau) au lieu de celui de liste. Cela ne pose pas de problème, car les listes Python sont en définitive des tableaux (ou du moins sont ce que l'on appelle des tableaux dans d'autres langages de programmation comme C ou Java).

Nous pouvons maintenant faire différentes sortes de chose avec ce nouveau tableau.

>>> array
array([   1. ,    2.4,  234. ,  112. ,  345. ])
>>> print(array)
[   1.     2.4  234.   112.   345. ]
>>> array.dtype
dtype('float64')

La première chose intéressante à remarquer est la façon dont les tableaux sont représentés lorsqu'on les affiche à la console : les valeurs sont séparées par des espaces et réparties uniformément en fonction de la plus longue des valeurs. Une observation plus intéressante est que chaque nombre est maintenant un nombre à virgule flottante, bien qu'il n'y eût qu'un seul nombre à virgule flottante dans la liste d'origine. Et nous pouvons le vérifier en affichant le type (dtype) de notre nouveau tableau.

En NumPy, les tableaux ont une « forme » (shape). La forme décrit la dimension d'un tableau :

>>> array.shape
(5,)

Le tableau de notre exemple a une seule dimension et comprend cinq éléments. NumPy est un système complexe qui peut gérer également des dimensions multiples, comme nous allons bientôt le voir.

Parfois, il serait difficile de créer un tableau en convertissant une liste comme nous venons de le faire. Ce serait notamment le cas pour des tableaux multidimensionnels. Il existe fort heureusement d'autres façons de créer des tableaux sans passer par une liste Python.

L'une de ces méthodes réarrange un intervalle de nombres en tableaux NumPy. C'est pratique pour donner des exemples, mais il est assez rare dans la réalité d'avoir des intervalles de valeurs à convertir en tableau.

>>> np.arange(25)
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24])
>>> np.arange(25).reshape(5,5)
array([[ 0,  1,  2,  3,  4],
[ 5,  6,  7,  8,  9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])

La fonction arange crée un tableau à partir d'un intervalle de valeurs (ici, la valeur inférieure de l'intervalle est implicitement zéro). Mais la méthode vraiment intéressante est reshape, qui donne au tableau une nouvelle forme ayant le bon nombre de dimensions. Dans ce cas, vous devez faire attention de donner une forme correcte au tableau, parce que vous obtiendrez une exception si le tableau ne tient pas correctement dans la forme voulue :

>>> np.arange(24).reshape(5,5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: cannot reshape array of size 24 into shape (5,5)

Ici, nous avons essayé d'ajouter 24 éléments à un tableau à deux dimensions 5x5 (on parle aussi de matrice à deux dimensions), mais cela ne fonctionne pas parce que nous avons besoin de 25 éléments.

Pour aller plus loin que cet exemple, vous pouvez créer des tableaux de valeurs uniformément réparties, même avec des intervalles non entiers entre les éléments. Supposons par exemple que vous vouliez une suite de nombres compris entre 10 et 20 avec un intervalle de 0,4 entre chaque. Il n'est pas aisé de créer une telle liste en Python, mais c'est très facile avec NumPy :

>>> range(10, 20, 0.4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
>>> list(map(lambda x: x/10, range(100, 200, 4)))
[10.0, 10.4, 10.8, 11.2, 11.6, 12.0, 12.4, 12.8, 13.2, 13.6, 14.0, 14.4, 14.8, 15.2, 15.6, 16.0, 16.4, 16.8, 17.2, 17.6, 18.0, 18.4, 18.8, 19.2, 19.6]
>>> np.arange(10, 20, 0.4)
array([ 10. ,  10.4,  10.8,  11.2,  11.6,  12. ,  12.4,  12.8,  13.2,
13.6,  14. ,  14.4,  14.8,  15.2,  15.6,  16. ,  16.4,  16.8,
17.2,  17.6,  18. ,  18.4,  18.8,  19.2,  19.6])

On voit qu'un intervalle de 0,4 n'est pas valide avec la fonction range de Python. J'ai donc dû employer un stratagème pour contourner le problème en Python : créer une liste d'entiers dix fois plus grands et les diviser ensuite par 10 dans un map, au risque d'introduire des erreurs de conversion. Aucune difficulté de ce genre avec NumPy.

Supposons maintenant que nous ayons besoin d'une matrice tridimensionnelle 3x3x3 remplie de chiffres 1. Connaissant Python, nous pouvons trouver rapidement une solution qui pourra être réutilisée ultérieurement pour créer d'autres jeux de données à l'avenir.

>>> np.array([1]*3*3*3).reshape(3,3,3)
array([[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]],

[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]],

[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]]])

Cependant, NumPy a une méthode interne pour ce genre de situation. Je recommande que vous utilisiez cette méthode plutôt que la conversion de Python en NumPy employée ci-dessus :

>>> np.ones((3,3,3))
array([[[ 1.,  1.,  1.],
[ 1.,  1.,  1.],
[ 1.,  1.,  1.]],

[[ 1.,  1.,  1.],
[ 1.,  1.,  1.],
[ 1.,  1.,  1.]],

[[ 1.,  1.,  1.],
[ 1.,  1.,  1.],
[ 1.,  1.,  1.]]])

Ceci crée une matrice 3D de nombres à virgule flottante. Si vous voulez des entiers, il vous faut fournir le type de donnée voulu en paramètre à la fonction ones :

>>> np.ones((3,3,3), dtype=np.int64)
array([[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]],

[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]],

[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]]])

La classe np.int64 définit des entiers sur 64 bits compatibles avec Python. Naturellement, il y a d'autres types d'entiers disponibles, mais je suggère celui-ci pour maintenir la compatibilité avec Python. Il y a bien sûr un inconvénient : cela nécessite plus de place que par exemple des entiers sur 16 bits.

Outre la fonction ones, il existe une fonction zeros, qui remplit la matrice avec des zéros, et la fonction empty, qui crée un tableau vide (ou du moins vide au sens NumPy) :

>>> empty_array = np.empty((3,6))
>>> empty_array
array([[  0.00000000e+000,   0.00000000e+000,   2.16211348e-314,
2.16211778e-314,   2.16211782e-314,   2.16211787e-314],
[  2.16211791e-314,   2.16211795e-314,   2.16211800e-314,
2.16211804e-314,   2.16211808e-314,   2.16211813e-314],
[  2.16211817e-314,   2.16211821e-314,   2.16211826e-314,
2.16211830e-314,   2.16211834e-314,   2.16211839e-314]])

Comme vous pouvez le constater, ce tableau contient des éléments extrêmement petits, si bien qu'il n'est pas vraiment vide. Donc, pensez-y et faites attention à chaque fois que vous utilisez la fonction empty.

3 - Accéder aux éléments d'un tableau

Maintenant que nous avons des tableaux, nous aimerions accéder à leurs éléments. Il y a deux façons de le faire. La première est la façon traditionnelle en Python, en utilisant des indices :

>>> array = np.arange(10, 40, 0.4).reshape(5,5,3)
>>> array[1][2][2]
19.20000000000001

Cela fonctionne bien tant que vous utilisez un nombre réduit de dimensions. Mais si le nombre de dimensions croît, vous allez devoir écrire beaucoup de crochets autour de vos indices. NumPy fournit une solution simple à ce petit problème : vous pouvez accéder aux éléments par leur indice, mais en fournissant les indices correspondant aux différentes dimensions sous la forme d'une liste de valeurs séparées par des virgules :

>>> array[1,2,2]
19.20000000000001

Parfois, vous ne désirez pas accéder à un seul champ de votre jeu de données, mais des lignes ou des colonnes entières. Il suffit pour cela d'utiliser les tranches de Python :

>>> array = np.arange(10, 40, 0.4).reshape(5,5,3)
>>> array[:, 2, 2]
array([ 13.2,  19.2,  25.2,  31.2,  37.2])

Dans cet exemple, nous avons pris la troisième colonne d'une ligne sur deux de chaque matrice. Si nous nous intéressons à la première ligne de chaque matrice, nous pouvons nous y prendre comme suit :

>>> array[:, 0, :]
array([[ 10. ,  10.4,  10.8],
[ 16. ,  16.4,  16.8],
[ 22. ,  22.4,  22.8],
[ 28. ,  28.4,  28.8],
[ 34. ,  34.4,  34.8]])
>>> array[:, 0]
array([[ 10. ,  10.4,  10.8],
[ 16. ,  16.4,  16.8],
[ 22. ,  22.4,  22.8],
[ 28. ,  28.4,  28.8],
[ 34. ,  34.4,  34.8]])

On constate qu'il est possible d'omettre le caractère deux-points si l'on désire tous les éléments de la dernière dimension. Naturellement, ceci ne fonctionne pas avec la première dimension. Si vous essayez, vous obtiendrez une exception pour syntaxe invalide.

Nous verrons d'autres exemples de tranches de tableaux NumPy dans le tutoriel suivant de cette série.

4 - Chargement des données

Si votre job est la science des données, vos données proviennent certainement d'une source extérieure et ce n'est pas vous qui créez un tableau chaque fois que vous voulez calculer quelque chose.

La plupart du temps, vous recevez vos données sous la forme d'un fichier CSV (Comma Separated Values, valeurs séparées par des virgules(1)). Chargeons dans un tableau les données suivantes contenues dans le fichier exemple.csv :

10.00,10.40,10.80,11.20,11.60,12.00,12.40,12.80,13.20,13.60,14.00,14.40,14.80,15.20,15.60,16.00,16.40,16.80,17.20,17.60,18.00,18.40,18.80,19.20,19.60
20.00,20.40,20.80,21.20,21.60,22.00,22.40,22.80,23.20,23.60,24.00,24.40,24.80,25.20,25.60,26.00,26.40,26.80,27.20,27.60,28.00,28.40,28.80,29.20,29.60
30.00,30.40,30.80,31.20,31.60,32.00,32.40,32.80,33.20,33.60,34.00,34.40,34.80,35.20,35.60,36.00,36.40,36.80,37.20,37.60,38.00,38.40,38.80,39.20,39.60

Oui, ce sont les mêmes données que dans le chapitre précédent, mais je les ai formatées sous la forme d'une matrice bidimensionnelle 3x25.

Nous avons à nouveau plusieurs options pour charger ces données dans un tableau NumPy. Je laisserai de côté les solutions Python convertissant le résultat en un tableau NumPy et m'intéresserai aux versions utilisant les fonctions internes de NumPy :

>>> csv = np.genfromtxt ('exemple.csv', delimiter=",")
>>> csv
array([[ 10. ,  10.4,  10.8,  11.2,  11.6,  12. ,  12.4,  12.8,  13.2,
13.6,  14. ,  14.4,  14.8,  15.2,  15.6,  16. ,  16.4,  16.8,
17.2,  17.6,  18. ,  18.4,  18.8,  19.2,  19.6],
[ 20. ,  20.4,  20.8,  21.2,  21.6,  22. ,  22.4,  22.8,  23.2,
23.6,  24. ,  24.4,  24.8,  25.2,  25.6,  26. ,  26.4,  26.8,
27.2,  27.6,  28. ,  28.4,  28.8,  29.2,  29.6],
[ 30. ,  30.4,  30.8,  31.2,  31.6,  32. ,  32.4,  32.8,  33.2,
33.6,  34. ,  34.4,  34.8,  35.2,  35.6,  36. ,  36.4,  36.8,
37.2,  37.6,  38. ,  38.4,  38.8,  39.2,  39.6]])

>>> csv_2 = np.loadtxt('exemple.csv', delimiter=',')
>>> csv_2
array([[ 10. ,  10.4,  10.8,  11.2,  11.6,  12. ,  12.4,  12.8,  13.2,
13.6,  14. ,  14.4,  14.8,  15.2,  15.6,  16. ,  16.4,  16.8,
17.2,  17.6,  18. ,  18.4,  18.8,  19.2,  19.6],
[ 20. ,  20.4,  20.8,  21.2,  21.6,  22. ,  22.4,  22.8,  23.2,
23.6,  24. ,  24.4,  24.8,  25.2,  25.6,  26. ,  26.4,  26.8,
27.2,  27.6,  28. ,  28.4,  28.8,  29.2,  29.6],
[ 30. ,  30.4,  30.8,  31.2,  31.6,  32. ,  32.4,  32.8,  33.2,
33.6,  34. ,  34.4,  34.8,  35.2,  35.6,  36. ,  36.4,  36.8,
37.2,  37.6,  38. ,  38.4,  38.8,  39.2,  39.6]])

On voit deux méthodes pour importer des fichiers CSV dans NumPy. Ces deux fonctions se ressemblent dans la mesure où elles utilisent toutes les deux comme paramètres le nom du fichier et le séparateur, mais elles diffèrent si nous examinons leur description détaillée.

La fonction genfromtxt est un peu plus complexe ; elle peut accepter plus de paramètres permettant d'ajuster finement la façon dont vous chargez les données. Dans l'article suivant, nous utiliserons un fichier CSV avec des données réelles et le formaterons pour comprendre comment les importer dans NumPy.

5 - Conclusion

Nous n'avons jeté qu'un coup d'œil très bref à NumPy. La suite de cette série de tutoriels plongera plus profondément et s'intéressera à des tranches plus affûtées. Et nous nous intéresserons à un fichier CSV présentant différents types de données dans ses colonnes, comme c'est souvent le cas avec des données d'un projet de la vie réelle.

6 - Remerciements

Nous remercions Gabor Laszlo Hajba qui nous a autorisés à publier son tutoriel Getting started with NumPy

Nous tenons également à remercier Lolo78 pour la traduction de ce tutoriel, Laethy pour la revue de la traduction et Claude Leloup pour la relecture orthographique.

(1)L'expression CSV s'applique aussi par extension aux données séparées par d'autres caractères comme le point-virgule, le tiret ou tout autre séparateur (NdT).