How do I fix the SettingWithCopyWarning in pandas?
Short answer
The warning means pandas cannot tell whether you are writing to the original frame or a temporary copy. Do the whole operation in one loc call, and note that pandas 3.0 replaces this warning with a ChainedAssignmentError under Copy-on-Write.
The short version
SettingWithCopyWarning appears when you index a DataFrame twice and then assign, because pandas cannot tell whether you meant to change the original or a temporary slice. Fix it by doing the selection and assignment in a single .loc call. Note that pandas 3.0 changes this: Copy-on-Write is the default there and the warning is gone.
What triggers it
The problem is chained indexing, two square-bracket operations in a row before an assignment.
import pandas as pd
df = pd.DataFrame({"city": ["Leeds", "Bath"], "temp": [12, 15]})
# The pattern that warns on pandas 1.x and 2.x
df[df["temp"] > 13]["temp"] = 99
The first df[...] returns a new object. The second bracket assigns into that new object, which may or may not be a view onto the original. Because the outcome is undefined, pandas warns rather than silently doing the wrong thing.
The safe pattern
Select rows and the target column together inside one .loc, so there is a single, unambiguous operation.
df.loc[df["temp"] > 13, "temp"] = 99
This reads as "for the rows where temp exceeds 13, set temp to 99", and it always writes to df itself.
If you meant to work on a copy
Sometimes you genuinely want an independent subset to modify. Make that intent explicit with .copy(), and the warning disappears because there is no ambiguity.
warm = df[df["temp"] > 13].copy()
warm["temp"] = 99 # changes warm only, df is untouched
The version difference to know
From pandas 3.0, Copy-on-Write is the default. Under Copy-on-Write, a slice never silently shares memory with its parent, so a chained assignment simply does not change the original at all. The old SettingWithCopyWarning is not merely switched off: it is replaced by a new ChainedAssignmentError warning, which tells you the write had no effect rather than leaving you guessing. Either way, code that relied on chained assignment "working" stops working on 3.0, which is a good reason to move to the single .loc form now. Because a large installed base is still on 1.x and 2.x (where the original warning appears), the .loc pattern above is the safe choice across every version.