Que signifie SettingWithCopyWarning, et comment le faites-vous disparaître ?

Questions d’entrevue Python et Pandas

Intermédiairepandas

La réponse courte

SettingWithCopyWarning veut dire : Pandas n’est pas sûr que votre affectation touche le DataFrame que vous croyez. Vous avez peut-être écrit dans une copie temporaire. La modification peut être visible, ou disparaître à la ligne suivante, selon la version, la disposition mémoire et le copy-on-write.

Ce n’est pas une erreur de syntaxe. C’est un aveu d’ambiguïté. L’ignorer, c’est accepter un résultat non déterministe.

D’où ça vient

Presque toujours d’une indexation chaînée : deux crochets, deux opérations.

python
df[df["statut"] == "actif"]["remise"] = 0.1

La première indexation fabrique un objet dont Pandas ne garantit pas qu’il soit une vue. La seconde écrit dedans. Si c’était une copie, df n’a pas bougé — et vous ne le voyez pas, parce que l’affectation « a marché » sur un objet jeté.

La même intention, en une opération :

python
df.loc[df["statut"] == "actif", "remise"] = 0.1

Un seul __setitem__, étiquettes explicites. L’avertissement disparaît, et le contrat est clair.

Ce que vous dites de plus

Depuis Pandas 2, le copy-on-write rend cette ambiguïté plus honnête : l’écriture chaînée ne propage plus vers l’original, et les versions récentes peuvent lever une erreur plutôt qu’un warning. En entretien, mentionner CoW montre que vous suivez l’outil, pas un blog de 2018. Ça ne change pas la règle de style : pas d’indexation chaînée à l’écriture.

L’autre source fréquente : un filtre stocké dans une variable, puis muté.

python
actifs = df[df["statut"] == "actif"]
actifs["remise"] = 0.1          # warning ou no-op selon le mode
df.loc[actifs.index, "remise"] = 0.1

Si actifs doit vivre sa vie, actifs = df.loc[...].copy() puis vous mutuez la copie en connaissance de cause.

La relance probable

« Le warning n’est pas apparu, donc c’est bon ? »

Non. L’absence d’avertissement ne prouve pas que vous teniez une vue. Certaines chaînes passent silencieuse. La preuve, c’est une affectation par loc sur l’objet racine, ou une copie assumée.

« Et pour créer une colonne sur un sous-ensemble ? »

Même règle. df.loc[masque, "nouvelle"] = valeur crée ou écrase la colonne aux lignes concernées, NaN ailleurs. Vous ne partez pas d’une slice pour ajouter une colonne à df.

Toutes les questions Python et Pandas