Les fonctions de Numpy#

Vidéo: Création de tableaux#

Créer des tableaux Numpy#

De nombreuses fonctions de Numpy sont dédiées à la construction de tableaux multidimensionnels. La liste complète des fonctions pour la création de tableaux peut se trouver dans la documentation Numpy. Attention à bien choisir la documentation appropriée à votre version de Numpy, accessible avec :

# Importation du module Numpy
import numpy as np

print(np.__version__)
1.24.4

La fonction arrayInfo#

Nous allons créer une fonction pour afficher les informations relatives aux tableaux Numpy, même si nous verrons plus tard la syntaxe de création d’une fonction personnelle.

def arrayInfo(array_name):
    """ Prints out the content, shape, dimensions and data type of an array """
    print("-----")
    print(array_name)
    print("shape:", array_name.shape)
    print("number of dimensions:", array_name.ndim)
    print("datatype:", array_name.dtype)
    print("-----")
    return

Nous pouvons alors utiliser cette fonction comme une fonction Python pour obtenir des informations sur un tableau :

# Création d'un tableau Numpy
A = np.array([[3, 5, 9],
            [4, 6, -2]])

# Utilisation de la fonction arrayInfo
arrayInfo(A)
-----
[[ 3  5  9]
 [ 4  6 -2]]
shape: (2, 3)
number of dimensions: 2
datatype: int64
-----

Uns et zeros#

Voici quelques fonctions utiles pour créer des tableaux préremplis :

fonction

description

empty(shape)

retourne un tableau de forme donnée

ones(shape)

retourne un tableau rempli de 1 de forme donnée

zeros(shape)

retourne un tableau rempli de 0 de forme donnée

Par exemple, créons un tableau A avec 3 lignes et 4 colonnes rempli de 1 :

A = np.ones((3, 4))
arrayInfo(A)
-----
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
shape: (3, 4)
number of dimensions: 2
datatype: float64
-----

On rappelle que la forme du tableau (shape) s’écrit sous la forme d’un tuple (voir Le module Numpy). Ici le tuple définit le nombre d’éléments dans chacune des dimensions du tableau : 3 éléments dans la dimension 0 (lignes), et 4 éléments dans la dimension 1 (colonnes), c’est-à-dire un tableau avec 3 lignes et 4 colonnes.

On remarque que le type des éléments du tableau est par défaut float64, c’est-à-dire des réels. Si nous voulons un type différent, il faut ajouter l’argument dtype à la fonction :

A = np.ones((3, 4), dtype = 'int64')
arrayInfo(A)
-----
[[1 1 1 1]
 [1 1 1 1]
 [1 1 1 1]]
shape: (3, 4)
number of dimensions: 2
datatype: int64
-----

Maintenant, créons un tableau à une dimension avec 5 éléments tous égaux à zéro. Pour cela, il faut se souvenir comment écrire un tuple avec 1 seul élément :

A = np.zeros((5,))
arrayInfo(A)
-----
[0. 0. 0. 0. 0.]
shape: (5,)
number of dimensions: 1
datatype: float64
-----

La fonction empty() permet de créer un tableau rapidement. Mais attention, ce tableau n’est pas vide, contrairement à ce que le nom de la fonction laisse entendre :

A = np.empty((3, 3))
arrayInfo(A)
-----
[[4.68322943e-310 0.00000000e+000 0.00000000e+000]
 [0.00000000e+000 0.00000000e+000 0.00000000e+000]
 [0.00000000e+000 0.00000000e+000 0.00000000e+000]]
shape: (3, 3)
number of dimensions: 2
datatype: float64
-----

On peut tester la vitesse d’exécution des différentes fonctions avec la commande %timeit :

# %timeit np.zeros((1000, 1000))
# %timeit np.empty((1000, 1000))

