5.4.1 · D1Scientific Computing (Python)

Foundations — NumPy — ndarray structure, dtype, shape, strides

2,408 words11 min readBack to topic

This page assumes nothing. Before you meet dtype, shape, or strides in the parent note, you need a picture of what a byte, an index, a tuple, and memory even are. We build each from zero, in the order they stack.


0. The most basic picture: memory is a numbered shelf

Figure — NumPy — ndarray structure, dtype, shape, strides

Why the topic needs this. Everything NumPy does is "compute an address, then read the drawers there." If drawers and addresses aren't concrete for you, the word stride (a jump measured in drawers) will float in the air with nothing to stand on. Look at the figure: the numbers under the row are addresses; the numbers inside are contents. NumPy always works with the addresses.


1. Index — the address you pretend to use

Notation you'll see: A[i] means "the element of A at index i." The letter i is just a stand-in for some whole number.

Figure — NumPy — ndarray structure, dtype, shape, strides

The figure shows the same six drawers labelled two ways: the black numbers are true addresses; the orange labels [0] [1] [2] … are the indices we choose to lay on top.


2. Tuple — a fixed little list of numbers

Why not just call it a list? Because in Python a list can grow and shrink; a tuple is meant to be a small, unchanging label. shape and strides are both tuples — one number per direction.

If you've met vectors as (x, y) pairs, a tuple is the same idea, just possibly longer.


3. Subscript notation — , ,

Why the topic needs it. An array can have 2, 3, 4, or more directions (called axes). We can't invent a fresh letter for each, so we write and let roam over the axis numbers. When you see it means "the index we chose along axis ."


4. Stride — the jump size, measured in bytes

We have used the word stride loosely above; now we pin it down, because it is the star of the whole topic.


5. The product symbol and the sum symbol

The parent note writes the total number of elements as and the byte offset as . These two Greek capitals are just shorthand for repeated arithmetic.

Why these tools and not just writing it out? Because an array can have any number of axes. Writing forever is impossible; says "do this for every axis, however many there are" in one symbol. It answers the question "how do I state one rule that works for 2-D, 3-D, and 100-D at once?"

Figure — NumPy — ndarray structure, dtype, shape, strides

The figure shows as a set of arrows all flowing into one "+" box, and as arrows flowing into one "×" box — the symbol is just the funnel.


6. Contiguous, and the "buffer" — all in one unbroken stretch

Why the topic needs it. NumPy is fast because array data is usually one contiguous buffer: the CPU can read neighbours cheaply (this is the deep reason behind CPU cache & memory locality). The whole trick of strides is possible only when NumPy controls exactly how far apart consecutive logical elements sit in the buffer — and that spacing is measured against a contiguous baseline.


7. Row-major layout — how a grid becomes a line

Here is the concept that makes strides inevitable. A grid (rows and columns) must be flattened into the single line of memory. There is a choice about the order.

Figure — NumPy — ndarray structure, dtype, shape, strides

Why the topic needs it. Once you see the grid unrolled row by row (the figure's orange path), the flat position of A[i, j] is obvious: to reach row i you must pass i whole rows of length C, then step j more. That is literally — the formula the parent derives. The picture is the derivation. (The opposite choice, column-major, is Row-major vs column-major (C vs Fortran order).)


8. Homogeneous — every cell the same size

Why the topic needs it. If cell 0 were 8 bytes but cell 1 were 2 bytes, "jump one step" would have no fixed size and strides couldn't exist. Homogeneity is precisely what lets a single itemsize describe "how fat is each cell," and lets stride jumps be constant.


Putting the symbols together (a preview, no new maths)

You now own every piece the parent uses. In its language:

Nothing here is unfamiliar now — that is the whole point of this page.


Prerequisite map

Byte + address = numbered shelf

Index = pretend position from 0

Tuple = fixed list of numbers

Subscript d_k = the k-th one

Stride = jump size in bytes

Sum and Product symbols

Contiguous buffer = unbroken run

Row-major = grid unrolled by rows

Homogeneous = equal itemsize

byte offset = sum of index times stride

ndarray = buffer plus metadata


Equipment checklist

Test yourself — reveal only after answering aloud.

What is a byte, in one sentence?
The smallest addressed box of storage, holding a value 0–255.
What is the difference between an address and an index?
An address is the real physical drawer number; an index is the pretend position we lay on top (row/column), starting at 0.
Why do indices start at 0, not 1?
Because index 0 means "zero steps from the start" — it makes offset arithmetic (steps × size) come out clean.
What does the tuple tell you?
3 entries along the first axis, 4 along the second — order matters.
What does the subscript in mean?
"The stride of axis 1" — a single labelled number, NOT stride times 1.
What is a stride, and in what units?
How many bytes you jump in memory to move one step along an axis — measured in bytes, not elements.
Can a stride be negative or zero?
Yes — reversed views (like A[::-1]) have negative strides, and broadcasting uses zero strides to fake a repeated axis.
What is a "buffer"?
The actual run of bytes holding the array's data — the flat row of drawers from Section 0.
Translate into words.
For every axis k, multiply its index by its stride, then add all those products together.
Translate into words.
Multiply the sizes of all axes together — that gives the total element count.
What is itemsize?
The number of bytes one element occupies (e.g. int64 = 8 bytes).
What does "contiguous" mean and why does NumPy like it?
All the bytes sit in one unbroken run; this lets the CPU read neighbours fast and lets strides work from a fixed baseline.
In row-major order, which axis changes fastest as you walk memory?
The last axis (columns) — you finish one full row before starting the next.
Why must array elements all share one dtype (be homogeneous)?
So every cell is the same itemsize, making constant stride jumps possible.

Connections

  • Parent: 5.4.01 NumPy — ndarray structure, dtype, shape, strides (Hinglish)
  • Python lists vs arrays — why lists are not homogeneous or contiguous.
  • Row-major vs column-major (C vs Fortran order) — the layout choice from Section 7.
  • CPU cache & memory locality — why contiguity is fast.
  • NumPy — views vs copies & np.shares_memory — what "same buffer, new metadata" enables.
  • NumPy — broadcasting — where zero strides come from; and NumPy — vectorization & performance — where these foundations pay off.