Dans un précédent tutoriel, nous avons abordé les bases des boucles Python, en expliquant comment parcourir des listes et des listes de listes (ou listes imbriquées). Mais on peut faire bien plus avec des boucles for que parcourir des listes. Dans le monde réel, vous pouvez utiliser des boucles avec d’autres structures de données, par exemple des tableaux numpy ou même des DataFrames pandas. Je vous présente dans cet article quelques techniques avancées des boucles FOR en Python.

Ce tutoriel commencera par traiter de l’utilisation des boucles for pour parcourir des structures de données Python communes autres que des listes (telles que des n-uplets et des dictionnaires). Nous allons ensuite explorer les boucles for directement avec les bibliothèques scientifiques Python courantes telles que numpy, pandas et matplotlib. Nous allons également examiner de plus près la fonction range() et son utilité lors de l’écriture de boucles.

Structure générale : Boucles for Python

Une boucle for est une instruction de programmation qui demande à Python de parcourir une collection d’objets en effectuant la même opération sur chaque objet en séquence. La syntaxe de base est la suivante:

for object in collection_of_objects:
    # code que vous souhaitez exécuter sur chaque objet

Chaque fois que Python parcourt la boucle, la variable object prend la valeur du prochain élément de notre séquence collection_of_objects et Python exécutera le code que nous avons écrit pour chaque élément de collection_of_objects.

Voyons maintenant comment utiliser les boucles for avec différentes sortes de structures de données. Nous allons sauter des listes puisque celles-ci ont été entièrement couvertes dans le précédent tutoriel. Pour aller plus loin sur les boucles, regardez l’article précédent sur les bases des boucles for en Python ou les modules 1 et 4 de la formation Data Science en cliquant ici.

Les structures de données

Tuples

Les tuples sont des séquences, tout comme des listes. La différence entre les tuples et les listes est que les tuples sont immuables; c’est-à-dire qu’ils ne peuvent pas être changés (en savoir plus sur les objets mutables et immuables en Python). Les tuples utilisent également des parenthèses au lieu des crochets.

Indépendamment de ces différences, les boucles sur les tuples ressemblent beaucoup aux listes.

x = (10,20,30,40,50)
for var in x:
    print("index "+ str(x.index(var)) + ":",var)
index 0: 10
index 1: 20
index 2: 30
index 3: 40
index 4: 50

Si nous avons une liste de n-uplets, nous pouvons accéder aux éléments individuels de chaque tuple de notre liste en les incluant tous les deux en tant que variables dans la boucle for, comme suit:

x = [(1,2), (3,4), (5,6)] 
for a, b in x: print(a, "+", b, "=", a+b)
1 + 2 = 3
3 + 4 = 7
5 + 6 = 11

Dictionnaires

Outre les listes et les n-uplets, les dictionnaires sont un autre type de données Python courant que vous êtes susceptible de rencontrer lorsque vous travaillez avec des données. Les boucles for peuvent également effectuer une itération dans des dictionnaires.

Les dictionnaires Python sont composés de paires clé-valeur (key-value). Ainsi, dans chaque boucle, il faut accéder à deux éléments (la clé et la valeur). Au lieu d’utiliser enumerate() comme nous le ferions avec des listes, il est nécessaire d’appeler la méthode .items() pour parcourir les deux clés et les valeurs correspondantes de chaque paire clé-valeur.

Par exemple, imaginons que nous ayons un dictionnaire appelé stocks qui contient à la fois des actions boursières et les cours boursiers correspondants. Nous utiliserons la méthode .items() sur notre dictionnaire pour générer une clé et une valeur pour chaque itération:

stocks = {
        'AAPL': 187.31,
        'MSFT': 124.06,
        'FB': 183.50
    }

for key, value in stocks.items() :
    print(key + " : " + str(value))
AAPL : 187.31
MSFT : 124.06
FB : 183.5

Notez que les noms key et value sont complètement arbitraires, nous pourrions aussi utiliser k et v ou encore x et y.

Strings