Comme on ne sait jamais vraiment ce que la fonction empty() met dans le tableau initialisé, il est presque toujours préférable d’utiliser zeros() ou ones(), pour être certain de ce qui est dans le tableau.

Les erreurs liées à des variables non itialisées ou mal initialisées sont fréquentes en programmation, et c’est donc une bonne habitude à prendre de toujours être certain que les variables sont correctement initialisées.

Exercice#

  1. Créer un tableau A de dimension 1, contenant 7 éléments tous égaux à \(4.2\) avec la fonction ones(). Afficher les attributs du tableau avec la fonction arrayInfo()

  2. Créer un tableau vide B avec la fonction zeros(), de même forme que A, sans écrire explicitement de tuple en argument de la fonction

Hide code cell source
# 1
A = np.ones((7,)) * 4.2
arrayInfo(A)

# 2
B = np.zeros(A.shape)
arrayInfo(B)
-----
[4.2 4.2 4.2 4.2 4.2 4.2 4.2]
shape: (7,)
number of dimensions: 1
datatype: float64
-----
-----
[0. 0. 0. 0. 0. 0. 0.]
shape: (7,)
number of dimensions: 1
datatype: float64
-----

Intervalles et pas#

Voici quelques fonctions utiles pour créer des tableaux dans un intervalle donné :

fonction

description

arange()

valeurs régulièrement espacées pour un pas donné

linspace()

valeurs régulièrement espacées pour un nombre d’éléments donné

logspace()

valeurs régulièrement espacées sur une échelle logarithmique pour un nombre d’éléments donné

La fonction linspace() est utile pour générer des tableaux avec des valeurs définies sur un intervalle donné. Par exemple, pour générer un tableau avec 15 valeurs dans l’intervalle [−4, 4] :

A = np.linspace(-4., 4, 15)
arrayInfo(A)
-----
[-4.         -3.42857143 -2.85714286 -2.28571429 -1.71428571 -1.14285714
 -0.57142857  0.          0.57142857  1.14285714  1.71428571  2.28571429
  2.85714286  3.42857143  4.        ]
shape: (15,)
number of dimensions: 1
datatype: float64
-----

Les deux premiers arguments de la fonction sont les bornes inférieure et supérieure, et le troisième argument est le nombre d’éléments du tableau. Si le nombre de points est omis, la fonction linspace() utilisera la valeur par défaut de 50 points, ce que l’on peut vérifier dans l’aide de python :

# help(np.linspace)

Il arrive souvent que l’on veuille créer un tableau avec non pas un nombre d’éléments donné à l’avance, mais un pas donné à l’avance. Le pas est l’intervalle entre 2 éléments. Si l’intervalle entre n’importe quels 2 éléments consécutifs reste le même, on parle alors de pas fixe ou de pas constant.

Voici une petite routine qui permet de créer un tableau d’éléments compris entre 2 bornes connues à l’avance et un pas fixe :

# Paramètres
start = -1.0   # borne inférieure
end = 2.0       # borne supérieure
step = 0.1      # pas

# Création du tableau
interval = end - start                     # intervalle
num_points = int(interval / step) + 1      # nombre d'éléments
g = np.linspace(start, end, num_points)    # tableau

# Attributs du tableau
arrayInfo(g)
-----
[-1.  -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1  0.   0.1  0.2  0.3
  0.4  0.5  0.6  0.7  0.8  0.9  1.   1.1  1.2  1.3  1.4  1.5  1.6  1.7
  1.8  1.9  2. ]
shape: (31,)
number of dimensions: 1
datatype: float64
-----

Exercice#

Pourquoi dans la routine précédente le nombre de points est donné par int(interval / step) + 1 ?

