pythonforbio.
[WASM idle]
NumPy17/18

ndarray

Starting sandbox…
Beginnerlesson2 tests

ndarray

In one line: a contiguous memory buffer plus a shape, a dtype and strides — everything else in NumPy is consequences of that.

The anatomy

a = np.arange(12).reshape(3, 4)

a.shape      # (3, 4)      tuple of dimension lengths
a.ndim       # 2           len(shape)
a.size       # 12          product of shape
a.dtype      # int64       element type — see [[NumPy dtypes]]
a.itemsize   # 8           bytes per element
a.nbytes     # 96          size * itemsize
a.strides    # (32, 8)     bytes to step per axis — see [[Memory Layout and Strides]]
a.base       # None if it owns its data, else the array it views
a.flags      # C_CONTIGUOUS, F_CONTIGUOUS, OWNDATA, WRITEABLE

a.base is the debugging tool people forget. If it is not None, you are holding a view and writes will propagate. See Views vs Copies.

The four constraints

These define what an ndarray is, and each one has a consequence you will feel:

  1. Homogeneous. Every element is the same dtype. → No mixed-type columns; that is what Pandas is for.
  2. Fixed size. The buffer is allocated once. → np.append reallocates and copies the whole array; never append in a loop. Build a list, then convert once, or preallocate with np.empty.
  3. N-dimensional and rectangular. No ragged rows. → Sequences of different lengths must be padded, or held in a list.
  4. Contiguous (usually). → Slicing is free; arbitrary reordering is not.

Shape is not a suggestion

Most NumPy errors are shape errors. Three shapes that look the same and are not:

np.array([1, 2, 3]).shape        # (3,)    1-D, no orientation
np.array([[1, 2, 3]]).shape      # (1, 3)  row vector
np.array([[1], [2], [3]]).shape  # (3, 1)  column vector

A (3,) array is neither a row nor a column. When you need one explicitly:

v[None, :]     # (1, 3)   also v[np.newaxis, :]
v[:, None]     # (3, 1)
v.reshape(-1, 1)

The [:, None] idiom is the workhorse of Broadcasting — it is how you subtract a per-row value from every row of a matrix.

Creating one

Covered fully in Array Creation, but the essentials:

np.array([[1, 2], [3, 4]])
np.zeros((3, 4)); np.ones((3, 4)); np.full((3, 4), np.nan)
np.arange(0, 10, 2); np.linspace(0, 1, 11)
np.empty((3, 4))         # uninitialised — fast but contains garbage
np.zeros_like(a)         # match shape and dtype of a

Bioinformatics framing

A (20000, 500) float32 expression matrix is 40 MB — trivially fast for whole-matrix operations, and catastrophically slow if you loop over its 10 million elements in Python.

counts = np.loadtxt("counts.tsv", skiprows=1, usecols=range(1, 501))
counts.shape            # (20000, 500) — genes × samples

cpm = counts / counts.sum(axis=0) * 1e6      # normalise per sample
logcpm = np.log2(cpm + 1)
gene_mean = logcpm.mean(axis=1)              # per gene → shape (20000,)

Note axis=0 sums down columns (one total per sample) while axis=1 averages across rows (one value per gene). Getting this backwards silently produces plausible-looking nonsense. See Axes and Reductions.

Common mistakes

  • Assuming (n,) and (n,1) are interchangeable. They broadcast differently and produce silently wrong shapes.
  • Growing an array in a loop with np.append or np.concatenate — quadratic time. Collect in a list, convert once.
  • Ignoring integer overflow. np.array([127], dtype=np.int8) + 1 gives -128, no warning. Read counts belong in int32 or int64.
  • Using np.empty and forgetting to fill it. The garbage looks like data.
  • arr.reshape(...) without assigning. Reshape returns a new array; it does not mutate. arr.resize() does mutate, which is why it is rarely what you want.
  • Comparing floats with ==. Use np.isclose.

See also

NumPy dtypes · Array Creation · Indexing and Slicing · Broadcasting · Views vs Copies · Memory Layout and Strides · NumPy

Test cases · 2

#viainputexpected stdout
1stdin1 2 3 4 5 6shape=(2, 3) ndim=2 size=6 dtype=float64
2stdin10 20 30 40 50 60shape=(3, 2) ndim=2 size=6 dtype=float64

Hints · 2

01Hint

np.loadtxt can read whitespace-separated rows straight from a string.

02Hint

Every ndarray exposes .shape, .ndim, .size and .dtype.

starter

No output yet — run the code to populate this drawer.

Views vs Copiesufuncs