When do I need to call copy() on a DataFrame?
Short answer
Call copy() when you take a subset and intend to modify it independently of the original. Under Copy-on-Write in pandas 3.0 this is safer by default, but an explicit copy still makes your intent clear.
The short version
Call .copy() when you take a subset of a frame and intend to modify it without affecting the original. Without it, whether the subset shares memory with its parent has historically been ambiguous, which is the root of the SettingWithCopyWarning. From pandas 3.0, Copy-on-Write makes the behaviour safe and predictable, but an explicit copy still documents your intent.
The classic situation
import pandas as pd
df = pd.DataFrame({"city": ["Leeds", "Bath"], "amount": [5, 7]})
subset = df[df["amount"] > 6] # is this a view or a copy? historically unclear
subset["amount"] = 99 # does this change df? historically unclear
That ambiguity is exactly what the warning was telling you about. Making the copy explicit removes all doubt.
subset = df[df["amount"] > 6].copy()
subset["amount"] = 99 # df is definitely untouched
What changed in pandas 3.0
Under Copy-on-Write, which is the default from pandas 3.0, a subset never silently shares memory with its parent in a way that lets a write leak through. Modifying a slice simply does not affect the original, and chained assignment raises a ChainedAssignmentError warning telling you the write had no effect.
This is a genuine improvement: the ambiguity is gone. But it also means code that relied on a view writing through to the parent will now silently stop working, which is a good reason to make every intent explicit.
The rule that works on every version
Two clear intents, two clear expressions:
To modify a subset independently, copy it.
warm = df[df["amount"] > 6].copy()
warm["amount"] = 99
To modify the original, index into the original directly in one .loc call.
df.loc[df["amount"] > 6, "amount"] = 99
Written this way, the code says what it means, works identically on pandas 1.x, 2.x and 3.x, and never produces a warning.
Copy is not free
A copy duplicates the data, so on a large frame it costs memory and time. Do not scatter .copy() everywhere defensively. Use it where you are about to modify a subset, which is precisely where it earns its cost. Under Copy-on-Write, pandas itself defers the actual copying until a write happens, so the cost is only paid when it is needed.
Copy is deep for the data, not for objects
df.copy() duplicates the values, so changing the copy does not affect the original. But if a cell contains a mutable Python object (a list, a dictionary), the copy still points at the same object, exactly as a shallow copy would. Storing mutable objects in DataFrame cells is best avoided for this and several other reasons.
The habit
When you write subset = df[...] and then intend to change subset, add .copy() in the same breath. It costs one method call, removes an entire class of confusing bug, and tells the next reader exactly what you meant.
Enjoying these?
Go from piecing together answers to real fluency.
Our courses take you from looking things up to doing the work with confidence, step by step on real data.
Browse the courses