La fonction arange() génère des valeurs dans l’intervalle semi-ouvert [début, fin[ avec un pas donné. Cependant, il est conseillé de ne pas l’utiliser avec un pas non entier, car le résultat n’est pas toujours cohérent avec la définition. Par exemple :

print(np.arange(7.8, 8.4, 0.05))
[7.8  7.85 7.9  7.95 8.   8.05 8.1  8.15 8.2  8.25 8.3  8.35 8.4 ]

inclut l’élément 8.4 au lieu de l’exclure.

Par défaut la fonction arange() va générer des valeurs entières si tous les arguments d’entrée sont des nombres entiers. De plus, si le pas est omis, la fonction arange() utilisera le pas par de défaut de 1.

On peut donc l’utiliser à la place de la fonction range() pour générer un tableau Numpy d’entiers :

A = np.arange(-3, 5)

# est équivalent à :
B = np.array(list(range(-3, 5)))

arrayInfo(A)
arrayInfo(B)
-----
[-3 -2 -1  0  1  2  3  4]
shape: (8,)
number of dimensions: 1
datatype: int64
-----
-----
[-3 -2 -1  0  1  2  3  4]
shape: (8,)
number of dimensions: 1
datatype: int64
-----

Exercice#

En vous aidant de l’aide de la fonction logspace(), créer le tableau Numpy contenant toutes les puissances de \(10\) depuis \(10^{-4}\) à \(10^{5}\), puis affichez les attributs du tableau avec arrayInfo.

Hide code cell source
# help(np.logspace)
A = np.logspace(-4, 5, 10)
arrayInfo(A)
-----
[1.e-04 1.e-03 1.e-02 1.e-01 1.e+00 1.e+01 1.e+02 1.e+03 1.e+04 1.e+05]
shape: (10,)
number of dimensions: 1
datatype: float64
-----

Vidéo: Manipuler les tableaux#

Manipulation de tableau#

Voici quelques fonctions utiles pour manipuler les tableaux Numpy. La liste complète est dans la documentation Numpy.

fonction

description

reshape()

donne une forme différente au tableau sans changer ses éléments

t.flatten()

retourne une copie 1D du tableau

t.T

transposée du tableau

concatenate()

joindre plusieurs tableaux

append()

ajoute des valeurs à la fin du tableau

unique()

trouve les éléments uniques du tableau

flip()

inverse l’ordre des éléments

Voici quelques exemples d’utilisation de ces fonctions :

# Tableau de forme (2, 4)
A = np.array([[2, 7, 9, 1],
            [-3, 4, 0, 2]])
arrayInfo(A)
-----
[[ 2  7  9  1]
 [-3  4  0  2]]
shape: (2, 4)
number of dimensions: 2
datatype: int64
-----
# Reformer en tableau de forme (4, 2)
B = np.reshape(A, (4, 2))
arrayInfo(B)
-----
[[ 2  7]
 [ 9  1]
 [-3  4]
 [ 0  2]]
shape: (4, 2)
number of dimensions: 2
datatype: int64
-----
# Attention, on voit que reshape est différent de la transposée, que l'on obtient comme suit :
C = A.T
arrayInfo(C)
-----
[[ 2 -3]
 [ 7  4]
 [ 9  0]
 [ 1  2]]
shape: (4, 2)
number of dimensions: 2
datatype: int64
-----

On peut joindre 2 tableaux suivant un axe donné, ici la première dimension, ou dimension 0 (lignes), avec la fonction append() :

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6]])
D = np.append(A, B, axis = 0)

arrayInfo(D)
-----
[[1 2]
 [3 4]
 [5 6]]
shape: (3, 2)
number of dimensions: 2
datatype: int64
-----

Si on veut joindre plusieurs tableaux ensemble, il faut les concaténer avec la fonction concatenate() :

C = np.array([[7, 8], [9, 10], [11, 12]])
D = np.concatenate((A, B, C), axis = 0)

arrayInfo(D)
-----
[[ 1  2]
 [ 3  4]
 [ 5  6]
 [ 7  8]
 [ 9 10]
 [11 12]]
shape: (6, 2)
number of dimensions: 2
datatype: int64
-----