Comme indiqué dans le tutoriel d’introduction, les boucles for peuvent également parcourir chaque caractère d’une chaîne strings. En résumé, voici comment cela fonctionne:

print("data science")
for c in "data science":
    print(c)
data science
d
a
t
a
 
s
c
i
e
n
c
e

Tableaux Numpy

Voyons maintenant comment les boucles for peuvent être utilisées avec les bibliothèques Python courantes de Data Science et leurs types de données.

Nous allons commencer par regarder comment utiliser les boucles avec des tableaux numpy. Commençons par créer des tableaux de nombres aléatoires.

import numpy as np
np.random.seed(0)  # seed aléatoire pour reproduire le même résultat
x = np.random.randint(10, size=6)
y = np.random.randint(10, size=6)

Itérer sur un tableau numpy à une dimension est très similaire à itérer sur une liste:

for val in x:
    print(val)
5
0
3
3
7
9

Et maintenant, si nous voulons parcourir un tableau à deux dimensions?
Si nous utilisons la même syntaxe que précédemment pour itérer un tableau à deux dimensions, nous ne pouvons qu’itérer des tableaux entiers à chaque itération.

# création du tableau 2 dimensions
z = np.array([x, y])

for val in z:
    print(val)
[5 0 3 3 7 9]
[3 5 2 4 7 6]

Un tableau à deux dimensions est constitué d’une paire de tableaux à une dimension. Pour visiter chaque élément plutôt que chaque tableau, nous pouvons utiliser la fonction numpy nditer(), un objet itérateur multidimensionnel qui prend un tableau comme argument.

Dans le code ci-dessous, nous allons écrire une boucle for qui parcourt chaque élément en passant z (notre tableau à deux dimensions) en tant qu’argument pour nditer():

for val in np.nditer(z):
    print(val)
5
0
3
3
7
9
3
5
2
4
7
6

Comme nous pouvons le voir, ceci énumère d’abord tous les éléments de x, puis tous les éléments de y.

Lors de la lecture par boucle for de ces différentes structures de données, les dictionnaires nécessitent une méthode alors que les tableaux numpy nécessitent une fonction.

DataFrames Pandas

Lorsque nous travaillons avec des données en Python, nous utilisons souvent des DataFrames Pandas. Et heureusement, nous pouvons utiliser les boucles for pour les parcourir également.

Faisons ce travail avec un petit fichier CSV (cliquez ici pour le télécharger) qui enregistre le PIB, la capitale et la population de six pays différents. Nous lirons ceci dans un DataFrame pandas ci-dessous.

Pandas fonctionne un peu différemment de numpy, nous ne pourrons donc pas simplement répéter le processus numpy que nous avons déjà appris. Si nous essayons de parcourir un DataFrame pandas comme nous le ferions avec un tableau numpy, ceci afficherait simplement les noms des colonnes:

import pandas as pd
df = pd.read_csv('gdp.csv', index_col=0)

for val in df:
    print(val)
Capital
GDP ($US Trillion)
Population 

Au lieu de cela, nous devons mentionner explicitement que nous souhaitons effectuer une itération sur les lignes du DataFrame. Nous faisons cela en appelant la méthode iterrows() sur le DataFrame et en affichant les données souhaitées (étiquette + ligne) où une ligne correspond à la série entière pandas.

for label, row in df.iterrows():
    print(label)
    print(row)
Ireland
Capital                Dublin
GDP ($US Trillion)     0.3337
Population            4784000
Name: Ireland, dtype: object
United Kingdom
Capital                 London
GDP ($US Trillion)       2.622
Population            66040000
Name: United Kingdom, dtype: object
United States
Capital               Washington, D.C.
GDP ($US Trillion)               19.39
Population                   327200000
Name: United States, dtype: object
China
Capital                  Beijing
GDP ($US Trillion)         12.24
Population            1386000000
Name: China, dtype: object
India
Capital                New Delhi
GDP ($US Trillion)         2.597
Population            1339000000
Name: India, dtype: object
Germany
Capital                 Berlin
GDP ($US Trillion)       3.677
Population            82790000
Name: Germany, dtype: object 

