
Pandas ne va nulle part : pourquoi c’est toujours mon choix pour la gestion des données
En apprenant la science des données en 2020, Pandas était l’un des outils les plus populaires. Bien que les nouveaux outils visent à améliorer les faiblesses de Pandas dans la gestion de très grands ensembles de données, j’utilise toujours Pandas pour de nombreuses tâches de nettoyage, de traitement et d’analyse de données. Oui, Pandas me donne du fil à retordre lorsque je travaille avec des milliards de lignes, mais c’est certainement plus que suffisant pour travailler avec quoi que ce soit en dessous.
Je vois que les Pandas sont utilisés non seulement pour l’EDA ou dans les ordinateurs portables, mais également dans les systèmes de production.
Dans cet article, je passerai en revue certaines opérations de nettoyage et de traitement des données pour démontrer les capacités de Pandas.
Commençons par l’ensemble de données, qui contient des unités de gestion de stock (SKU) et une API de recherche de réponses pour ces SKU.
import pandas as pd
search_results = pd.read_csv("search_results.csv")
search_results.head()
Le résultat de la recherche est une liste de dictionnaires et ressemble à ceci :
search_results.loc[0, "search_result"]
"[{'my_id': 'HBCV00007F5Y2B', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00007UPQBM', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00008I29IH', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00006U3ZYB', 'distance': 0.8961254358291626, 'entity': {}},
{'my_id': 'HBCV0000AFA4H6', 'distance': 0.8702399730682373, 'entity': {}},
{'my_id': 'HBCV00009CDGD4', 'distance': 0.86175537109375, 'entity': {}},
{'my_id': 'HBCV000046336T', 'distance': 0.8594968318939209, 'entity': {}},
{'my_id': 'HBCV00009QDZRT', 'distance': 0.8572311997413635, 'entity': {}},
{'my_id': 'HBCV00008E11P3', 'distance': 0.8553324937820435, 'entity': {}},
{'my_id': 'HBV00000C4IY6', 'distance': 0.8539167642593384, 'entity': {}}]
... and 5 entities remaining"Comme nous le voyons dans le résultat, il ne s’agit pas d’une liste appropriée au format de dictionnaire à cause de la dernière partie (« … et 5 entités restantes »). En outre, il est enregistré sous la forme d’une seule chaîne.
Afin d’en faire un meilleur usage, nous devons le convertir en une liste appropriée de dictionnaires. La ligne de code suivante supprime la dernière partie en divisant la chaîne en « … » et prend la première division.
search_results.loc[0, "search_result"].split("...")[0].strip()Cependant, la sortie est toujours une seule chaîne. Nous pouvons utiliser le module ast intégré de Python pour le convertir en liste :
import ast
res = ast.literal_eval(search_results.loc[0, "search_result"].split("...")[0].strip())
res
[{'my_id': 'HBCV00007F5Y2B', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00007UPQBM', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00008I29IH', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00006U3ZYB', 'distance': 0.8961254358291626, 'entity': {}},
{'my_id': 'HBCV0000AFA4H6', 'distance': 0.8702399730682373, 'entity': {}},
{'my_id': 'HBCV00009CDGD4', 'distance': 0.86175537109375, 'entity': {}},
{'my_id': 'HBCV000046336T', 'distance': 0.8594968318939209, 'entity': {}},
{'my_id': 'HBCV00009QDZRT', 'distance': 0.8572311997413635, 'entity': {}},
{'my_id': 'HBCV00008E11P3', 'distance': 0.8553324937820435, 'entity': {}},
{'my_id': 'HBV00000C4IY6', 'distance': 0.8539167642593384, 'entity': {}}]Nous avons maintenant les résultats de la recherche sous la forme d’une véritable liste de dictionnaires. Ce n’était que pour une seule ligne. Nous devons appliquer la même opération à tous les SKU (c’est-à-dire à la colonne SKU entière).
Une option consiste à parcourir toutes les lignes d’une boucle for et à effectuer la même opération. Cependant, ce n’est pas la meilleure option. Nous devrions privilégier les opérations vectorisées lorsque nous le pouvons. Une opération vectorisée signifie essentiellement exécuter le code sur toutes les lignes à la fois.
Sur une seule ligne, j’ai utilisé le fractionnement pour supprimer la dernière partie de la chaîne mais cela n’a pas fonctionné en opération vectorisée. Une option plus robuste semble utiliser une expression régulière.
search_results.loc[:, 'search_result'] = search_results['search_result'].str.replace(r"\.\.\..*", "", regex=True).str.strip()Ce code sélectionne «…» et tout ce qui vient après et les remplace par rien. En d’autres termes, il supprime la partie « … et 5 entités restantes ».
Nous avons maintenant toutes les lignes de la colonne des résultats de recherche sous forme de liste appropriée de dictionnaires.
search_results.loc[10, "search_result"]
"[{'my_id': 'HBCV00007F5Y2B', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00007UPQBM', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00008I29IH', 'distance': 1.0, 'entity': {}},
{'my_id': 'HBCV00006U3ZYB', 'distance': 0.8961254358291626, 'entity': {}},
{'my_id': 'HBCV0000AFA4H6', 'distance': 0.8702399730682373, 'entity': {}},
{'my_id': 'HBCV00009CDGD4', 'distance': 0.86175537109375, 'entity': {}},
{'my_id': 'HBCV000046336T', 'distance': 0.8594968318939209, 'entity': {}},
{'my_id': 'HBCV00009QDZRT', 'distance': 0.8572311997413635, 'entity': {}},
{'my_id': 'HBCV00008E11P3', 'distance': 0.8553324937820435, 'entity': {}},
{'my_id': 'HBV00000C4IY6', 'distance': 0.8539167642593384, 'entity': {}}]"Ils sont toujours enregistrés sous forme de chaîne mais je peux facilement les convertir en liste à l’aide du module ast, ce que je ferai à l’étape suivante.
Ce qui m’intéresse, ce sont les SKU renvoyés dans les résultats de recherche. Je vais créer une nouvelle colonne en extrayant les SKU dans les dictionnaires. Je peux y accéder en utilisant la clé « my_id » du dictionnaire.
Il y a 3 parties de cette opération :
- Convertissez la chaîne de résultat de la recherche en liste à l’aide de la fonction literal_eval
- Extraire le SKU de la clé my_id du dictionnaire
- Faites cela dans une compréhension de liste pour obtenir les SKU de tous les dictionnaires de la liste
Nous pouvons effectuer toutes ces opérations en appliquant une fonction lambda à toutes les lignes comme suit :
search_results.loc[:, "result_skus"] = \
search_results["search_result"].apply(lambda x: [item['my_id'] for item in ast.literal_eval(x)])
search_results.head()
Chaque ligne de la colonne result_skus contient une liste de 10 SKU. Disons que j’ai besoin d’avoir ces 10 SKU sur des lignes différentes. Pour chaque ligne de la colonne sku, 10 lignes seront créées à partir de la liste de la colonne result_skus. Il existe un moyen très simple de procéder dans Pandas, à savoir la fonction d’explosion.
data = search_results[["sku", "result_skus"]].explode("result_skus", ignore_index=True)
data.head()
Nous avons créé une nouvelle trame de données avec la colonne sku et result_skus. Le dessin ci-dessous montre ce que fait la fonction d’éclatement :

