## TL;DR
Never assign through chained indexing like `df[mask]["col"] = x`; always use `.loc[mask, "col"] = x`. With copy-on-write (default since pandas 3), chained assignment never writes through to the original frame, it fails silently instead of warning.

```text
pandas copy-on-write chained assignment behavior
```

## Use this when
- An assignment to a filtered or sliced dataframe does not stick
- You see SettingWithCopyWarning on pandas 2, or silence on pandas 3
- You are migrating code to copy-on-write semantics

## Not for this skill when
- Merges fail on key mismatches
- `read_csv` misparses a column
- `apply` is too slow and needs vectorization

## Steps

1. Confirm the chained assignment is the problem. This pattern looks right but writes to a temporary:

```python
df[df["status"] == "active"]["score"] = 0   # does nothing under CoW
print((df["status"] == "active").sum(), (df["score"] == 0).sum())
```
Expected output: the filter matches rows but zero rows got the new value. The assignment went to a throwaway copy.

2. Rewrite every chained assignment as a single `.loc` call:

```python
df.loc[df["status"] == "active", "score"] = 0
print((df.loc[df["status"] == "active", "score"] == 0).all())
```
Expected output: `True`. One indexing operation, so pandas knows exactly which cells to update.

3. For multi-step transforms, assign the intermediate explicitly instead of chaining:

```python
filtered = df[df["status"] == "active"].copy()
filtered["score"] = filtered["score"] * 2
```
Expected output: `filtered` is an independent frame you own, and assignments to it always work. Under copy-on-write the `.copy()` is cheap until you actually modify.

4. If you intentionally want a view-like workflow across function calls, return the new frame instead of mutating:

```python
def boost_scores(frame):
    out = frame.copy()
    out.loc[out["status"] == "active", "score"] *= 2
    return out
```
Expected output: the caller's frame is untouched and the returned frame has the change. Copy-on-write makes this pattern memory-cheap.

5. Check whether copy-on-write is active in your environment:

```python
print(pd.options.mode.copy_on_write)
```
Expected output: `True` on pandas 3 and later (it is the only mode), configurable on pandas 2.x. Code that relies on old view-mutation behavior breaks here.

## Variant phrasings

### SettingWithCopyWarning pandas 2
The warning was the old signal for this exact problem. Fix it the same way: `.loc` for in-place writes, `.copy()` for owned intermediates. Under pandas 3 the warning is gone because the write simply never happens.

### pandas assignment not working on filtered dataframe
Same root cause. Any `df[boolean_mask]["col"] = value` is two indexing operations and the second one writes to a temporary.

### chained assignment with iloc
`df.iloc[0:5]["col"] = x` has the same problem. Use `df.iloc[0:5, df.columns.get_loc("col")] = x` for a single operation.

## Why it happens
`df[mask]` returns a new object, and `["col"] = x` on it assigns into that temporary, which is immediately discarded. Before copy-on-write, pandas sometimes returned a view, so the write occasionally leaked through to the original, which is why the old behavior was unpredictable. Copy-on-write removed the view case entirely: chained assignment now reliably does nothing.

## Edge cases
- Method chains ending in assignment (`df.query(...)["col"] = x`) are still chained assignment, rewrite with `.loc`.
- `df["new_col"] = ...` on the full frame (single indexing) is always fine and is not chaining.
- In-place methods like `fillna(inplace=True)` on a slice hit the same trap, assign the result instead.
- Libraries that mutate frames you pass in may silently stop working under CoW, check their changelogs.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_K_zPAIBKv1wgG_He5-AYww