Nous pouvons également accéder à des valeurs spécifiques d’une série pandas. Supposons que nous voulions seulement afficher la capitale de chaque pays. Nous pouvons spécifier que nous voulons seulement sortir de la colonne “Capital” comme ceci:

for label, row in df.iterrows():
    print(label + " : " + row["Capital"])
Ireland : Dublin
United Kingdom : London
United States : Washington, D.C.
China : Beijing
India : New Delhi
Germany : Berlin

Pour aller plus loin que de simples affichages, ajoutons une colonne en utilisant une boucle for. Ajoutons une colonne de PIB par habitant. Rappelez-vous que la méthode .loc[] est basée sur un pays (label). Dans le code ci-dessous, nous allons ajouter la colonne de PIB par habitant et calculer sa valeur pour chaque pays en divisant le PIB total par la population et en multipliant le résultat par un billion (car les chiffres du PIB sont indiqués en billions).

for label, row in df.iterrows():
    df.loc[label,'gdp_per_cap'] = row['GDP ($US Trillion)']/row['Population '] * 1000000000000
print(df)
                         Capital  GDP ($US Trillion)  Population   \
Country                                                             
Ireland                   Dublin              0.3337      4784000   
United Kingdom            London              2.6220     66040000   
United States   Washington, D.C.             19.3900    327200000   
China                    Beijing             12.2400   1386000000   
India                  New Delhi              2.5970   1339000000   
Germany                   Berlin              3.6770     82790000   

                 gdp_per_cap  
Country                       
Ireland         69753.344482  
United Kingdom  39703.210176  
United States   59260.391198  
China            8831.168831  
India            1939.507095  
Germany         44413.576519  

Pour chaque ligne de notre DataFrame, nous créons une nouvelle étiquette et nous définissons des données de ligne égales au PIB total divisé par la population du pays, et en les multipliant par 1T$ pour des milliers de dollars.

La fonction range()

Nous avons vu comment nous pouvons utiliser les boucles for pour itérer sur toute séquence ou structure de données. Mais que se passe-t-il si nous souhaitons parcourir ces séquences dans un ordre spécifique ou pour un nombre de fois spécifique?

Cela peut être accompli avec la fonction range() (fonction built-in de Python). En fonction du nombre de paramètres utilisés pour la fonction, vous pouvez décider du début et de la fin de cette série de nombres ainsi que de de la différence entre un nombre et le suivant. Notez que, comme pour les listes, le décompte de la fonction range() commence à 0 et non à 1.

On peut appeler la fonction range() de trois manières:

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

range(stop)

range(stop) prend un paramètre – utilisé lorsque nous voulons effectuer une itération sur une série de nombres commençant à 0 et incluant tous les nombres jusqu’au numéro que nous avons défini, mais sans l’inclure.

for i in range(3):
    print(i)
0
1
2

range(start, stop)

range(start, stop) prend deux paramètres – où nous pouvons non seulement définir la fin de la série mais aussi le début. Vous pouvez utiliser range() pour générer une série de nombres de A à B en utilisant range(A, B).

for i in range(1, 8):
    print(i)
1
2
3
4
5
6
7

 

range(start, stop, step)

range(start, stop, step) prend trois arguments. Outre les valeurs minimale et maximale, nous pouvons définir la différence entre un nombre dans la séquence et le suivant. La valeur par défaut de ‘step’ est 1 si aucune valeur n’est fournie.

for i in range(3, 16, 3):
    print(i)
3
6
9
12
15

Notez que cela fonctionne de la même manière pour les séquences non numériques.

Nous pouvons également utiliser l’index des éléments dans une séquence pour itérer. L’idée principale consiste à calculer d’abord la longueur de la liste, puis à effectuer une itération sur la séquence dans la plage de cette longueur. Prenons un exemple:

languages = ['Espagnol', 'Anglais',  'Français', 'Allemand', 'Irlandais', 'Chinois']

for index in range(len(languages)):
   print('Langue:', languages[index])
Langue: Espagnol
Langue: Anglais
Langue: Français
Langue: Allemand
Langue: Irlandais
Langue: Chinois

Dans notre boucle for ci-dessus, nous examinons l’index de la séquence de nombres crée par range(). Notez que nous utilisons également la fonction len() dans ce cas, car la liste n’est pas numérique.

Pour chaque itération, nous exécutons notre instruction print. Donc, pour chaque index de range(len(languages)), nous voulons afficher une langue. Comme la longueur de notre séquence de langues est 6 (c’est-à-dire la valeur évaluée par len(languages)), nous pouvons réécrire l’instruction comme suit:

for index in range(6):
   print('Langue:', languages[index])
Langue: Espagnol
Langue: Anglais
Langue: Français
Langue: Allemand
Langue: Irlandais
Langue: Chinois

 

Tracer des graphiques avec des boucles For

Lorsque nous souhaitons parcourir une collection, nous utilisons chaque élément pour générer un sous-graphique ou même pour tracer un nouveau graphique. Par exemple, prenons le dataset très populaire iris.csv populaire et effectuons des tracés avec des boucles for. Si vous ne connaissez pas Matplotlib ou Seaborn, consultez la page de formation ici. Considérons le graphique ci-dessous.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.read_csv('iris.csv')
# créer une figure et un axe
fig, ax = plt.subplots()

# nuage de points des données sepal_length et des données sepal_width
ax.scatter(df['sepal_length'], df['sepal_width'])
# fixer un titre et des intitulés d'axe
ax.set_title('Iris Dataset')
ax.set_xlabel('sepal_length')
ax.set_ylabel('sepal_width')
Text(0,0.5,'sepal_width')

Graphique nuage de points Iris

Ci-dessus, nous avons tracé la longueur de chaque sépale par rapport à sa largeur, mais nous pouvons donner plus de signification au graphique en colorant chaque point de données par la classe d’espèces de chaque fleur. Une façon de le faire est de disperser chaque point séparément à l’aide d’une boucle for et de passer dans la couleur correspondante.

# créer un dictionnaire de couleurs
colors = {'setosa':'r', 'versicolor':'g', 'virginica':'b'}

# créer une figure et un axe
fig, ax = plt.subplots()

# tracer chaque point
for i in range(len(df['sepal_length'])):
    ax.scatter(df['sepal_length'][i], df['sepal_width'][i], color=colors[df['species'][i]])

# fixer un titre et des intitulés d'axe
ax.set_title('Iris Dataset')
ax.set_xlabel('sepal_length')
ax.set_ylabel('sepal_width')
Text(0,0.5,'sepal_width')

Graphique nuage de points colorés

Et si nous voulions visualiser la distribution univariée de certaines caractéristiques de notre dataset iris? Nous pouvons faire cela avec plt.subplot() qui crée un sous-graphique unique dans une grille composée du nombre de colonnes et de lignes que nous pouvons définir.

Distribution des caractéristiques d'Iris

Sans plonger trop profondément dans la syntaxe matplotlib pour le moment, voici une brève description de chaque composant principal de notre graphique:

  • plt.subplot() – utilisé pour créer notre grille 2 x 2 et définir la taille globale.
  • zip() – il s’agit d’une fonction built-in Python qui simplifie énormément la lecture simultanée de plusieurs itérables de même longueur.
  • axes.flatten(), où flatten() est une méthode de tableau numpy – renvoie une version aplatie de nos tableaux (colonnes).
  • ax.set() – nous permet de définir tous les attributs de notre objet axes avec une seule méthode.

 

Opérations additionnelles

Boucles imbriquées

Python nous permet d’utiliser une boucle dans une autre boucle. Cela implique une boucle externe qui a, à l’intérieur, une boucle interne.

Considérez la structure suivante:

for iterator_var in sequence:
    for iterator_var in sequence:
        # ...

Les boucles imbriquées peuvent être utiles pour parcourir des éléments dans des listes composées de listes. Dans une liste composée de listes, si nous employons une seule boucle for, le programme affichera chaque liste interne sous la forme d’un élément:

languages = [['Espagnol', 'Anglais',  'Français', 'Allemand'], ['Python', 'Java', 'Javascript', 'C++']]

for lang in languages:
    print(lang)
['Espagnol', 'Anglais', 'Français', 'Allemand']
['Python', 'Java', 'Javascript', 'C++']

Afin d’accéder individuellement à chaque élément des listes internes, nous définissons une boucle for imbriquée:

for x in languages:
    print("------")
    for lang in x:
        print(lang)
------
Espagnol
Anglais
Français
Allemand
------
Python
Java
Javascript
C++

Ci-dessus, la boucle for externe parcourt la liste de listes principale (qui contient deux listes dans cet exemple) et la boucle for interne parcourt les listes individuelles elles-mêmes. La boucle externe exécute 2 itérations (pour chaque sous-liste) et à chaque itération, nous exécutons notre boucle interne en affichant tous les éléments des sous-listes respectives.

Cela nous indique que le curseur se déplace depuis la boucle la plus externe, traverse la boucle interne puis revient à la boucle for externe, jusqu’à ce que le curseur couvre toute la plage, ce qui correspond à 2 fois dans ce cas.

Continue et break pour les boucles

Les instructions de contrôle de boucle modifient l’exécution d’une boucle for de sa séquence normale.

Et si nous voulons filtrer une langue spécifique dans notre boucle interne, nous pouvons utiliser l’instruction continue pour le faire, ce qui nous permet de sauter une partie spécifique de notre boucle lorsqu’une condition externe est déclenchée.

for x in languages:
    print("------")
    for lang in x:
        if lang == "Allemand":
            continue
        print(lang)
------
Espagnol
Anglais
Français
------
Python
Java
Javascript
C++

Dans notre boucle ci-dessus, dans la boucle intérieure, si lang est égal à “Allemand” alors oublie cette itération uniquement et on continue avec le reste de la boucle. La boucle n’est pas terminée.

Regardons un exemple numérique maintenant:

from math import sqrt
number = 0

for i in range(10):
   number = i ** 2

   if i % 2 == 0:
      continue    # continue ici

   print(str(round(sqrt(number))) + ' au carré est égal à ' + str(number))
1 au carré est égal à 1
3 au carré est égal à 9
5 au carré est égal à 25
7 au carré est égal à 49
9 au carré est égal à 81

Nous avons donc défini une boucle qui parcourt tous les chiffres de 0 à 9 et élève au carré chaque chiffre. Dans notre boucle, à chaque itération, nous vérifions si le nombre est divisible par 2 (nombre pair), si c’est le cas, on continue à exécuter tout en ignorant cette itération.

Et la commande break? Cela nous permet de sortir entièrement d’une boucle lorsqu’une condition externe est remplie. Regardons un exemple simple pour comprendre son fonctionnement en utilisant le même exemple que ci-dessus:

number = 0

for i in range(10):
   number = i ** 2

   if i == 7:
      break

   print(str(round(sqrt(number))) + ' au carré est égal à ' + str(number))
0 au carré est égal à 0
1 au carré est égal à 1
2 au carré est égal à 4
3 au carré est égal à 9
4 au carré est égal à 16
5 au carré est égal à 25
6 au carré est égal à 36

Dans l’exemple ci-dessus, on teste la valeur de i, si celle-ci vaut 7, la commande break arrête la boucle. Notre boucle itère donc sur les entiers de 0 à 6 avant de sortir complètement de la boucle.

Conclusion

Dans cet article, nous avons découvert des applications plus avancées des boucles for, ainsi que leur utilisation dans les workflows de Data Science classique.

Nous avons appris comment parcourir différents types de structures de données et comment utiliser des boucles avec des DataFrames pandas et la bibliothèque matplotlib pour créer plusieurs graphiques.

Enfin, nous avons étudié des techniques plus avancées nous permettant de mieux contrôler le fonctionnement et l’exécution de nos boucles for.