Logique, structures de contrôle et filtrage#
Vidéo: Opérateurs de comparaison#
Opérateurs de comparaison#
Une expression booléenne est une expression qui est soit vraie, soit fausse. Par exemple, l’opérateur ==
compare les expressions de chaque côté de l’opérateur et renvoie la valeur True
(vrai) s’ils sont égaux et False
(faux) sinon.
# Opérateur égalité
print(5 == 5)
print(2 == 4)
True
False
Les valeurs True
et False
appartiennent au type bool
:
print(type(True))
print(type(False))
<class 'bool'>
<class 'bool'>
La liste des opérateurs de comparaison est la suivante :
opérateur |
description |
---|---|
|
x est égal à y |
|
x n’est pas égal à y |
|
x est strictement supérieur à y |
|
x est strictement inférieur à y |
|
x est supérieur ou égal à y |
|
x est inférieur ou égal à y |
Attention
Ces opérateurs semblent familiers. Cependant, les symboles de Python sont différents des symboles mathématiques. En particulier, le signe = des mathématiques n’est pas le symbole =
de Python, qui est l’opérateur d’affectation, alors que le symbole ==
est un opérateur relationnel en Python.
Égalité#
Pour vérifier l’égalité entre 2 expressions on utilise le symbole ==
. Les expressions de chaque côté de l’opérateur sont évaluées et comparées. Pour vérifier que 2 expressions ne sont pas égales on utilise l’opérateur !=
.
Les exemples suivants renvoient tous le booléen True
:
print((7 + 2) == 9)
print("bonjour" != "au revoir")
print(True == True)
print(3 * 5 == 5. + 5. + 5.)
True
True
True
True
Exercice#
D’après vous, quels sont les résultats des comparaisons suivantes. Vérifiez dans la console :
True
est égal àFalse
43
n’est pas égal à3 * 15
"Paris"
est égal à"paris"
True
est égal à"Paris"
Show code cell source
print(True == False)
print(43 != (3 * 15))
print("Paris" == "paris")
print(True == "Paris")
False
True
False
False
Supérieur et inférieur#
On peut utiliser les opérateurs de comparaison inférieur à <
et supérieur à >
. Ces symboles typographiques s’appellent des chevrons. On peut aussi les combiner au symbole =
pour former les opérateurs inférieur ou égal <=
et supérieur ou égal >=
. Le signe égal est toujours après le chevron.
Voici quelques exemples qui renvoient tous le booléen True
:
print(6 < 23)
print(8 - 3 >= 5)
print("beta" < "theta")
True
True
True
Comme on l’a vu précédemment, pour une valeur de type string
(chaîne de charactères), l’ordre croissant est l’ordre alphabétique.
Exercice#
D’après vous, quels sont les résultats des comparaisons suivantes. Vérifiez dans la console :
10 / 3
est supérieur à3.33333
"chateau"
est supérieur ou égal à"chien"
True
est supérieur àFalse
Show code cell source
print(10 / 3 > 3.33333)
print("chateau" >= "chien")
print(True > False)
True
False
True
Comparer des tableaux#
Il est possible d’utiliser les opérateurs de comparaison directement avec des tableaux Numpy. Les comparaisons sont effectuées pour chaque élément du tableau. Le résultat est un tableau Numpy contenant les résultats de chacune des comparaisons, c’est-à-dire un tableau de valeurs de type booléen.
Par exemple :
# Importer le module Numpy
import numpy as np
# Créer des tableaux Numpy
A = np.array([1.4, 7.1, 9.0, 2.5, -4.6])
B = np.array([2.6, -5.7, 4.0, 2.5, 5.8])
# Comparaisons
print(A > 5.)
print(B > A)
[False True True False False]
[ True False False False True]
Vérifions que le résultat de la comparaison entre des tableaux Numpy est bien un tableau Numpy contenant des booléens :
# Comparaison entre 2 tableaux Numpy A et B
C = B > A
# Type du résultat : tableau Numpy
print(type(C))
# Type des éléments du tableau : booléens
print(C.dtype)
<class 'numpy.ndarray'>
bool
Exercice#
Les tableaux Numpy suivants contiennent les surfaces (en \(\mathrm{m}^2\)) des pièces de 2 maisons différentes, A et B, dans le même ordre : une chambre, le salon, la cuisine et la salle de bain :
maison_A = np.array([15.5, 22.3, 6.6, 8.5]) # m**2
maison_B = np.array([12.3, 28.8, 12.7, 8.0]) # m**2
En utilisant des opérateurs de comparaison, déterminer :
quelles sont les pièces de la maison A dont la superficie est supérieure ou égal à 10.0 \(\mathrm{m}^2\) ?
quelles sont les pièces de la maison B dont les surfaces sont plus grandes que celles de la maison A ?
la surface totale de la maison A est-elle plus grande que celle de la maison B ? Note : on pourra utiliser l’opérateur Numpy
sum()
.
Show code cell source
# 1 : Chambre, salon et cuisine
print(maison_A >= 10.0)
# 2 : Salon et cuisine
print(maison_B > maison_A)
# 3 : Non
print(np.sum(maison_A) > np.sum(maison_B))
[ True True False False]
[False True True False]
False
Vidéo: Opérateurs logiques#
Opérateurs logiques#
Il existe trois opérateurs logiques : and
, or
et not
. Il ont le même sens que leur traduction, respectivement : et, ou et non. On peut les utiliser pour combiner des expressions booléennes. Par exemple, l’expression x > 10 and x <= 14
est vraie seulement si x
est strictement supérieur à 10
et x
est inférieur ou égal à 14
.
L’expression n % 5 == 0 or n % 6 == 0
est vraie si n
est divisible par 5
ou si n
est divisible par 6
ou les deux.
Finalement, l’opérateur not
nie une expression. Par exemple not x > y
est vraie si x > y
est faux, c’est-à-dire si x
est inférieur ou égal à y
.
Exercice#
Créons les variables masse_terre
et masse_mars
contenant les masses de la Terre et de Mars en \(\mathrm{kg}\) :
masse_terre = 5.9736e24 # kg
masse_mars = 6.4185e23 # kg
En utilisant des opérateurs logiques et des expressions booléennes, déterminer :
si la masse de la Terre est strictement comprise entre \(10^{23}\) et \(10^{24} ~\mathrm{kg}\)
si la masse de Mars est supérieure ou égale à \(10^{25} ~\mathrm{kg}\) ou si elle est inférieure ou égale à la masse de la Terre
si la masse de la Terre est strictement comprise entre 1 et 10 fois la masse de Mars
Finalement, déterminer sans l’écrire a priori le résultat de l’expression not(not(masse_terre > 1e23) or not(masse_terre >= masse_mars and masse_mars > 1e25))
Show code cell source
# 1: faux
print(1e23 < masse_terre and masse_terre < 1e24)
# 2: vrai
print(1e25 <= masse_mars or masse_mars <= masse_terre)
# 3: vrai
print(masse_mars < masse_terre and masse_terre < 10 * masse_mars)
# 4: faux
print(not(not(masse_terre > 1e23) or not(masse_terre >= masse_mars and masse_mars > 1e25)))
False
True
True
False
Numpy et les opérateurs logiques#
Les opérateurs logiques and
, or
et not
ne fonctionnent pas avec les tableaux Numpy. Nous avons vu que le résultat de la comparaison de 2 tableaux Numpy est un tableau Numpy contenant des booléens. Pour effectuer des opérations logiques entre les éléments de 2 tableaux Numpy bool_1
et bool_2
contenant des booléens, il faut utiliser les fonctions Numpy suivantes :
fonction Numpy |
opérateur logique |
---|---|
|
et |
|
ou |
|
non |
bool_1
et bool_2
peuvent être le résultat d’une comparaison entre des tableaux Numpy. Reprenons les tableaux A
et B
créés plus haut :
# Créer des tableaux Numpy
A = np.array([1.4, 7.1, 9.0, 2.5, -4.6])
B = np.array([2.6, -5.7, 4.0, 2.5, 5.8])
# Création des tableaux de booléens, résultats de comparaisons
# Ici on cherche tous les éléments de A qui sont strictement inférieurs à ceux de B et à 5.0
bool_1 = A < B
bool_2 = A < 5.0
# Opération logique sur les tableaux de booléens
print(np.logical_and(bool_1, bool_2))
[ True False False False True]
On voit que seuls les premier et dernier éléments de A sont à la fois strictement inférieurs à ceux de B et à 5.0.
Il existe de nombreuses fonctions logiques dans le module Numpy, la liste est disponible sur le site du module. Parmi les fonctions utiles, on note :
all()
: teste si tous les éléments d’un tableau sont vraisany()
: teste si au moins un des éléments d’un tableau est vrai
Par exemple :
# Teste si au moins un des éléments de A est strictement supérieur à 8 :
print(np.any(A > 8.0))
True
Exercice#
Reprenons les tableaux A
et B
définis plus haut, contenant les surfaces en \(\mathrm{m}^2\) des pièces de 2 maisons :
# Superficie des pièces : chambre, salon, cuisine, salle de bain
maison_A = np.array([15.5, 22.3, 6.6, 8.5]) # m**2
maison_B = np.array([12.3, 28.8, 12.7, 8.0]) # m**2
Déterminer, grâce aux fonctions logiques de Numpy :
quelles sont les pièces de la maison A dont la surface est strictement comprise entre 8 et 16 \(\mathrm{m}^2\) ?
quelles sont les pièces dont la surface n’est pas strictement inférieure à 10 dans les 2 maisons A et B ?
est-ce qu’au moins une des pièces de la maison A est plus grande que les pièces correspondantes de la maison B ?
Show code cell source
# 1 : Chambre et salle de bains
bool_1 = maison_A > 8
bool_2 = maison_A < 16
print(np.logical_and(bool_1, bool_2))
# 2 : Chambre et salon
bool_1 = np.logical_not(maison_A < 10)
bool_2 = np.logical_not(maison_B < 10)
print(np.logical_and(bool_1, bool_2))
# 3 : Oui
print(any(A > B))
[ True False False True]
[ True True False False]
True
Vidéo: Instructions conditionnelles et filtrage#
Instructions conditionnelles#
Il est très fréquent dans un programme que l’on veuille exécuter une instruction seulement si certaines conditions sont remplies. Pour cela, on utilise l’instruction if
:
# Création et affectation de la variable x
x = 2
# Test si x est positif
if x > 0:
print("x est strictement positif")
x est strictement positif
L’expression booléenne après l’instruction if
est appelée condition.
Dans le code ci-dessus, vérifiez ce qu’il se passe si vous changez la valeur de la variable x
pour une valeur négative.
Instructions composées
Les instructions if
, comme d’autres que nous verrons par la suite, sont des instructions composées : un en-tête, suivi d’un corps indenté contenant une instruction par ligne. Il n’y a pas de limite au nombre d’instructions contenues dans le corps indenté, mais il doit y en avoir au moins une.
Si la condition après l’instruction if
n’est pas vérifiée, il est possible d’exécuter une instruction alternative avec l’instruction else
:
# Ré-affectation de la variable x
x = 3
# Test si x est pair ou impair
if x % 2 == 0:
print("x est pair")
else:
print("x est impair")
x est impair
La condition est soit vraie soit fausse, une des 2 instructions est donc forcément exécutée. Ces 2 alternatives sont appelées des branchements.
Il est possible d’avoir besoin de plus de 2 branches. On écrit alors un enchaînement de conditions avec l’instruction elif
:
# Ré-affectation de la variable x
x = 0
# Test si x est positif, négatif ou nul
if x > 0:
print("x est positif")
elif x < 0:
print("x est négatif")
else:
print("x est nul")
x est nul
Il n’y a pas de limite au nombre de conditions qui peuvent être enchaînées, c’est-à-dire que l’on peut mettre autant d’instructions elif
que l’on veut. L’instruction else
sera toujours à la fin. Même si plusieurs des conditions sont vérifiées, seulement la première qui est vérifiée sera exécutée. Par exemple :
bouton_1 = False
bouton_2 = True
if bouton_1 and bouton_2:
print("Les 2 boutons sont allumés")
elif bouton_1:
print("Le bouton 1 est allumé")
elif bouton_2:
print("Le bouton 2 est allumé")
elif bouton_1 or bouton_2:
print("Un des 2 boutons est allumé")
else:
print("Aucun des 2 boutons n'est allumé")
Le bouton 2 est allumé
Dans l’exemple ci-dessus, l’instruction correspondant à la condition bouton_1 or bouton_2
ne sera jamais exécutée, car si elle est vraie alors il y aura forcément une des 2 conditions bouton_1
ou bouton_2
qui est vraie et dont l’instruction sera exécutée plus tôt.
Exercice#
Écrire l’instruction conditionnelle avec les branchements suivants :
si
x
est un multiple de 5 et de 6 afficher à l’écran “x est un multiple de 5 et de 6”si
x
est un multiple de 5 et pas de 6 afficher à l’écran “x est un multiple de 5 mais pas de 6”si
x
est un multiple de 6 et pas de 5 afficher à l’écran “x est un multiple de 6 mais pas de 5”si
x
n’est un multiple ni de 5 ni de 6 afficher à l’écran “x n’est un multiple ni de 5 ni de 6”
Testez vos conditions avec les nombres 8, 10, 12, et 30.
# Création et affectation de la variable x
x = 30
# Instruction conditionnelle
if x % 5 == 0 and x % 6 == 0:
print("x est un multiple de 5 et de 6")
elif x % 5 == 0 and x % 6 != 0:
print("x est un multiple de 5 mais pas de 6")
elif x % 5 != 0 and x % 6 == 0:
print("x est un multiple de 6 mais pas de 5")
else:
print("x est un multiple ni de 5 ni de 6")
x est un multiple de 5 et de 6
Filtrer un tableau Numpy#
Pour extraire un sous-tableau Numpy qui vérifie certaines conditions, il est possible d’utiliser un tableau de booléens en indice d’un autre tableau :
# Créer des tableaux Numpy
A = np.array([1.4, 7.1, 9.0, 2.5, -4.6])
B = np.array([2.6, -5.7, 4.0, 2.5, 5.8])
# condition est un tableau Numpy de booléens
condition = A > 4
print(condition)
# Filtrer le tableau A avec le tableau condition
print(A[condition])
# Il est possible de filtrer le tableau B avec la condition sur A
print(B[condition])
[False True True False False]
[7.1 9. ]
[-5.7 4. ]
On voit que les éléments filtrés sont ceux qui correspondent aux valeurs True
du tableau condition
. On peut filtrer les 2 tableaux sans créer le tableau condition
intermédiaire :
print(A[A > 4])
print(B[A > 4])
[7.1 9. ]
[-5.7 4. ]
Finalement, on peut utiliser les fonctions logiques de Numpy pour combiner les conditions :
condition = np.logical_and(A > 0, A < 5)
print(A[condition])
[1.4 2.5]
Note : le tableau condition
est souvent qualifié de “masque”, ou de “masque d’indices”. Si vous mettez un masque sur les yeux avant d’aller bronzer sur la plage, vous aurez bronzé partout sauf sous le masque : le masque permet de définir une zone qui ne bronze pas. Ici c’est pareil, le masque d’indices permet de sélectionner les éléments du tableau NumPy qu’on garde. On parle par exemple de masques en photolithographie.
Exercice#
Extraire du tableau
A
toutes les valeurs négativesExtraire du tableau
B
toutes les valeurs strictement comprises entre 1 et 3
# 1.
print(A[A < 0])
# 2.
print(B[np.logical_and(B > 1, B < 3)])
[-4.6]
[2.6 2.5]
Filtrer un DataFrame#
Filtrer un objet DataFrame fonctionne de façon similaire au filtrage d’un tableau Numpy. Pour cela il faut extraire un objet Series de l’objet DataFrame, et lui appliquer une condition. On obtient alors un objet Series contenant des booléens, avec lequel on peut filtrer l’objet DataFrame.
Reprenons la liste des pays de l’union européenne sauvegardée dans le module précédent :
# Import du module pandas
import pandas as pd
# Lecture du fichier pickle contenant les données
df_europe = pd.read_pickle('./europe.pkl')
Nous voulons extraire la liste des pays contenant plus de 50 millions d’habitants :
# Afficher les noms des colonnes
print(df_europe.columns)
# On extrait l'objet Series correspondant à la colonne 'population'
sr_population = df_europe['population']
# On applique la condition sur le nombre d'habitants
condition = sr_population > 50e6
# Afficher l'objet condition
condition.head(n = 2)
Index(['pays', 'capitale', 'population', 'date d'adhésion', 'sièges', 'poids'], dtype='object')
1 True
2 False
Name: population, dtype: bool
On voit que la variable condition
est un objet de type Series qui contient des booléens.
# On filtre l'objet df_europe avec l'objet condition
df_europe_big = df_europe[condition]
# On affiche le tableau filtré
df_europe_big
pays | capitale | population | date d'adhésion | sièges | poids | |
---|---|---|---|---|---|---|
1 | allemagne | berlin | 82162000 | 1957-01-01 | 96 | 1.370336 |
11 | france | paris | 66661621 | 1957-01-01 | 74 | 1.301915 |
15 | italie | rome | 60665551 | 1957-01-01 | 73 | 1.411261 |
De façon beaucoup plus synthétique on aurait pu se passer de l’objet intermédiaire condition
en écrivant :
df_europe[ df_europe['population'] > 50e6 ]
pays | capitale | population | date d'adhésion | sièges | poids | |
---|---|---|---|---|---|---|
1 | allemagne | berlin | 82162000 | 1957-01-01 | 96 | 1.370336 |
11 | france | paris | 66661621 | 1957-01-01 | 74 | 1.301915 |
15 | italie | rome | 60665551 | 1957-01-01 | 73 | 1.411261 |
De la même manière que pour les tableaux Numpy, il est possible d’utiliser les fonctions logiques de Numpy sur les objets Series. Par exemple, cherchons les pays dont la date d’adhésion est supérieure à 1990 et dont la population est supérieure à 10 millions :
# Conditions
condition1 = df_europe["population"] > 10e6
condition2 = df_europe["date d'adhésion"] > np.datetime64('1990','Y')
# Opération logique et
df_europe[np.logical_and(condition1, condition2)]
pays | capitale | population | date d'adhésion | sièges | poids | |
---|---|---|---|---|---|---|
21 | pologne | varsovie | 37967209 | 2004-01-01 | 51 | 1.575391 |
23 | république tchèque | prague | 10553853 | 2004-01-01 | 21 | 2.333646 |
24 | roumanie | bucarest | 19759968 | 2007-01-01 | 32 | 1.899287 |
Le module Pandas a introduit une notation un peu plus pratique pour les opérations logiques :
Opération |
notation Pandas |
---|---|
et |
|
ou |
|
non |
|
Par exemple, cherchons les pays dont l’année d’adhésion n’est pas 1957 et dont le nombre de sièges est supérieur à 50 :
df_europe[ ~(df_europe["date d'adhésion"] == np.datetime64('1957','Y')) & (df_europe["sièges"] > 50) ]
pays | capitale | population | date d'adhésion | sièges | poids | |
---|---|---|---|---|---|---|
8 | espagne | madrid | 46438422 | 1986-01-01 | 54 | 1.363776 |
21 | pologne | varsovie | 37967209 | 2004-01-01 | 51 | 1.575391 |
Attention
Ces opérateurs de logique binaire (&
, \|
, ~
) n’ont pas la même priorité des opérations que les opérateurs de logique booléenne (and
, or
, not
), et en l’occurence ils sont prioritaires devant les opérateurs de comparaison (==
, <
, >
, <=
, >=
, !=
), contrairement aux opérateurs de logique booléenne, donc il est conseillé de mettre des parenthèses. La priorité des opérations peut être vérifiée ici.
Par exemple, ~(df_europe["date d'adhésion"] == np.datetime64('1957','Y')) & df_europe["sièges"] > 50
ne donne pas le bon résultat, parce que Python tente d’abord d’effectuer l’opération binaire &
entre ~(df_europe["date d'adhésion"] == np.datetime64('1957','Y'))
et df_europe["sièges"]
(au lieu de df_europe["sièges"] > 50
). Alors que ~(df_europe["date d'adhésion"] == np.datetime64('1957','Y')) & (df_europe["sièges"] > 50)
donne le résultat souhaité (regardez bien les parenthèses).
Exercice#
À partir du DataFrame df_europe
, extraire :
Les pays dont la lettre commence par une lettre entre ‘c’ et ‘g’ (non inclus). On rappelle que l’ordre croissant pour les objets de type
str
est l’ordre alphabétique.Pour les pays qui ont adhéré en 2004, ceux dont le nombre de sièges est 6 ou dont le poids est inférieur à 2.
# 1.
df_europe[ (df_europe["pays"] >= 'd') & (df_europe["pays"] < 'g') ]
pays | capitale | population | date d'adhésion | sièges | poids | |
---|---|---|---|---|---|---|
7 | danemark | copenhague | 5659715 | 1973-01-01 | 13 | 2.693863 |
8 | espagne | madrid | 46438422 | 1986-01-01 | 54 | 1.363776 |
9 | estonie | tallinn | 1315944 | 2004-01-01 | 6 | 5.347374 |
10 | finlande | helsinki | 5401267 | 1995-01-01 | 13 | 2.822763 |
11 | france | paris | 66661621 | 1957-01-01 | 74 | 1.301915 |
# 2.
condition1 = df_europe["date d'adhésion"] == np.datetime64('2004', 'Y')
condition2 = (df_europe["sièges"] == 6) | (df_europe["poids"] < 2)
df_europe[ condition1 & condition2 ]
pays | capitale | population | date d'adhésion | sièges | poids | |
---|---|---|---|---|---|---|
5 | chypre | nicosie | 848319 | 2004-01-01 | 6 | 8.295046 |
9 | estonie | tallinn | 1315944 | 2004-01-01 | 6 | 5.347374 |
19 | malte | la valette | 434403 | 2004-01-01 | 6 | 16.198887 |
21 | pologne | varsovie | 37967209 | 2004-01-01 | 51 | 1.575391 |