Accéder au contenu principal

SettingWithCopyWarning dans Pandas : Comment corriger cet avertissement

Apprenez à corriger l'avertissement SettingWithCopyWarning de Pandas : Une valeur tente d'être définie sur une copie d'une tranche d'un DataFrame.
Actualisé 17 nov. 2024  · 8 min de lecture

SettingWithCopyWarning est un avertissement que Pandas peut soulever lorsque nous effectuons une affectation à un DataFrame. Cela peut se produire lorsque nous utilisons des affectations enchaînées ou lorsque nous utilisons un DataFrame créé à partir d'une tranche. Il s'agit d'une source courante de bogues dans le code Pandas, à laquelle nous avons tous déjà été confrontés. Il peut être difficile de déboguer car l'avertissement peut apparaître dans un code qui semble fonctionner correctement.

Il est important de comprendre le site SettingWithCopyWarning car il signale des problèmes potentiels liés à la manipulation des données. Cet avertissement indique que votre code ne modifie peut-être pas les données comme prévu, ce qui peut entraîner des conséquences imprévues et des bogues obscurs difficiles à détecter.

Dans cet article, nous allons explorer le site SettingWithCopyWarning dans pandas et comment l'éviter. Nous discuterons également de l'avenir de Pandas et de la façon dont l'option copy_on_write modifiera notre façon de travailler avec les DataFrames.

Vues et copies de DataFrame

Lorsque nous sélectionnons une tranche d'un DataFrame et que nous l'assignons à une variable, nous pouvons obtenir soit une vue, soit une nouvelle copie du DataFrame.

Dans le cas d'une vue, la mémoire est partagée entre les deux DataFrame. Cela signifie que la modification d'une valeur d'une cellule présente dans les deux DataFrame les modifiera toutes les deux.

Exemple de vue DataFrame Pandas

Lors d'une copie, une nouvelle mémoire est allouée et un DataFrame indépendant contenant les mêmes valeurs que l'original est créé. Dans ce cas, les deux DataFrame sont des entités distinctes, de sorte que la modification d'une valeur dans l'un d'eux n'affecte pas l'autre.

Exemple de copie d'un DataFrame

Pandas essaie d'éviter de créer une copie lorsqu'il le peut afin d'optimiser les performances. Cependant, il est impossible de prévoir à l'avance si nous obtiendrons une vue ou une copie. La question SettingWithCopyWarning est soulevée chaque fois que nous attribuons une valeur à un DataFrame dont on ne sait pas s'il s'agit d'une copie ou d'une vue d'un autre DataFrame.

Comprendre la SettingWithCopyWarning avec des données réelles

Nous utiliserons ce jeu de données Kaggle Real Estate Data London 2024 pour apprendre comment SettingWithCopyWarning se produit et comment y remédier. 

Cet ensemble de données contient des données récentes sur l'immobilier à Londres. Voici un aperçu des colonnes présentes dans l'ensemble de données :

  • addedOn: La date à laquelle l'inscription a été ajoutée.
  • title: Le titre de l'annonce.
  • descriptionHtml: Une description HTML de l'annonce.
  • propertyType: Le type de bien. La valeur sera "Not Specified" si le type n'a pas été spécifié.
  • sizeSqFeetMax: La taille maximale en pieds carrés.
  • bedrooms: Le nombre de chambres à coucher.
  • listingUpdatedReason: Raison de la mise à jour de l'annonce (par exemple, nouvelle annonce, réduction de prix).
  • price: Le prix de l'annonce en livres sterling.

Exemple avec une variable temporaire explicite

Supposons que l'on nous dise que les biens dont le type n'est pas spécifié sont des maisons. Nous voulons donc mettre à jour toutes les lignes où propertyType est égal à "Not Specified" en "House". Une façon de procéder consiste à filtrer les lignes dont le type de propriété n'est pas spécifié dans une variable DataFrame temporaire et à mettre à jour les valeurs de la colonne propertyType de la façon suivante :

import pandas as pd
dataset_name = "realestate_data_london_2024_nov.csv"
df = pd.read_csv(dataset_name)

