Q&A library Python

Why does changing a NumPy slice also change the original array?

Python Advanced copiesnumpyviews

Short answer

A basic slice of a NumPy array is a view that shares memory with the original, so writing to it changes both. Use .copy() when you need an independent array, and note that fancy indexing already returns a copy.

The short version

A plain slice of a NumPy array is a view onto the same memory, not a separate array, so assigning into the slice also changes the original. This is deliberate and makes slicing fast and memory-cheap, but it surprises people. Call .copy() when you need an independent array. Fancy indexing (with a list or a boolean mask) already returns a copy, so the behaviour is not uniform.

Seeing the shared memory

import numpy as np

a = np.array([1, 2, 3, 4])
s = a[1:3]      # a view onto a's memory
s[0] = 99
print(a)        # [ 1 99  3  4]  <- the original changed

Writing s[0] = 99 changed a, because s is a window onto the same underlying data, not a fresh array.

Making an independent copy

If you want to work on a slice without touching the original, copy it explicitly.

b = a[1:3].copy()
b[0] = -1
print(a)        # unchanged by edits to b

Now b has its own memory, so changes stay local.

Why NumPy does this

Views make slicing free: no data is duplicated, so slicing a huge array costs almost nothing. That efficiency is central to NumPy's speed. The price is that a slice is not a safe scratch copy, so any in-place edit reaches back to the parent. Once you know this, it is predictable rather than surprising.

The inconsistency to remember

Basic slicing returns a view, but fancy indexing returns a copy.

view = a[0:2]          # view, shares memory
copy1 = a[[0, 1]]      # copy, independent (fancy indexing)
copy2 = a[a > 1]       # copy, independent (boolean mask)

So a[0:2] = 0 changes a, but assigning into the result of a[[0, 1]] would not. This split trips people up, so when in doubt, check with .base: a view's .base points at the original, while a copy's .base is None.

This is the same idea behind the SettingWithCopyWarning in pandas, where it is unclear whether you are writing to a view or a copy. The safe habit in both libraries is the same: if you intend to modify a subset independently, copy it first, and if you intend to modify the original, index into the original directly rather than through an intermediate slice.

A practical rule

Treat a slice as a live window on the original, not a snapshot. When you extract a piece to transform separately, add .copy() and the ambiguity disappears.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project