Array Creation and Manipulation
Building Arrays for Science
Before you can broadcast, vectorize, or index, you need arrays. NumPy provides dozens of array creation functions, but a handful cover 95% of scientific computing needs. This section catalogs them and shows the reshape/stack/concatenate patterns that turn individual arrays into the multi-dimensional structures your computations require.
Definition: Core Array Creation Functions
Core Array Creation Functions
| Function | Purpose | Example |
|---|---|---|
np.zeros(shape) |
Array of zeros | np.zeros((3, 4)) |
np.ones(shape) |
Array of ones | np.ones((2, 3), dtype=np.complex128) |
np.eye(n) |
Identity matrix | np.eye(4) |
np.arange(start, stop, step) |
Evenly spaced (by step) | np.arange(0, 10, 0.5) |
np.linspace(start, stop, n) |
Evenly spaced (by count) | np.linspace(0, 1, 100) |
np.logspace(start, stop, n) |
Log-spaced | np.logspace(1, 6, 50) β from to |
np.full(shape, value) |
Filled with constant | np.full((3, 3), np.pi) |
np.empty(shape) |
Uninitialized (fast) | np.empty((1000,)) β contents are garbage |
Rule: prefer np.linspace over np.arange for floating-point ranges
(avoids off-by-one issues from floating-point step accumulation).
Definition: np.meshgrid
np.meshgrid
np.meshgrid creates coordinate matrices from 1-D coordinate vectors,
essential for evaluating functions on 2-D grids:
x = np.linspace(-2, 2, 100)
y = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, y) # each has shape (100, 100)
# Evaluate 2-D Gaussian
Z = np.exp(-(X**2 + Y**2))
# Memory-efficient alternative: sparse=True
Xs, Ys = np.meshgrid(x, y, sparse=True)
# Xs has shape (1, 100), Ys has shape (100, 1)
# Broadcasting produces the same result with less memory
Z_sparse = np.exp(-(Xs**2 + Ys**2))
Definition: reshape vs ravel vs flatten
reshape vs ravel vs flatten
| Function | Returns | Copy? |
|---|---|---|
a.reshape(shape) |
New shape, same data | View if possible, copy otherwise |
a.ravel() |
1-D array | View if possible (C-contiguous) |
a.flatten() |
1-D array | Always a copy |
a.T |
Transpose | View (just swaps strides) |
a = np.arange(12)
b = a.reshape(3, 4) # view β no data copied
c = b.ravel() # view β back to 1-D
d = b.flatten() # copy β safe to modify
# -1 in reshape = infer this dimension
e = a.reshape(2, -1) # shape (2, 6) β NumPy computes 6
Definition: Stacking and Concatenating Arrays
Stacking and Concatenating Arrays
| Function | Description | Example |
|---|---|---|
np.concatenate |
Join along existing axis | np.concatenate([a, b], axis=0) |
np.stack |
Join along new axis | np.stack([a, b]) β adds axis 0 |
np.vstack |
Stack vertically (axis 0) | np.vstack([row1, row2]) |
np.hstack |
Stack horizontally (axis 1) | np.hstack([col1, col2]) |
np.block |
Build from nested lists | np.block([[A, B], [C, D]]) |
A = np.eye(2)
B = np.ones((2, 3))
C = np.zeros((3, 2))
D = np.full((3, 3), 5)
# Block matrix: [[A, B], [C, D]]
M = np.block([[A, B], [C, D]]) # shape (5, 5)
Theorem: Reshape Returns a View When Contiguous
a.reshape(new_shape) returns a view (no copy) if and only if
a is contiguous in memory (C or Fortran order) and the new shape
is compatible with contiguous strides.
If the data is already contiguous, a new shape just means reinterpreting the same flat buffer with different axis lengths. No data needs to move. If the array has non-contiguous strides (e.g., from transposing a non-square matrix), the elements are not in the right order for the new shape, so a copy is required.
Example: 2-D Function Evaluation with meshgrid
Evaluate and plot on the domain
using np.meshgrid.
Grid creation and evaluation
x = np.linspace(-np.pi, np.pi, 200)
y = np.linspace(-np.pi, np.pi, 200)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)
# Z is a (200, 200) array ready for plt.contourf or plt.imshow
import matplotlib.pyplot as plt
plt.contourf(X, Y, Z, levels=20, cmap='RdBu_r')
plt.colorbar(label='sin(x) cos(y)')
plt.xlabel('x'); plt.ylabel('y')
plt.title('2-D Function via meshgrid')
plt.show()
Example: Building Block Matrices
Construct the 4x4 block matrix: where .
Using np.block
I2 = np.eye(2)
A = np.array([[1, 2], [3, 4]])
Z = np.zeros((2, 2))
M = np.block([[I2, A],
[Z, I2]])
print(M)
# [[1. 0. 1. 2.]
# [0. 1. 3. 4.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]]
Quick Check
What does np.arange(12).reshape(3, -1) produce?
A (3, 4) array
A (3, 12) array
An error
A (3, 3) array
-1 means infer: 12 / 3 = 4.
Common Mistake: Floating-Point Pitfall with np.arange
Mistake:
Using np.arange with float step and expecting a precise endpoint:
a = np.arange(0, 1, 0.1)
print(len(a)) # Sometimes 10, sometimes 11 β depends on float rounding!
Correction:
Use np.linspace for floating-point ranges:
a = np.linspace(0, 1, 11) # Always exactly 11 points, endpoint included
meshgrid
Creates N-D coordinate matrices from 1-D coordinate vectors, enabling vectorized evaluation of functions on grids.
Related: Broadcasting, Linspace
Array Creation
# Code from: ch05/python/array_creation.py
# Load from backend supplements endpoint