# Obtain all rows with unspecified property type
no_property_type = df[df["propertyType"] == "Not Specified"]

# Update the property type to “House” on those rows
no_property_type["propertyType"] = "House"

En exécutant ce code, pandas produira le fichier SettingWithCopyWarning:

SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  no_property_type["propertyType"] = "House"

La raison en est que pandas ne peut pas savoir si le DataFrame no_property_type est une vue ou une copie de df

Lorsque nous assignons une partie du DataFrame à une variable, nous pouvons obtenir soit une vue, soit une copie, mais nous ne pouvons pas savoir laquelle.

C'est un problème car le comportement du code suivant peut être très différent selon qu'il s'agit d'une vue ou d'une copie. 

Dans cet exemple, notre objectif est de modifier le DataFrame original. Cela ne se produira que si no_property_type est une vue. Si le reste de notre code suppose que df a été modifié, il peut se tromper car il n'y a aucun moyen de garantir que c'est le cas. En raison de ce comportement incertain, Pandas lance un avertissement pour nous en informer.

Même si notre code s'exécute correctement parce que nous avons obtenu une vue, nous pourrions en obtenir une copie lors d'exécutions ultérieures, et le code ne fonctionnerait pas comme prévu. Il est donc important de ne pas ignorer cet avertissement et de s'assurer que notre code fera toujours ce que nous voulons qu'il fasse.

Exemple avec une variable temporaire cachée

Dans l'exemple précédent, il est clair qu'une variable temporaire est utilisée car nous affectons explicitement une partie du DataFrame à une variable nommée no_property_type.

Toutefois, dans certains cas, ce n'est pas aussi explicite. L'exemple le plus courant de SettingWithCopyWarning est celui de l'indexation en chaîne. Supposons que nous remplacions les deux dernières lignes par une seule :

df[df["propertyType"] == "Not Specified"]["propertyType"] = "House"

À première vue, il ne semble pas qu'une variable temporaire soit créée. Toutefois, l'exécution de cette procédure entraîne également l'envoi d'un message à l'adresse SettingWithCopyWarning

La façon dont ce code est exécuté est la suivante :

  1. df[df["propertyType"] == "Not Specified"] est évaluée et stockée temporairement en mémoire. 
  2. L'index ["propertyType"] de cet emplacement de mémoire temporaire est consulté. 

Les accès à l'index sont évalués un par un et l'indexation en chaîne donne donc lieu au même avertissement, car nous ne savons pas si les résultats intermédiaires sont des vues ou des copies.

Les accès à l'index sont évalués un par un et, par conséquent, l'indexation en chaîne donne lieu au même avertissement car nous ne savons pas si les résultats intermédiaires sont des vues ou des copies. Le code ci-dessus est essentiellement le même que le code suivant :

tmp = df[df["propertyType"] == "Not Specified"]
tmp["propertyType"] = "House"

Cet exemple est souvent appelé indexation chaînée car nous enchaînons les accès indexés à l'aide de []. Tout d'abord, nous accédons à [df["propertyType"] == "Not Specified"] puis à ["propertyType"].

Indexation en chaîne

Comment résoudre le problème de la SettingWithCopyWarning

Nous allons apprendre à écrire notre code de manière à ce qu'il n'y ait pas d'ambiguïté et que le site SettingWithCopyWarning ne soit pas déclenché. Nous avons appris que l'avertissement provient d'une ambiguïté sur la question de savoir si un DataFrame est une vue ou une copie d'un autre DataFrame.

La solution consiste à s'assurer que chaque DataFrame que nous créons est une copie si nous voulons qu'elle soit une copie ou une vue si nous voulons qu'elle soit une vue.

Modifier en toute sécurité le DataFrame d'origine avec loc 

Corrigeons le code de l'exemple ci-dessus où nous voulons modifier le DataFrame original. Pour éviter d'utiliser une variable temporaire, utilisez la propriété loc indexer.

df.loc[df["propertyType"] == "Not Specified", "propertyType"] = "House"

