Tableaux de nombres¶
La librairie numpy permet de traiter de façon efficace des tableaux numpy. Nous découvrirons cette librairie, sachant que la librairie Pandas repose sur numpy.
numpy
est la librairie qui permet de manipuler de larges tableaux de données. Elle offre plusieurs avantages par rapport aux listes : elle est beaucoup plus rapide et surtout permet de faire des calculs sans utiliser de boucles for. Il est indispensable de savoir manipuler correctement les tableaux numpy pour d’une part gagner du temps lors de l’exécution du programme, mais surtout gagner du temps lors de l’écriture du programme.
Contrairement aux listes, la taille et le type de donnée est fixé à la création d’un tableau numpy, ce qui fait que la mémoire est immédiatement allouée au tableau.
Voici quelques exemples :
import numpy as np
a = np.arange(10)
print(a**2)
print(np.sin(a))
[ 0 1 4 9 16 25 36 49 64 81]
[ 0. 0.84147098 0.90929743 0.14112001 -0.7568025 -0.95892427
-0.2794155 0.6569866 0.98935825 0.41211849]
Création d’un tableau¶
Il existe plusieurs fonctions pour créer un tableau.
Création d’un tableau à partir d’une liste :
a = np.array([1, 2, 4])
print(a.dtype)
# data type is calculated automatically
a = np.array([1.2, 2, 4])
print(a.dtype) # all numbers are float
# data type can be forced
a = np.array([1, 2, 4], dtype=float)
print(a.dtype)
int64
float64
float64
Création d’un tableau uniforme
N = 10
a = np.zeros(N)
b = np.ones(N)
Créer un range
N = 10
a = np.arange(N)
print('Valeur calculée', a.sum())
print('Valeur théorique', (N*(N+1))/2)
Valeur calculée 45
Valeur théorique 55.0
Répartition uniforme de points:
N = 300
x = np.linspace(0, 2*np.pi, num=N)
Répartition sur une échelle logarithmique:
x = np.logspace(0, 2, 11)
print(x)
[ 1. 1.58489319 2.51188643 3.98107171 6.30957344
10. 15.84893192 25.11886432 39.81071706 63.09573445
100. ]
Fonctions numpy¶
Le fonctions arithmétiques ainsi que toutes les fonctions mathématiques de la librairie numpy (ce sont plus ou moins les mêmes que la librairie math) fonctionnent avec des tableaux, c’est à dire qu’il n’est pas nécéssaire d’utiliser une boucle for pour créer un nouveau tableau.
Une fonction pourra être utilisée avec un tableau à condition que toutes les fonctions qu’elle utilise fonctionnent avec un tableau.
def gaussienne(x, moyenne=0, ecart_type=1):
return np.exp( -(x-moyenne)**2 / (2*ecart_type**2))
gaussienne(np.linspace(-3, 3, 11))
array([0.011109 , 0.05613476, 0.1978987 , 0.48675226, 0.83527021,
1. , 0.83527021, 0.48675226, 0.1978987 , 0.05613476,
0.011109 ])
Dans cet exemple, le boucle for est réalisée au niveau de chaque opération élémentaire. Comme toutes les fonctions sont compatible avec numpy, la fonction gaussienne
l’est. Si on remplace np.exp par math.exp, alors il y a une erreur.
Les fonctions de base de statistiques existent soit sous forme de fonction dans la librairie numpy soit sous forme de méthode sur les tableaux. Il s’agit de np.mean
, np.std
, np.var
. Il existe aussi d’autre fonction de reductions (elle prennent un tableau est renvoie un nombre) : np.sum
, np.mean
, np.max
, np.min
.
x = np.random.rand(1_000_000)
print(x.mean()) # ou print(np.mean(x))
print(x.std())
0.4999435020786929
0.2886119645283682
Remarquons que l’instruction if
n’est pas une fonction. Il n’y a pas de sens à l’utiliser avec un tableau.
x = np.random.rand(10)
if (x>.5):
y = 1-x
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-13-85ec1d204b32> in <module>
1 x = np.random.rand(10)
----> 2 if (x>.5):
3 y = 1-x
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Indexer un tableau numpy¶
On peut indexer un tableau numpy de plusieurs façon différentes
Comme les listes, avec des entier ou des slices
Avec un tableau d’entier
Avec un tableau de booléens (les deux tableaux doivent avoir la même taille)
Les tableaux numpy sont modifiables. On peut aussi écrire de la même façon à condition que les tableaux aient une taille compatible
a = np.array([1, 5, 4, 6])
indx = np.array([3, 1])
print(a[indx])
mask = np.array([True, False, False, True])
a[mask]
[6 5]
array([1, 6])
Remaquons qu’il est possible de créer un tableau de booleen avec les opérateurs de comparaison
a = np.random.normal(size=10)
print(a)
print(a>0)
print(a[a>0])
[ 1.90777979 -0.27021906 0.20357936 -1.01244181 0.48567974 0.10253538
1.8550102 1.14398201 0.3825322 -0.31723682]
[ True False True False True True True True True False]
[1.90777979 0.20357936 0.48567974 0.10253538 1.8550102 1.14398201
0.3825322 ]
Exemple : créer une fonction valeur absolue compatible numpy
def abs_np(x):
output = x.copy()
output[output<0] *= -1
return output
abs_np(a)
array([1.90777979, 0.27021906, 0.20357936, 1.01244181, 0.48567974,
0.10253538, 1.8550102 , 1.14398201, 0.3825322 , 0.31723682])
Quelques opérations courantes¶
Il existe beaucoup de fonction numpy permettant de faire simplement des opérations sur les tableaux :
Voici quelques exemples. Parfois les fonctions numpy existent sous forme d’une méthode (objet.method
).
import numpy as np
a = np.random.normal(size=10000)
print(np.max(a))
print(a.max()) # Sous forme d'une méthode
np.sum(a)
np.mean(a)
np.var(a)
np.std(a)
np.min(a)
np.max(a)
4.3373002062093935
4.3373002062093935
4.3373002062093935
Lorsque l’on a un tableau 2D (ou plus), il est possible d’éxecuter l’opération ligne par ligne (ou colonne par colonne).
n = 10
m = 4
# Tableau 2D avec les notes
# Les notes sont aléatoires !
data = np.random.rand(n, m)
moyenne_A = np.mean(data, axis=0) # Sum_i a_ij
moyenne_B = np.mean(data, axis=1) # Sum_j a_ij
Opération de tri :
a = np.random.rand(10)
b = np.sort(a)
print(b)
[0.08764322 0.1230581 0.17004889 0.18385438 0.45941352 0.55188637
0.61358225 0.70715072 0.73755352 0.95182282]
Il est parfois utile de connaître l’indice du maximum ou minimum ou de connaître l’ordre du tri. Cela s’obtient avec les fonctions argmax
, argmin
ou argsort
.
i_max = moyenne_eleves.argmax()
print(f'Le meilleur élève est le numéro {i_max}')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-7-fa5dcea6eb43> in <module>
----> 1 i_max = moyenne_eleves.argmax()
2
3 print(f'Le meilleur élève est le numéro {i_max}')
NameError: name 'moyenne_eleves' is not defined
Autres fonctions utiles:
N = 1000
a = np.random.normal(size=N)
# Différence entre deux éléments
np.diff(a)
# Marche aléatoire
b = np.cumsum(a)
Lire et écrire dans un fichier¶
Il existe deux type d’enregistrement : l’enregistrement sous forme d’un fichier texte et celui sous forme binaire. Dans un fichier texte, le tableau doit être de dimension un ou deux. Il est écrit ligne par ligne sous forme de nombre décimaux. Dans un fichier binaire, c’est la mémoire de l’ordinateur qui est recopier dans le fichier. Les fichiers textes ont l’avantage d’être lisible par un humain et d’être compatible avec beaucoup de logiciel, cependant l’écriture et surtout la lecture du fichier prend beaucoup de temps. Les fichiers binaire seront beaucoup plus rapide.
Pour enregistrer au format texte :
filename = 'test.dat'
a = np.array([1, 2, 4])
np.savetxt(filename, a)
with open(filename) as f:
print(f.read())
# Numbers are converted to float
1.000000000000000000e+00
2.000000000000000000e+00
4.000000000000000000e+00
Il est possible de lire des fichiers au format ‘csv’ tels que ceux exportés par un tableur. Voici un exemple.
# Création du fichier
csv_content = """# Tension; courant
1; 2.3
2; 4.5
3; 7.0"""
filename = 'test.csv'
with open(filename, 'w') as f:
f.write(csv_content)
data = np.loadtxt(filename, delimiter=';')
print(data[:,1])
[2.3 4.5 7. ]
Dans le cas présent, il est aussi possible de lire chaque colonne dans une variable directement :
# Utilisation de l'argument unpack
tension, courant = np.loadtxt(filename, delimiter=';', unpack=True)
print(tension/courant)
[0.43478261 0.44444444 0.42857143]
Pour les fichiers binaires, on utilise la fonction np.load
et np.save
. Le tableau sera strictement identique après relecture.
filename='test.npy'
a = np.array([1, 2, 4])
np.save(filename, a)
new_a = np.load(filename)
print(new_a)
# a est toujours un tableau d'entier
[1 2 4]