Fix: Pandas SettingWithCopyWarning
Quick Answer
Learn how to fix the Pandas SettingWithCopyWarning by using .loc[], .copy(), and avoiding chained indexing in your DataFrame operations.
The Error
You filter a DataFrame and try to assign a value to a column:
df_filtered = df[df['status'] == 'active']
df_filtered['score'] = 100Pandas throws this warning:
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 insteadThe operation might appear to work, but the original DataFrame remains unchanged — or worse, the behavior is unpredictable depending on how Pandas decides to handle the internal memory.
Why This Happens
Pandas uses a concept called views and copies when you slice a DataFrame. A view shares the underlying data with the original DataFrame. A copy creates independent data.
The problem is that Pandas doesn’t guarantee which one you get. When you chain operations like df[df['x'] > 5]['y'] = 10, Pandas may create a temporary copy for the filter step, and your assignment writes to that copy instead of the original DataFrame. The warning tells you exactly this: your assignment might not do what you intend.
This is called chained indexing — accessing data through multiple bracket operations in sequence. It’s the root cause of almost every SettingWithCopyWarning.
Fix 1: Use .loc[] for Assignment
The most common fix. Use .loc[] to combine filtering and assignment in a single operation:
# Wrong - chained indexing
df[df['status'] == 'active']['score'] = 100
# Correct - single .loc[] call
df.loc[df['status'] == 'active', 'score'] = 100.loc[] guarantees you’re writing to the original DataFrame, not a copy. This is the recommended approach in the official Pandas documentation.
For multiple conditions:
df.loc[(df['status'] == 'active') & (df['age'] > 25), 'score'] = 100Pro Tip:
.loc[]uses label-based indexing. For position-based indexing, use.iloc[]. Never mix them —df.loc[0]anddf.iloc[0]can return different rows if the index doesn’t start at 0.
Fix 2: Create an Explicit Copy with .copy()
If you need a separate DataFrame to work with and don’t intend to modify the original, call .copy() explicitly:
# This triggers the warning
df_subset = df[df['region'] == 'US']
df_subset['revenue'] = df_subset['revenue'] * 1.1
# This is safe
df_subset = df[df['region'] == 'US'].copy()
df_subset['revenue'] = df_subset['revenue'] * 1.1.copy() creates a completely independent DataFrame. Changes to df_subset won’t affect df, and Pandas won’t warn you because it knows the data is independent.
This is the right approach when you’re building a transformed dataset for analysis or export, not trying to update the original.
Fix 3: Avoid Chained Indexing Entirely
Chained indexing is any time you use multiple [] operators in sequence:
# Chained indexing - bad
df['col1']['col2']
df[df['x'] > 5]['y']
df.iloc[0:5]['name']
# Single indexing - good
df.loc[df['x'] > 5, 'y']
df.loc[0:5, 'name']Every time you chain [], Pandas might return a view or a copy. You can’t predict which one. Rewrite all chained access patterns to use a single .loc[] or .iloc[] call.
For complex operations where you need multiple steps, use .copy() at the start:
working_df = df[df['active']].copy()
working_df['new_col'] = working_df['a'] + working_df['b']
working_df['category'] = working_df['new_col'].apply(categorize)Common Mistake: Using
df.query()doesn’t avoid this issue.df.query('x > 5')['y'] = 10is still chained indexing. Usedf.loc[df['x'] > 5, 'y'] = 10instead.
Fix 4: Enable Copy-on-Write Mode
Pandas 2.0 introduced Copy-on-Write (CoW) as an opt-in feature. In Pandas 3.0, it becomes the default behavior. CoW eliminates SettingWithCopyWarning entirely by making every indexing operation return a copy, but deferring the actual copy until you try to modify the data.
Enable it at the top of your script:
pd.options.mode.copy_on_write = TrueOr set it in your environment:
import pandas as pd
pd.set_option('mode.copy_on_write', True)
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
subset = df[df['a'] > 1]
subset['b'] = 99 # No warning, modifies only subsetWith CoW enabled, the warning disappears because Pandas guarantees that modifications to a subset never affect the original. This is the future-proof solution if you’re on Pandas 2.0+.
Note: CoW changes the behavior of view-based mutations. If your code relies on modifying a slice to update the original DataFrame, it will silently stop working under CoW. Audit your code before enabling it.
Fix 5: Use .assign() for New Columns
The .assign() method creates a new DataFrame with the additional column, leaving the original untouched:
# Instead of this (might warn)
df_filtered = df[df['active']]
df_filtered['doubled'] = df_filtered['value'] * 2
# Use this
df_filtered = df[df['active']].assign(doubled=lambda x: x['value'] * 2).assign() is method-chainable, making it useful for building data pipelines:
result = (
df[df['active']]
.assign(
doubled=lambda x: x['value'] * 2,
label=lambda x: x['name'].str.upper()
)
.sort_values('doubled', ascending=False)
)This pattern is inherently safe because .assign() always returns a new DataFrame.
Fix 6: Handle Inplace Operations Carefully
Some Pandas methods have an inplace parameter. When used on a slice, they can trigger the warning:
df_subset = df[df['score'] > 50]
df_subset.fillna(0, inplace=True) # WarningTwo fixes:
# Option 1: Use .copy()
df_subset = df[df['score'] > 50].copy()
df_subset.fillna(0, inplace=True)
# Option 2: Avoid inplace entirely (preferred)
df_subset = df[df['score'] > 50].fillna(0)The Pandas core team has discussed deprecating inplace in future versions. Method chaining without inplace is the modern Pandas style and avoids this class of warnings entirely.
Fix 7: Fix Column Assignment on Filtered DataFrames
A common pattern that triggers the warning is filtering a DataFrame, storing it in a variable, then modifying it later:
active_users = df[df['is_active']]
# Many lines of code later...
active_users['last_seen'] = pd.Timestamp.now() # WarningThe fix depends on your intent:
If you want to update the original DataFrame:
df.loc[df['is_active'], 'last_seen'] = pd.Timestamp.now()If you want to work with a separate copy:
active_users = df[df['is_active']].copy()
active_users['last_seen'] = pd.Timestamp.now()The key insight is to decide upfront whether you’re modifying the original or working with independent data, and use the appropriate method from the start.
Fix 8: Suppress the Warning (Last Resort)
If you understand the behavior and the warning is a false positive in your specific case, you can suppress it:
import warnings
warnings.filterwarnings('ignore', category=pd.errors.SettingWithCopyWarning)Or for a specific block:
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=pd.errors.SettingWithCopyWarning)
df_subset['col'] = valuesOr using Pandas options:
pd.options.mode.chained_assignment = None # Suppress
pd.options.mode.chained_assignment = 'warn' # Default
pd.options.mode.chained_assignment = 'raise' # Raise exceptionWarning: Only suppress the warning if you’ve verified that your code produces correct results. Setting chained_assignment = 'raise' during development helps catch issues early — switch to None only in production after thorough testing.
Still Not Working?
If you’re still seeing the warning after applying these fixes:
Check for indirect chaining. Functions that return filtered DataFrames can hide chained indexing. If a function returns
df[df['x'] > 5], the caller modifying that result triggers the warning. Return.copy()from the function instead.Watch for MultiIndex DataFrames. Slicing a MultiIndex DataFrame with
.loc[]can sometimes return views unexpectedly. Use.copy()after MultiIndex slicing to be safe.Upgrade Pandas. The warning behavior has changed across versions. Pandas 2.0+ with CoW mode eliminates the issue entirely. Check your version with
pd.show_versions().Check NumPy array sharing. If you created a DataFrame from a NumPy array, the DataFrame might share memory with the array. Changes to one affect the other. Use
df = pd.DataFrame(array.copy())to break the link.Review your IDE or notebook settings. Jupyter notebooks can sometimes mask or duplicate warnings. Restart the kernel and test in a clean environment.
Look for third-party library interactions. Libraries like scikit-learn or matplotlib might modify DataFrames internally, triggering the warning from their code. Update those libraries or wrap their inputs with
.copy().
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Celery Task Not Received or Not Executing
Fix Celery tasks not being received or executed by resolving broker connections, autodiscovery issues, task name mismatches, and worker configuration.
Fix: AWS Lambda Unable to import module / Runtime.ImportModuleError
How to fix the AWS Lambda Runtime.ImportModuleError and Unable to import module error caused by wrong handler paths, missing dependencies, layer issues, and packaging problems.
Fix: Python TypeError: unhashable type: 'list'
Learn why Python raises TypeError unhashable type list, dict, or set and how to fix it when using dictionary keys, sets, groupby, dataclasses, and custom classes.
Fix: Python ValueError: Too Many Values to Unpack
Learn why Python raises ValueError too many values to unpack and how to fix it when unpacking tuples, iterating dictionaries, parsing files, and using zip or enumerate.