Un peu de tri#

Il peut être utile de trier un tableau Numpy. Reprenons les tableaux de la leçon précédente donnant les nombres d’habitants et les superficies des 8 villes les plus peuplées de France en 2017 :

noms = np.array(['Paris', 'Marseille', 'Lyon', 'Toulouse', 'Nice', 'Nantes', 'Montpellier', 'Strasbourg'])
nombre_habitants = np.array([2187526, 863310, 516092, 479553, 340017, 309346, 285121, 280966])
superficie = np.array([105.40, 240.62, 47.87, 118.30, 71.92, 65.19, 56.88, 78.26]) # km**2

Nous voyons que les tableaux sont triés suivant la donnée du nombre d’habitants, du plus grand au plus petit. On aimerait trier les données des villes suivant la donnée de la superficie en ordre décroissant. On peut utiliser les fonctions sort() et flip() :

superficie_sorted = np.sort(superficie)         # Tri par défaut dans l'ordre croissant
superficie_sorted = np.flip(superficie_sorted)    # Inverse l'ordre du tri
print(superficie_sorted)
[240.62 118.3  105.4   78.26  71.92  65.19  56.88  47.87]

Si on obtient bien un tableau trié dans le bon ordre pour les superficies, les tableaux contenant les nombres d’habitants et les noms de ville n’ont pas été triés, et on ne sait pas dans quel ordre les trier pour qu’ils correspondent au nouvel ordre du tableau donnant les superficies.

Pour résoudre ce problème, on va utiliser un masque d’indices grâce à la fonction argsort(). Cette fonction retourne non pas le tableau trié, mais le tableau d’indices qui permet de trier le tableau. On peut alors utiliser ce masque d’indices en indice pour trier les autres tableaux :

mask = np.argsort(superficie)   # Retourne le masque d'indices pour trier le tableau dans l'ordre croissant
mask = np.flip(mask)            # Inverse l'ordre du masque pour obtenir un ordre décroissant

# Application du masque sur les 3 tableaux
noms_sorted = noms[mask]
nombre_habitants_sorted = nombre_habitants[mask]
superficie_sorted = superficie[mask]

# Affichage des tableaux triés
print(noms_sorted)
print(nombre_habitants_sorted)
print(superficie_sorted)
['Marseille' 'Toulouse' 'Paris' 'Strasbourg' 'Nice' 'Nantes' 'Montpellier'
 'Lyon']
[ 863310  479553 2187526  280966  340017  309346  285121  516092]
[240.62 118.3  105.4   78.26  71.92  65.19  56.88  47.87]

Vidéo: format DateTime64 et fonctions maths#

Quelle est la date ?#

Afin de manipuler simplement des dates, Numpy a un type de données datetime64. On peut alors utiliser les fonctions vues plus haut sur ce nouveau type de données.

Pour créer une date on utilise alors la fonction datetime() :

date = np.datetime64('2021-09-21')
print("La date est :", date)
print("L'année est :", np.datetime64(date, 'Y'))
La date est : 2021-09-21
L'année est : 2021

On peut créer simplement des tableaux de dates avec la fonction arange() :

dates = np.arange('2021-09', '2021-10', dtype = 'datetime64[D]') 
print("Dates de septembre 2021 :\n", dates) 
print("La date est en septembre :", date in dates)
Dates de septembre 2021 :
 ['2021-09-01' '2021-09-02' '2021-09-03' '2021-09-04' '2021-09-05'
 '2021-09-06' '2021-09-07' '2021-09-08' '2021-09-09' '2021-09-10'
 '2021-09-11' '2021-09-12' '2021-09-13' '2021-09-14' '2021-09-15'
 '2021-09-16' '2021-09-17' '2021-09-18' '2021-09-19' '2021-09-20'
 '2021-09-21' '2021-09-22' '2021-09-23' '2021-09-24' '2021-09-25'
 '2021-09-26' '2021-09-27' '2021-09-28' '2021-09-29' '2021-09-30']
La date est en septembre : True

Ou encore calculer des durées :

duree = np.datetime64('2022-01-05') - np.datetime64('2021-09-21')
print("No. de jours :", duree) 
print("No. de semaines :", np.timedelta64(duree, 'W')) 
No. de jours : 106 days
No. de semaines : 15 weeks

Ou encore trier un tableau de dates :

a = np.array(['2018-01-19', '2017-05-06', '2021-11-25'], dtype = 'datetime64') 
print("Dates triées :", np.sort(a))
Dates triées : ['2017-05-06' '2018-01-19' '2021-11-25']

Pour aller plus loin

Le type datetime64 de Numpy est similaire au type datetime de Python. On peut trouver les références pour Numpy ici, et pour Python ici.

Les fonctions mathématiques#

La force de Numpy est d’avoir de nombreuses fonctions mathématiques prédéfinies que l’on peut utiliser avec comme argument un tableau Numpy. La plupart de ces fonctions sont vectorisées, c’est-à-dire qu’elles fonctionnent avec des tableaux multidimensionnels, de la même manière que lorsque l’on a utilisé les opérateurs arithmétiques.

Voici une liste non exhaustive de fonctions classées par thème. La liste complète est dans l’aide de Numpy.

fonctions

Numpy

Trigonométrique

sin(), cos(), tan(), degrees(), radians()

Somme, produit

prod(), sum()

Exponentielle et logarithme

exp(), log(), log10()

Autres

sqrt(), fabs(), maximum(), minimum()

Ces fonctions appartiennent au module Numpy, il ne faut donc pas oublier d’écrire np.func() pour les appeler.

Exercice#

Un oscillateur amorti en régime libre à une dimension, lorsque le taux d’amortissement \(\xi\) est faible, se déplace suivant l’équation

\[ x(t) = C e^{-\xi\omega_0 t} \sin(\omega_d t+\phi) \]

avec \(x\) la position, \(t\) le temps, et \(\omega_d=\omega_0\sqrt{1-\xi^2}\). Vous prendrez comme valeurs : \(C=1~\mathrm{m}\), \(\xi=0.1\), \(\omega_0=1~\mathrm{s}^{-1}\) et \(\phi=0.2~\mathrm{rad}\).

  1. Créer le tableau Numpy t contenant 50 valeurs de \(t\) entre 0 et 10.

  2. Calculer alors le tableau Numpy x contenant les valeurs de \(x\) pour les valeurs correspondantes du tableau t, grâce à une unique expression

  3. Afficher le tableau des positions

Hide code cell source
# Paramètres
C = 1       # m
xi = 0.1    # sans unités
w0 = 1      # s**-1
phi = 0.2   # radians

# Tableau des temps
t = np.linspace(0, 10, 50) # s

# Tableau correspondant des positions
x = C * np.exp(-xi * w0 * t) * np.sin(w0 * np.sqrt(1 - xi ** 2) * t + phi)

# Affichage des positions
print(x)
[ 0.19866933  0.38431009  0.54689659  0.6807386   0.78154248  0.84652994
  0.87449045  0.86576775  0.82218372  0.74690515  0.64426118  0.51952075
  0.37864069  0.227996    0.07410384 -0.07664723 -0.21825188 -0.34531696
 -0.45325632 -0.53844506 -0.59832852 -0.63148334 -0.63763015 -0.61759912
 -0.57325189 -0.50736454 -0.42347814 -0.32572417 -0.21863285 -0.10693294
  0.00464846  0.11157817  0.20969347  0.29535659  0.3655817   0.41813022
  0.45157202  0.46531119  0.45957698  0.43538141  0.39444687  0.3391076
  0.27219031  0.19687956  0.11657387  0.03473899 -0.04523576 -0.12017224
 -0.18722434 -0.24398044]