NumPy Views vs Copies, Strides & Memory Layout

Why views vs copies matter

Many NumPy operations return a view: a new ndarray object that shares the same underlying memory buffer. Changes in a view affect the original array. A copy owns its data; changes don’t affect the source. Understanding this avoids subtle bugs and improves performance.

Slicing returns views (usually)

import numpy as np

a = np.arange(6)          # [0 1 2 3 4 5]
s = a[1:4]                # slice -> view
s[0] = 99
print(a)                  # [ 0 99  2  3  4  5]  changed!

c = a[1:4].copy()         # explicit copy
c[0] = -1
print(a[1:4])             # original unchanged now

Strides: how NumPy walks memory

strides describe how many bytes to skip to move by one step along each dimension. Together with shape and dtype.itemsize, strides define how a view maps onto the same data buffer.

M = np.arange(12, dtype=np.int32).reshape(3,4)
print(M.shape, M.dtype, M.strides)  # e.g. (3, 4), int32, (16, 4) on typical systems
# (16 bytes to move to next row, 4 bytes to move to next column)

row_view = M[::2, :]                 # step of 2 in rows
print(row_view.shape, row_view.strides)

col_view = M[:, ::2]                 # step of 2 in cols
print(col_view.shape, col_view.strides)

C-order vs F-order memory layout

NumPy stores arrays in C order (row-major) by default: the last dimension changes fastest. Fortran order (column-major) makes the first dimension change fastest. Layout affects contiguity and can influence speed when iterating or interfacing with other tools.

A = np.arange(12).reshape(3,4)         # default C-order
print(A.flags['C_CONTIGUOUS'], A.flags['F_CONTIGUOUS'])  # True, False

F = np.asfortranarray(A)
print(F.flags['C_CONTIGUOUS'], F.flags['F_CONTIGUOUS'])  # False, True

ravel() vs flatten()

  • ravel(): returns a 1D view when possible, otherwise a copy.
  • flatten(): always returns a copy.
X = np.arange(9).reshape(3,3)
rv = X.ravel()     # likely a view
fl = X.flatten()   # copy
rv[0] = 99
print(X[0,0])      # may change because rv often views original

reshape(): view or copy?

reshape() returns a view if it can (i.e., if a contiguous layout exists for the requested shape). Otherwise it creates a copy. Use np.reshape(X, newshape, order='C'/'F') to control memory order expectations.

Y = np.arange(12)
Y2 = Y.reshape(3,4)     # view (contiguous)
Y3 = Y2.T.reshape(6,2)  # may need a copy due to transpose (non-contiguous)
print(Y2.base is Y)     # True (shares memory)

Advanced: as_strided (handle with care!)

np.lib.stride_tricks.as_strided can build complex views by manually specifying shape and strides. It’s powerful but unsafe if used incorrectly (can produce overlapping or out-of-bounds views). Prefer high-level APIs first.

from numpy.lib.stride_tricks import as_strided

a = np.arange(10, dtype=np.int32)
# Create a sliding window view of width 4
window = 4
shape = (a.size - window + 1, window)
strides = (a.strides[0], a.strides[0])
w = as_strided(a, shape=shape, strides=strides)  # view only
print(w[:3])
# Always ensure the computed windows stay within the buffer!

When does NumPy copy?

  • Explicit .copy(), flatten().
  • Operations that require contiguous memory from non-contiguous data (e.g., some reshapes of transposed arrays).
  • Type casts that change dtype.
  • Some ufuncs when output cannot alias input safely.

Detecting views vs copies

Check arr.base: if not None, the array is a view on another array’s memory.

Z = np.arange(8)
v = Z[::2]
c = Z[::2].copy()
print(v.base is Z, c.base is Z)   # True, False

Performance tips

  • Prefer slicing and ravel() over flatten() when you don’t need isolation.
  • Keep arrays contiguous when performance is critical; use np.ascontiguousarray / np.asfortranarray before heavy computation if required by libraries.
  • Minimize unnecessary copies in tight loops and large arrays.

Practice: quick exercises

# 1) Prove that slicing returns a view by modifying a slice
a = np.arange(10)
b = a[3:8]
b[:] = -1
print(a)  # indices 3..7 should be -1

# 2) Show ravel vs flatten behavior on a transposed array
X = np.arange(12).reshape(3,4)
XT = X.T
r = XT.ravel()      # likely a copy now (non-contiguous)
f = XT.flatten()    # copy
r[0] = 999
print(X[0,0])       # check whether original changed

# 3) Compute strides of a 2D array and a strided subview
M = np.arange(24, dtype=np.int64).reshape(4,6)
S = M[::2, ::3]
print(M.strides, S.strides, S.shape)

# 4) Create a safe sliding window function using as_strided
def sliding_window(a, w):
    from numpy.lib.stride_tricks import as_strided
    shape = (a.size - w + 1, w)
    strides = (a.strides[0], a.strides[0])
    return as_strided(a, shape=shape, strides=strides)
print(sliding_window(np.arange(7), 3))
Numpy Indexing & Slicing reshape() Broadcasting shape where()
Subhendu Mohapatra — author at plus2net
Subhendu Mohapatra

Author

🎥 Join me live on YouTube

Passionate about coding and teaching, I publish practical tutorials on PHP, Python, JavaScript, SQL, and web development. My goal is to make learning simple, engaging, and project‑oriented with real examples and source code.



Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
©2000-2025   plus2net.com   All rights reserved worldwide Privacy Policy Disclaimer