Itérer sur un dataframe Pandas (Partie 1/3)

Comment itérer efficacement sur les lignes d'un dataframe Pandas.

De prime abord, il peut sembler étrange de vouloir parcourir séquentiellement un ensemble de données qui, par essence, peut être très volumineux. Bien qu'on se dise qu'un tel besoin est contre-nature, cette question a été maintes fois posées à travers les différents forums spécialisés, aussi elle mérite qu'on s'y attarde.

Comme tout ce que l'on entreprend avec la librairie Pandas, il existe une multitude de façon d'itérer sur les lignes d'un dataframe. Néanmoins, toutes les solutions ne se valent pas en terme de consommation mémoire et de temps d'exécution.
Nous avons découpé cet article en 3 parties et aborderons dans ce premier volet les méthodes de base, celles auxquelles tout le monde pense en premier. Il s'agit des solutions à proscrire, néanmoins pour être complet, il convient de les passer en revue.

Les données sur lesquelles nous allons travailler :

Pour les besoins de cet article nous allons travailler sur la base clients d'une banque. Cette base contient 41.188 lignes pour 21 colonnes.
Sur ces 21 colonnes, 11 sont de nature qualitative (situation maritale, niveau d'éducation, etc ...) et 10 sont quantitatives (âge, nombre de campagnes dont le client a fait l'objet, etc ...).
C'est précisément le nombre de campagnes qui va nous intéresser et pour l'exemple, disons que nous allons simplement incrémenter de 1 cette variable.
Bien entendu, Pandas permet d'effectuer cette opération en 1 seule ligne de façon optimisée, mais nous voulons itérer sur les lignes.

Chargeons tout d'abord nos données :


import pandas as pd
bank = pd.read_csv('bank-datas.csv', sep=';',)

La variable que nous interesse se nomme 'campaign'.

1. La méthode iterrows

La première méthode est iterrows, elle va convertir chaque ligne en série. Les performances sont médiocres et cette méthode est à éviter autant que possible.


%%timeit -n 10
nb_campaign = []
for index, row in bank.iterrows():
    nb_campaign.append(row['campaign'] + 1)

Pour l'exemple, nous créons un dataframe vide puis lui ajoutons à chaque itération une nouvelle ligne. Pour mesurer les performances, nous affichons enfin, via la librairie timeit, le temps d'exécution par boucle.

Verdict : un temps moyen de 1.86 secondes par itération.

2. Les boucles For

Plus encore que la première méthode iterrows, l'usage des boucles est sans aucun doute un des premiers reflexes pour qui veux parcourir les enregistrements d'un dataframe. L'idée est de boucler sur un dataframe en passant par l'incrémentation d'un index au regard du nombre d'enregistrements retourné par un range, par exemple. Il suffit ensuite d'utiliser cet index sur des méthodes loc ou iloc.
Bien que quelque peu plus rapides que la méthode précédente, chaque ligne est, là encore, convertie en série.


%%timeit -n 10
nb_campaign = []
for index in range(len(bank)):
    nb_campaign.append(bank['campaign'].loc[index] + 1)

Verdict : un temps moyen de 602 ms par itération.

3. La méthode apply

Identique en performance aux boucles for, la méthode apply a néanmoins le mérite d'être plus compréhensible et plus esthétique.


%%timeit -n 10
nb_campaign = bank.apply(lambda row: row['campaign'] + 1, axis=1).to_list()

Verdict : un temps moyen de 408 ms par itération.

4. La méthode itertuples

Probablement moins connue que iterrows, la méthode itertuples suit le même principe. Les lignes ne sont plus converties en série mais en tuples.
S'agissant d'un objet plus léger, les performances s’en trouvent améliorées.


%%timeit -n 10
nb_campaign = []
for row in bank.itertuples():
    nb_campaign.append(row.campaign + 1)

Verdict : un temps moyen de 97.2 ms par itération.

Conclusion

Les performances se sont améliorées mais restent néanmoins relativement faibles. Dans une seconde partie nous évoquerons les compréhensions de liste, puis dans une troisième et dernière partie les vectorisations (Pandas puis Numpy), pour lesquelles, nous le verrons, les performances seront enfin au rendez-vous.


Retrouvez dans la rubrique "Nos datasets" toutes les données dont vous aurez besoin pour tester et pratiquer !