Avec ce code, nous agissons directement sur le DataFrame original df via la propriété loc indexer, il n'y a donc pas besoin de variables intermédiaires. C'est ce que nous devons faire lorsque nous voulons modifier directement le DataFrame d'origine.

À première vue, cela peut ressembler à une indexation en chaîne, car il y a encore des paramètres, mais ce n'est pas le cas. Les crochets [] définissent chaque indexation.

Loc double index vs indexation chaînée

Notez que l'utilisation de loc n'est sûre que si vous affectez directement une valeur, comme nous l'avons fait ci-dessus. Si nous utilisons plutôt une variable temporaire, nous retombons dans le même problème. Voici deux exemples de codes qui ne résolvent pas le problème : 

  1. Utilisation de loc avec une variable temporaire :
# Using loc plus temporary variable doesn’t fix the issue
no_property_type = df.loc[df["propertyType"] == "Not Specified"]
no_property_type["propertyType"] = "House"
  1. Utilisation de loc avec un index (identique à l'indexation en chaîne) :
# Using loc plus indexing is the same as chained indexing
df.loc[df["propertyType"] == "Not Specified"]["propertyType"] = "House"

Ces deux exemples ont tendance à troubler les gens parce que l'on croit souvent, à tort, que tant qu'il y a une adresse loc, on modifie les données d'origine. Ce n'est pas le cas. La seule façon de s'assurer que la valeur est affectée au DataFrame d'origine est de l'affecter directement à l'aide d'un seul loc sans indexation séparée.

Travailler en toute sécurité avec une copie du DataFrame original avec copy()

Lorsque nous voulons nous assurer que nous opérons sur une copie du DataFrame, nous devons utiliser la méthode suivante la méthode .copy() .

Supposons que l'on nous demande d'analyser le prix au mètre carré des biens immobiliers. Nous ne voulons pas modifier les données originales. L'objectif est de créer un nouveau DataFrame avec les résultats de l'analyse pour l'envoyer à une autre équipe.

La première étape consiste à filtrer certaines lignes et à nettoyer les données. Plus précisément, nous devons

  • Supprimez les lignes où sizeSqFeetMax n'est pas défini.
  • Supprimez les lignes où le site price est "POA" (prix sur demande).
  • Convertissez les prix en valeurs numériques (dans l'ensemble de données original, les prix sont des chaînes au format suivant : "£25,000,000").

Nous pouvons effectuer les étapes ci-dessus à l'aide du code suivant :

# 1. Filter out all properties without a size or a price
properties_with_size_and_price = df[df["sizeSqFeetMax"].notna() & (df["price"] != "POA")]

# 2. Remove the £ and , characters from the price columns
properties_with_size_and_price["price"] = properties_with_size_and_price["price"].str.replace("£", "", regex=False).str.replace(",", "", regex=False)

# 3. Convert the price column to numeric values
properties_with_size_and_price["price"] = pd.to_numeric(properties_with_size_and_price["price"])

Pour calculer le prix au pied carré, nous créons une nouvelle colonne dont les valeurs sont le résultat de la division de la colonne price par la colonne sizeSqFeetMax:

properties_with_size_and_price["pricePerSqFt"] = properties_with_size_and_price["price"] / properties_with_size_and_price["sizeSqFeetMax"]

Si nous exécutons ce code, nous obtenons à nouveau le site SettingWithCopyWarning. Cela ne devrait pas être une surprise car nous avons explicitement créé et modifié une variable DataFrame temporaire properties_with_size_and_price.

Puisque nous voulons travailler sur une copie des données plutôt que sur le DataFrame original, nous pouvons résoudre le problème en nous assurant que properties_with_size_and_price est une copie fraîche du DataFrame et non une vue en utilisant la méthode la méthode .copy() sur la première ligne :

properties_with_size_and_price = df[df["sizeSqFeetMax"].notna() & (df["price"] != "POA")].copy()

Ajouter de nouvelles colonnes en toute sécurité

La création de nouvelles colonnes se comporte de la même manière que l'attribution de valeurs. Chaque fois qu'il est ambigu de savoir si nous travaillons avec une copie ou une vue, pandas lèvera un SettingWithCopyWarning.

Si nous voulons travailler avec une copie des données, nous devons les copier explicitement à l'aide de la méthode .copy(). Ensuite, nous sommes libres d'attribuer une nouvelle colonne de la manière que nous souhaitons. C'est ce que nous avons fait en créant la colonne pricePerSqFt dans l'exemple précédent.

En revanche, si nous voulons modifier le DataFrame d'origine, il y a deux cas à considérer. 

  1. Si la nouvelle colonne s'étend sur toutes les lignes, nous pouvons modifier directement le DataFrame d'origine. Cela ne provoquera pas d'avertissement car nous ne sélectionnerons pas un sous-ensemble de lignes. Par exemple, nous pourrions ajouter une colonne note pour chaque ligne où le type de maison est manquant : 
df["notes"] = df["propertyType"].apply(lambda house_type: "Missing house type" if house_type == "Not Specified" else "")
  1. Si la nouvelle colonne ne définit des valeurs que pour un sous-ensemble de lignes, nous pouvons utiliser la propriété d'indexation loc. Par exemple :
df.loc[df["propertyType"] == "Not Specified", "notes"] = "Missing house type"

Notez que dans ce cas, la valeur des colonnes qui n'ont pas été sélectionnées sera indéfinie. La première approche est donc préférable car elle nous permet de spécifier une valeur pour chaque ligne.

SettingWithCopyWarning Erreur dans Pandas 3.0

Pour l'instant, SettingWithCopyWarning n'est qu'un avertissement, pas une erreur. Notre code est toujours exécuté, et Pandas nous informe simplement de faire attention. 

Selon la documentation la documentation officielle de PandasSettingWithCopyWarning ne sera plus utilisé à partir de la version 3.0 et sera remplacé par une véritable fonction erreur par défaut, ce qui permettra d'appliquer des normes de code plus strictes.

Informations sur la mise à jour de Pandas 3.0

Pour s'assurer que notre code reste compatible avec les futures versions de pandas, il est recommandé de le mettre à jour pour qu'il génère une erreur au lieu d'un avertissement.

Pour ce faire, vous devez définir l'option suivante après l'importation de pandas :

import pandas as pd
pd.options.mode.copy_on_write = True

En ajoutant cela au code existant, nous nous assurons de traiter chaque affectation ambiguë dans notre code et nous nous assurons que le code fonctionne toujours lorsque nous mettons à jour pandas 3.0.

Conclusion

Le site SettingWithCopyWarning apparaît chaque fois que notre code rend ambiguë la question de savoir si une valeur que nous modifions est une vue ou une copie. Nous pouvons y remédier en étant toujours explicites sur ce que nous voulons :

  • Si nous voulons travailler avec une copie, nous devons la copier explicitement en utilisant la méthode copy().
  • Si nous voulons modifier le DataFrame original, nous devons utiliser la propriété loc indexer et assigner la valeur directement lors de l'accès aux données sans utiliser de variables intermédiaires.

Bien qu'il ne s'agisse pas d'une erreur, nous ne devrions pas ignorer cet avertissement car il peut conduire à des résultats inattendus. De plus, à partir de Pandas 3.0, cela deviendra une erreur par défaut, nous devrions donc protéger notre code en activant la fonction Copy-on-Write dans notre code actuel à l'aide de la commande pd.options.mode.copy_on_write = True. Cela permettra de s'assurer que le code reste fonctionnel pour les futures versions de Pandas.

FAQ

Pourquoi est-ce que j'obtiens l'avertissement SettingWithCopyWarning même lorsque j'utilise .loc ?

L'utilisation de loc est toujours sûre et ne déclenche pas d'avertissement ni n'entraîne de comportement inattendu, à condition que df n'ait pas été créé à partir d'un autre DataFrame. Par exemple, si df est le DataFrame qui a été créé à partir de la lecture de l'ensemble de données, il n'y a pas de danger. Toutefois, s'il s'agit du résultat d'un calcul intermédiaire, il n'est sûr que si le DataFrame a été copié à l'aide de la méthode copy().

Comment supprimer l'avertissement SettingWithCopy ?

Nous pouvons utiliser le paquet warnings et ajouter le code :

import warnings
warnings.simplefilter(action='ignore', category=pd.errors.SettingWithCopyWarning)

Cependant, il est fortement conseillé de ne pas supprimer ou ignorer cet avertissement, car il signifie que votre code ne fait peut-être pas ce que vous pensez qu'il fait.

Comment ignorer l'avertissement SettingWithCopy ?

Nous pouvons ignorer l'avertissement en utilisant temporairement le paquetage warnings et en enveloppant notre code avec :

import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore", category=pd.errors.SettingWithCopyWarning)
  # Our code goes here

Cependant, il est fortement conseillé de ne pas supprimer ou ignorer cet avertissement, car il signifie que votre code ne fait peut-être pas ce que vous pensez qu'il fait.

Comment corriger l'avertissement SettingWithCopyWarning lors de l'ajout d'une nouvelle colonne ?

La correction de l'avertissement lors de l'ajout d'une colonne se fait de la même manière que pour l'attribution de valeurs. Veillez à copier d'abord le DataFrame si vous avez l'intention de travailler sur une copie des données ou d'utiliser loc.

J'ai suivi ce guide et j'obtiens toujours l'avertissement SettingWithCopyWarning. Comment puis-je y remédier ?

Sur les versions les plus récentes de pandas, suivre les pratiques de ce tutoriel ne devrait pas déclencher d'avertissement.

Si vous utilisez une version plus ancienne, nous vous conseillons de la mettre à jour vers la dernière version stable. Les versions antérieures sont connues pour avoir des bogues autour de cet avertissement et pour le déclencher alors qu'il n'y a pas de problème avec le code.

Si la version de pandas est à jour, il est probable que le problème sous-jacent soit un cas d'indexation en chaîne non évident. Examinez attentivement la manière dont le DataFrame a été créé.


Photo of François Aubry
Author
François Aubry
LinkedIn
L'enseignement a toujours été ma passion. Dès mes premiers jours d'études, j'ai cherché avec enthousiasme des occasions de donner des cours particuliers et d'aider d'autres étudiants. Cette passion m'a amenée à poursuivre un doctorat, où j'ai également été assistante d'enseignement pour soutenir mes efforts académiques. Au cours de ces années, j'ai trouvé un immense épanouissement dans le cadre d'une classe traditionnelle, en favorisant les liens et en facilitant l'apprentissage. Cependant, avec l'avènement des plateformes d'apprentissage en ligne, j'ai reconnu le potentiel de transformation de l'éducation numérique. En fait, j'ai participé activement au développement d'une telle plateforme dans notre université. Je suis profondément engagée dans l'intégration des principes d'enseignement traditionnels avec des méthodologies numériques innovantes. Ma passion est de créer des cours qui sont non seulement attrayants et instructifs, mais aussi accessibles aux apprenants à l'ère du numérique.
Sujets

Apprenez les Pandas avec ces cours !

cours

Joining Data with pandas

4 hr
164.3K
Learn to combine data from multiple tables by joining data together using pandas.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow
Apparenté

blog

Les 20 meilleures questions d'entretien pour les flocons de neige, à tous les niveaux

Vous êtes actuellement à la recherche d'un emploi qui utilise Snowflake ? Préparez-vous à répondre à ces 20 questions d'entretien sur le flocon de neige pour décrocher le poste !
Nisha Arya Ahmed's photo

Nisha Arya Ahmed

20 min

blog

Les 32 meilleures questions d'entretien sur AWS et leurs réponses pour 2024

Un guide complet pour explorer les questions d'entretien AWS de base, intermédiaires et avancées, ainsi que des questions basées sur des situations réelles. Il couvre tous les domaines, garantissant ainsi une stratégie de préparation bien équilibrée.
Zoumana Keita 's photo

Zoumana Keita

30 min

See MoreSee More