Arrays and shapes
Use array shapes, broadcasting and reductions to express a small data transformation.
THE IDEA
A shape is the size of every axis
A NumPy array is a block of numbers of one type, arranged on a grid. Its shape lists how long the grid is along each axis. A table of 4 examples with 3 features each has shape (4, 3): axis 0 (length 4) runs down the examples, axis 1 (length 3) runs across the features. Whether axis 0 means "examples" is a convention you choose and must remember; NumPy only knows the lengths.
>>> import numpy as np
>>> X = np.zeros((4, 3))
>>> X.shape, X.ndim, X.size
((4, 3), 2, 12) # axis lengths, number of axes, number of elementsInside memory the numbers are one flat line. The shape only says how to read that line as a grid: the last axis changes fastest. That is why a cell's position a[i, j] turns into the single offset i * (row length) + j.
Click a cell. The numbers in the cells are the order the values are stored in memory, row by row (the last axis changes fastest). Now reshape the same 12 values:
Why shapes matter: (12,) is not (12, 1) is not (1, 12)
The same 12 numbers can be a flat list, a column or a row. These are three different shapes, and later (broadcasting) NumPy treats them differently. Try the three buttons above. Converting between them is a reshape, and -1 means "work this length out for me".
Predict first. You have 12 numbers. What does a.reshape(5, -1) do?
Predict first. In a feature table of shape (4, 3), what is X[:, 1]?
The common mistake: a reshape that is valid but means something else
Reshape only checks that the counts match, not that the meaning does:
>>> flat = np.arange(6)
>>> flat.reshape(-1, 3) # 2 rows of 3 features
array([[0, 1, 2],
[3, 4, 5]])
>>> flat.reshape(3, -1) # 3 rows of 2 features
array([[0, 1],
[2, 3],
[4, 5]])Chapter context
A tensor is a multidimensional array with a shape and element type. Many AI bugs come from doing a valid operation along the wrong axis. Write the intended dimensions before trying to shorten the code.
TRY IT YOURSELF
- describe(a) returns (ndim, shape, size) of an array.
- to_table(flat, n_features) turns a flat array into one row per example and n_features columns, and must fail loudly (ValueError) when the length does not divide evenly.
- as_column(v) turns shape (n,) into (n, 1) and refuses anything that is not 1-D. dims(X) returns (n_examples, n_features) for a 2-D table and raises ValueError otherwise.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
What does shape (4, 3) mean for a feature table?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
For a data matrix , feature means are
Here i indexes examples and j indexes features. The mean vector has d entries, so subtracting it from X broadcasts along rows.
Worked example
For X = [[1, 2], [3, 4]], the feature means are [2, 3]. Centering produces [[-1, -1], [1, 1]]. Each centered column has mean zero. Computing row means instead would answer a different question even though the operation is valid.
Reference code
The following example uses the Python standard library and does not call a remote service.
X = [[1, 2], [3, 4]]
means = [sum(row[j] for row in X) / len(X) for j in range(2)]
centered = [[x - m for x, m in zip(row, means)] for row in X]
assert means == [2, 3]
assert centered == [[-1, -1], [1, 1]]
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
Broadcasting is a shape rule, not permission to ignore axis meanings. Vectorized code can create large intermediate arrays. Normalize training and test data with training statistics only.