NumPy views vs copies: predict which array changes
Trace shared memory through slicing, advanced indexing and assignment, and set an explicit copy boundary in a numerical workflow.
A NumPy view is a different array object that refers to existing array data. A copy owns separate data. Changing an element through a writable view can therefore change the original array. Basic slicing usually creates a view; advanced indexing creates a copy of the selected data.
This affects preprocessing, image crops and feature subsets. A short expression can either share a large data buffer or allocate a new one. We will inspect a small integer array, reproduce a surprising mutation and distinguish extracting a selection from assigning into that selection. The example requires NumPy and uses invented values.
Follow the memory relationship
Run the complete example. np.shares_memory checks whether these particular arrays overlap in memory; comparing their Python identities would answer a different question.
import numpy as np
source = np.arange(6)
window = source[1:4]
selected = source[[1, 2, 3]]
independent = source[1:4].copy()
print("slice shares:", np.shares_memory(source, window))
print("indexed shares:", np.shares_memory(source, selected))
window[0] = 99
selected[1] = -5
independent[2] = -8
print("source:", source.tolist())
print("selected:", selected.tolist())
print("independent:", independent.tolist())
mask = source > 3
extracted = source[mask]
extracted[:] = 0
assert source[-1] == 5
source[mask] = 0
print("after indexed assignment:", source.tolist())
Expected output:
slice shares: True
indexed shares: False
source: [0, 99, 2, 3, 4, 5]
selected: [1, -5, 3]
independent: [1, 2, -8]
after indexed assignment: [0, 0, 2, 3, 0, 0]
window is a new array object, but its first element points at the same data as source[1]. In contrast, the advanced-indexed array and the explicit copy keep the values they received when created. Their later mutations do not propagate back.
Extraction and assignment are different operations
The mask is calculated after the value 99 enters the source array. It selects that value along with 4 and 5. Extracting source[mask] creates a copy, so overwriting extracted does not update source.
However, source[mask] = 0 is assignment into the original array at the selected positions. NumPy does not promise that this assignment behaves like retrieving an intermediate array and mutating that intermediate. This distinction explains why “boolean indexing makes a copy” does not mean “boolean-indexed assignment does nothing.”
Avoid chained expressions when their mutation behavior is important. Name the intended target and assign into it directly. For example, source[mask][0] = 7 modifies an extracted temporary, whereas an explicit integer index into source targets the original data.
Reshape and transpose need inspection too
Reshaping can return a view when the requested organization is compatible with the data layout; otherwise it may require a copy. A transpose often changes strides without moving the underlying elements. Do not infer ownership from a new shape or variable name.
The .base attribute can help investigate provenance, but a simple test such as array.base is source is not a general overlap detector: the reference may lead to another object in a chain. For actual overlap, inspect the operation and use the appropriate memory-sharing tools. Exact overlap checks can be expensive for pathological layouts; this tiny example is intentionally straightforward.
See NumPy's copies and views guide for the indexing and reshape rules. Our calculation uses ordinary numeric arrays. Object-dtype arrays have another boundary: copying their data buffer can still leave references to the same Python objects.
Place a deliberate copy boundary
If an experiment should modify a crop or subset independently, call .copy() when that independent ownership begins. If the goal is to update the original, retain the view and make that contract clear. Copying every intermediate wastes memory and can obscure an intentional update.
A small view can also keep a much larger original buffer alive. If a tiny long-lived result should outlive a temporary large array, a deliberate copy may allow the larger allocation to be released. Measure the actual workload before optimizing ownership around this possibility.
A useful exercise
Create a 3-by-4 array, take every other column and modify one element through the selection. Predict the changed coordinate in the original. Repeat with an explicit list of column indices, and use both value assertions and np.shares_memory to verify your explanation.
Continue with the NumPy project examples to apply array operations in a complete task. The Data Tools resources connect memory, table validation and modeling workflows.