Array Shapes and Axis Operations¶
What This Is¶
This topic is about keeping rows, columns, and axis reductions straight. Many later mistakes in modeling come from losing track of which dimension represents examples and which represents features.
The practical skill is not "knowing NumPy exists." It is knowing which NumPy operation preserves row alignment, which one reduces a dimension, and which one silently changes the meaning of your data.
When You Use It¶
- building feature matrices with
NumPy - reducing across rows or columns
- adding derived features to an existing matrix
Most Useful NumPy Moves¶
The functions below do most of the work in this topic:
np.arrayto create a matrix from Python listsshapeandnp.ndimto inspect the current layoutmeanandsumto reduce across rows or columnsreshapeandnp.expand_dimsto turn 1D results into column vectorsnp.squeezeto remove size-1 dimensions when you intentionally added themnp.column_stackto append 1D derived features as columnsnp.stackto create a brand-new axis when you want to keep separate pieces distinctnp.concatenateto join arrays along an existing axis when the shapes already match
That list is enough for most tabular feature-matrix work.
Tooling¶
numpy.arrayshapemean(axis=...)sum(axis=...)- boolean masks
np.column_stacknp.stacknp.concatenatereshapenp.expand_dimsnp.squeezeastype(float)np.ndim
Minimal Example¶
import numpy as np
X = np.array([[5.0, 0.0, 86.0], [0.0, 2.0, 64.0], [2.0, 1.0, 78.0]])
row_means = X.mean(axis=1)
column_means = X.mean(axis=0)
column_totals = X.sum(axis=0)
Axis Mental Model¶
Think of axis as the direction you are collapsing:
axis=0reduces rows and returns one value per columnaxis=1reduces columns and returns one value per row
For a 3 x 4 matrix:
X.mean(axis=0)gives4valuesX.mean(axis=1)gives3values
That one distinction explains many shape bugs.
Worked Pattern¶
late_flag = (X[:, 0] <= 0).astype(float)
repeat_flag = (X[:, 1] >= 2).astype(float)
X_augmented = np.column_stack([X, late_flag, repeat_flag])
print(X.shape, X_augmented.shape)
What to notice:
axis=1reduces across columns and returns one value per rowaxis=0reduces across rows and returns one value per column- every derived feature must have the same row count as the base matrix
column_stackis safe only when row alignment is still intact
Two more useful patterns:
col_means = X.mean(axis=0, keepdims=True)
X_centered = X - col_means
row_means = X.mean(axis=1)
row_means_2d = np.expand_dims(row_means, axis=1)
keepdims=True is useful when you want broadcasting to keep working without manually reshaping the result. np.expand_dims is useful when a 1D result needs to become a column vector before stacking.
Two debugging habits help here:
- print the shape after every transformation that changes the matrix
- compare the derived vector length to
X.shape[0]before stacking anything
If a feature came from a filtered subset, rebuild it from the original row order instead of trying to force it into place.
Failure Pattern¶
Creating a derived feature with the wrong row count and stacking it anyway. If the base matrix has n rows, every derived feature must also have n rows.
Other warning signs:
- a boolean mask was computed on a different table
- a derived vector was sorted independently from
X - a transpose changed the interpretation of rows and columns
- a matrix was stacked before the row counts were checked
squeezeremoved a dimension you still needed for later stackingreshapewas used to silence a shape mismatch instead of fixing the upstream logic
np.stack is also a common place to make a mistake. It creates a new axis, so it is not the same operation as appending a column. If you want to add features, column_stack or concatenate(..., axis=1) is usually the right shape story.
np.concatenate joins along an existing axis. That makes it useful when you already have compatible 2D arrays and want to widen the feature matrix without creating a new axis.
np.squeeze removes size-1 dimensions. That is convenient after an expand_dims or a reshape, but dangerous if you are not sure whether the singleton dimension is still needed.
np.ndim is a fast sanity check when you are not sure whether something is a vector, a matrix, or a higher-dimensional tensor.
Practice¶
- Create a
4 x 3array and compute row means and column means. - Add one derived flag and verify the new shape.
- Build one wrong-length feature vector and explain why it must be rejected.
- Use
reshapeto turn a 1D vector into a column vector and explain why that matters for stacking. - Compare
np.column_stackwithnp.stackand describe when each one is the more natural choice. - Explain what happens if you compute a mask on sorted data and then attach it to unsorted data.
- Show how
keepdims=Truechanges the result of a reduction. - Explain the difference between
concatenate,stack, andcolumn_stackin one sentence each.
Runnable Example¶
Open the matching example in AI Academy and run it from the platform.
While reading the output, ask:
- did the row count stay fixed
- do the row means and column means answer different questions
- did the augmented shape change only in the feature dimension
- would a later model still know which values came from which row
Inspect the row means, column means, and final stacked matrix shape. If the shape changes in a way you did not expect, the bug is probably in how the new feature was built, not in the model.
Quick Checks¶
- If a vector should be one value per row, its length must match
X.shape[0]. - If a value came from a reduction across columns, it should be a row-level summary.
- If a value came from a reduction across rows, it should be a column-level summary.
- If the output is going into
column_stack, test the length first and the meaning second. - If a reduction will be used in subtraction or division later, consider
keepdims=True. - If you are about to call
squeeze, ask whether the size-1 dimension was useful for broadcasting.
Common Tricks¶
- Use
X.mean(axis=0, keepdims=True)when you want a column-wise summary that still broadcasts cleanly back ontoX. - Use
X.mean(axis=1)when you want a row-level signal and then turn it into a column withreshape(-1, 1)ornp.expand_dims(..., axis=1). - Use
np.column_stackfor one-dimensional derived features that should become new columns. - Use
np.concatenate([X, extra], axis=1)when both inputs are already 2D and share the same row count. - Use
np.stack([...], axis=0)only when you really want to create a new outer axis and keep the inputs separate.
Questions To Ask¶
- Does this operation reduce rows, reduce columns, or add a new axis?
- If I print the shape, what exact tuple do I expect?
- Is this derived value one per row or one per column?
- Would broadcasting still work if I changed this to
keepdims=True? - Am I appending a feature or building a new dimension?
- What would break first if I shuffled the rows before stacking?
Longer Connection¶
Continue with Python, NumPy, Pandas, Visualization for a fuller data-to-matrix workflow.