Considérez le contraire. Nous avons une trame de données comme indiqué ci-dessus, mais nous souhaitons avoir tous les résultats d’un SKU sur une seule ligne.
Nous pouvons utiliser la fonction groupby pour regrouper les lignes par sku puis appliquer la fonction list sur la colonne result_skus :
new_data = data.groupby("sku", as_index=False)["result_skus"].apply(list)
new_data.head()Cela nous ramènera à l’étape précédente :

À l’aide de la fonction d’explosion, nous avons créé une trame de données avec une ligne distincte pour chaque SKU dans la colonne result_skus. Que se passe-t-il si nous devons les séparer en différentes colonnes au lieu de lignes ?
Une option consiste à appliquer la fonction pd.Series à la colonne result_skus et à concaténer les colonnes résultantes au dataframe d’origine.
new_cols = new_data["result_skus"].apply(pd.Series)
new_data = pd.concat([new_data, new_cols], axis=1)
new_data.head()
Les colonnes de 0 à 9 contiennent les 10 SKU dans la colonne result_skus. Ce code utilisant la fonction apply n’est pas une opération vectorisée.
Nous avons une autre option, vectorisée et beaucoup plus rapide.
new_cols = pd.DataFrame(new_data["result_skus"].tolist())
new_data = pd.concat([new_data, new_cols], axis=1)Ce code nous donnera la même trame de données que ci-dessus mais beaucoup plus rapide.
J’ai démontré une tâche typique de nettoyage et de traitement des données qu’un data scientist ou un analyste peut rencontrer dans le cadre de son travail. Je suis sur le terrain depuis plus de 5 ans et Pandas a toujours suffi à faire ce dont j’avais besoin, sauf lorsque je travaillais de très grands ensembles de données (par exemple des milliards de lignes).
Les outils les mieux adaptés à des ensembles de données aussi volumineux ont une syntaxe similaire à celle de Pandas. Par exemple, PySpark est une sorte de mélange de Pandas et de SQL. Polars est très similaire à Pandas en termes de syntaxe. Ainsi, apprendre et pratiquer Pandas reste une compétence très précieuse pour toute personne travaillant dans le domaine de la science des données et de l’IA.
Merci d’avoir lu.



