import numpy as np
contenu de ce notebook (sauter si déjà acquis)¶
tests sur les tableaux multi-dimensionnels numpy
par fonctions vectorisées ufunc
masques/filtres booléens
composition des conditions
opérateurs logiques bit-à-bit &
|
~
équivalent numpy
np.logical_and
np.logical_or
np.logical_not
obtenir une vue sur les éléments du tableau initialnumpy.argwhere
, numpy.nonzero
et numpy.putmask
tests sur tableaux multi-dimensionnels¶
# le code
tab = np.random.randint(10, size=(2, 3))
print(tab)
print(tab % 2 == 0)
print(np.equal(tab % 2, 0))
res = tab % 2 == 0
print(res.shape)
[[7 2 5]
[1 1 0]]
[[False True False]
[False False True]]
[[False True False]
[False False True]]
(2, 3)
n’utilisez pas de for-python: utilisez les ufunc
¶
# > est une ufunc
# on peut écrire indifféremment
tab > 5
array([[ True, False, False],
[False, False, False]])
# ou bien
np.greater(tab, 5)
array([[ True, False, False],
[False, False, False]])
combiner les résultats¶
# le code
tab = np.random.randint(10, size=(2, 3))
res = np.equal(tab%2, 0)
print(np.any(res))
print(np.all(res))
print(np.sum(res))
print(np.count_nonzero(tab%2==0))
print(np.sum(res, axis=0))
np.count_nonzero(tab%2==0, axis=0)
True
False
3
3
[2 1 0]
array([2, 1, 0])
les masques/filtres booléens¶
# le code
tab = np.random.randint(-10, 10, size=(2, 3, 4))
tab
array([[[ 2, -8, -5, -9],
[ -5, -6, -2, 3],
[ -8, -4, -6, -10]],
[[ -7, -7, -7, 5],
[ -9, 3, 7, 5],
[ 3, -3, -7, 6]]])
# le code
print(tab[np.greater(tab, 0)])
print(tab[tab > 0])
[2 3 5 3 7 5 3 6]
[2 3 5 3 7 5 3 6]
# le code
tab [tab > 0] = 0
tab
array([[[ 0, -8, -5, -9],
[ -5, -6, -2, 0],
[ -8, -4, -6, -10]],
[[ -7, -7, -7, 0],
[ -9, 0, 0, 0],
[ 0, -3, -7, 0]]])
# le code
np.argwhere(tab==0)
array([[0, 0, 0],
[0, 1, 3],
[1, 0, 3],
[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 0],
[1, 2, 3]])
composition des conditions¶
4 règles
vous ne pouvez pas utiliser les opérateurs logiques Python
and
,or
,not
(ils ne sont pas vectorisés)vous devez utiliser les opérateurs logiques bit-à-bit
&
|
~
ou leur équivalent en fonction
numpy
np.logical_and
np.logical_or
np.logical_not
(qui sont binaires)vous devez parenthéser les sous-termes de vos expressions
on crée un tableau d’entiers aléatoires entre 0 et 100
tab = np.random.randint(100, size=(3, 4))
masque pour sélectionner les éléments entre 25 et 75
(tab >= 25) & (tab < 75)
masque pour sélectionner les éléments non-pairs entre 25 et 75
(tab >= 25) & (tab < 75) & ~(tab%2==0)
et donc en version numpy
np.logical_and(tab >= 25, tab < 75)
np.logical_and(tab >= 25, np.logical_and(tab < 75, np.logical_not(tab%2==0)))
# le code
tab = np.random.randint(100, size=(3, 4))
print(tab)
print((tab >= 25) & (tab < 75))
print((tab >= 25) & (tab < 75) & ~(tab%2==0))
[[96 62 0 76]
[88 22 28 86]
[23 0 31 31]]
[[False True False False]
[False False True False]
[False False True True]]
[[False False False False]
[False False False False]
[False False True True]]
# le code
print(np.logical_and(tab >= 25, tab < 75))
print(np.logical_and(tab >= 25, np.logical_and(tab < 75, np.logical_not(tab%2==0))))
[[False True False False]
[False False True False]
[False False True True]]
[[False False False False]
[False False False False]
[False False True True]]
# ATTENTION
# en Python pur on a le droit d'écrire un test comme
# 25 <= tab < 75
# MAIS comme ça va implicitement faire un 'and'
# ça ne fonctionne pas avec les tableaux numpy
try:
25 <= tab < 75
except Exception as exc:
print("OOPS - ne marche pas avec numpy\n", exc)
OOPS - ne marche pas avec numpy
The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
modifier les éléments dans tableau d’origine¶
affecter une sélection¶
avec une expression de sélection de cette forme tab[mask]
on peut aussi modifier (ces emplacements dans) le tableau de départ
en affectant directement une valeur
remarquez que la sélection se trouve à gauche du signe =
tab = np.array([[1, 2, 3], [4, 5, 6]])
tab[tab % 2 == 0] = 100
print(tab)
[[ 1 100 3]
[100 5 100]]
# le code
tab = np.array([[1, 2, 3], [4, 5, 6]])
tab[tab % 2 == 0] = 100
print(tab)
[[ 1 100 3]
[100 5 100]]
c’est fragile (1)¶
par contre il faut être un peu prudent; certaines formes, pourtant voisines en apparence, ne vont pas fonctionner
1er cas
maladroitement, je range la sélection dans une variable
la sélection ne se trouve plus à gauche du =
dans cette forme l’affectation va en fait modifier un tableau temporaire
bref, ça ne fonctionne plus !
tab = np.array([[1, 2, 3], [4, 5, 6]])
view = tab[tab%2==0]
view[...] = 100
print(tab)
-> ([[1, 2, 3], # et non [1, 100, 3],...
[4, 5, 6]])
print(view)
-> [100 100 100]
# le code
tab = np.array([[1, 2, 3], [4, 5, 6]])
view = tab[tab%2==0]
view[...] = 100
print(tab)
print(view)
[[1 2 3]
[4 5 6]]
[100 100 100]
c’est fragile (2)¶
2ème cas
imaginons que je ne veux modifier que le premier des éléments pair
je vais essayer en indexant ma sélection
mais ça ne fonctionne pas comme espéré
ici encore l’effet de bord se perd dans la nature
et le tableau original n’est pas modifié
tab = np.array([[1, 2, 3], [4, 5, 6]])
tab[tab%2==0][0] = 100
print(tab)
-> ([[1, 2, 3], # et non [1, 100, 3],...
[4, 5, 6]])
# le code
tab = np.array([[1, 2, 3], [4, 5, 6]])
tab[tab%2==0][0] = 100
print(tab)
[[1 2 3]
[4 5 6]]
repérer les éléments par leurs indices¶
dans ce genre de situation, pour modifier les éléments sélectionnés dans le tableau d’origine, on peut repèrer les éléments par leur indice dans le tableau d’origine
et pour calculer ces indices, deux fonctions:
- la fonction
numpy.nonzero
- la fonction
numpy.argwhere
(avancé)
la fonction numpy.nonzero
¶
numpy.nonzero
- renvoie un tuple de même dimension que le tableau d’origine
- dans chaque dimension, on a la liste des indices
exemple
tab = np.array([[1, 2, 3], [4, 5, 6]])
np.nonzero(~(tab%2==0))
-> ([0, 0, 1], [0, 2, 1])
la première liste contient les indices des lignes [0, 0, 1]
la seconde liste contient les indices des colonnes [0, 2, 1]
tab[0, 0]
tab[0, 2]
et tab[1, 1]
sont les 3 éléments
print(tab[0, 0], tab[0, 2], tab[1, 1])
-> 1, 3, 5
la magie: vous pouvez indicer le tableau d’origine avec ce tuple
pour obtenir une vue sur le tableau d’origine
tab[np.nonzero(~(tab%2==0))]
-> 1, 3, 5
et donc vous pouvez modifier les éléments du tableau original
tab[np.nonzero(~(tab%2==0))] = 1000
tab
-> [[1000, 2, 1000],
[ 4, 1000, 6]]
tab = np.array([[1, 2, 3], [4, 5, 6]])
print("non zero", np.nonzero(~(tab%2==0)))
print("elements", tab[0, 0], tab[0, 2], tab[1, 1])
print("filter", tab[np.nonzero(~(tab%2==0))])
tab[np.nonzero(~(tab%2==0))] = 0
print("edited tab", tab)
non zero (array([0, 0, 1]), array([0, 2, 1]))
elements 1 3 5
filter [1 3 5]
edited tab [[0 2 0]
[4 0 6]]
la fonction numpy.argwhere
¶
numpy.argwhere
- renvoie un tableau de dimension 2
- autant de lignes que d’éléments filtrés
- chaque ligne donne l’index d’un élément
dans chacune des dimensions du tableau d’origine
exemple
tab = np.array([[1, 2, 3], [4, 5, 6]])
np.argwhere(~(tab%2==0))
-> [[0, 0],
[0, 2],
[1, 1]]
la première ligne contient les indices du premier élément [0, 0]
la seconde ligne contient les indices du second élément [0, 2]
la troisième ligne contient les indices du troisième élément [1, 1]
vous ne pouvez pas indicer directement le tableau d’origine par ce tableau
et non on ne fait pas de for-python
on remarque
- que les résultats de
numpy.nonzero
etnumpy.argwhere
sont très proches - à une transposée et un type
tuple
près
cond = ~(tab%2==0)
np.nonzero(cond) # ([0, 0, 1], [0, 2, 1])
np.argwhere(cond) # [[0 0] [0 2] [1 1]]
np.argwhere(cond).T # [[0 0 1] [0 2 1]]
tuple(np.argwhere(cond).T) # ([0, 0, 1], [0, 2, 1])
tab[tuple(np.argwhere(cond).T)]
-> array([1, 3, 5])
# le code
tab = np.array([[1, 2, 3], [4, 5, 6]])
cond = ~(tab%2==0)
print(np.argwhere(cond).T)
print(np.nonzero(cond))
print(tuple(np.argwhere(cond).T))
tab[tuple(np.argwhere(cond).T)]
[[0 0 1]
[0 2 1]]
(array([0, 0, 1]), array([0, 2, 1]))
(array([0, 0, 1]), array([0, 2, 1]))
array([1, 3, 5])
modifier avec array.putmask()
¶
avancés
la fonction numpy.putmask(tab, cond, value)
remplace dans un numpy.ndarray
les éléments obéissant à une condition, par une valeur donnée en argument
la modification est effectuée dans le tableau (en place)
tab = np.arange(12).reshape(3, 4)
np.putmask(tab, tab%2==1, 0)
tab -> ([[ 0, 0, 2, 0],
[ 4, 0, 6, 0],
[ 8, 0, 10, 0]])
# le code
tab = np.arange(12).reshape(3, 4)
np.putmask(tab, tab%2==1, 0)
tab
array([[ 0, 0, 2, 0],
[ 4, 0, 6, 0],
[ 8, 0, 10, 0]])