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()


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-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer