Code › codeit-ai-sprint
NumPy Array Dimensions and Axes
A lesson note on vectors, matrices, NumPy arrays, shape, axis, and basic array operations
This lesson started with vectors and matrices, then moved into NumPy arrays. At first, it looked like a linear algebra review, but it became the setup for understanding shape and axis in NumPy.
Starting from vectors and matrices
A vector is an ordered group of numbers. In linear algebra, it is treated as something with magnitude and direction. A vector with one number is one-dimensional, a vector with x and y values is two-dimensional, and a vector with x, y, and z values is three-dimensional. Once the dimension goes higher than that, it becomes hard to draw, but the computation still treats it as a vector with more numeric components.
A matrix is a rectangular arrangement of numbers in rows and columns. In tabular data, rows usually represent samples, and columns usually represent variables, so a matrix can hold many samples and many features at once.
For matrix operations, addition and subtraction only work when the two matrices have the same shape. Scalar multiplication applies the same number to every element. Matrix multiplication is different. It does not multiply matching positions. The number of columns in the first matrix must match the number of rows in the second matrix. If an m x n matrix is multiplied by an n x p matrix, the result is an m x p matrix.
When a matrix is multiplied by a vector, the matrix applies a linear transformation to that vector and produces a new vector. In two dimensions, transformations like rotation, scaling, and reflection can be represented with matrices. The matrix is not the vector itself. It is used as an operator that takes a vector as input and computes a new vector.
NumPy arrays and Python lists
A Python list is a built-in Python data structure. A NumPy array is an ndarray provided by NumPy. Both can handle multiple values in order, but their internal structure and computation model are different.
A Python list stores references to values in order. The values can have different types, and another type of value can be inserted in the middle.
In NumPy, dtype means the data type of the values inside an array. A NumPy array usually stores values with one unified dtype in contiguous memory. One reason NumPy is fast is that its core operations are implemented in C. Instead of running a Python loop over individual objects, NumPy’s internal C-level loops process contiguous data with the same dtype. Depending on the build and CPU support, NumPy can also use CPU optimizations such as SIMD.
This difference also matters for strings. If an array is converted with astype(str), NumPy may choose a fixed string width. When a longer string is assigned later, it can be truncated. If string length or mixed object types need to be handled safely, the array rules have to be changed, for example by using object dtype.
Shape and reshape
Shape shows the size of each dimension as a tuple. A one-dimensional array can have a shape like (6,), and the comma is there because a one-element tuple still has to be represented as a tuple.
import numpy as np
data = np.array([1, 2, 3, 4, 5, 6])
print(data.shape)
# (6,)
matrix = data.reshape(2, 3)
print(matrix)
# [[1 2 3]
# [4 5 6]]
reshape(2, 3) rearranges six numbers into a 2-row, 3-column array. reshape(2, 5, 10) can be read as two blocks, each containing five rows of ten values.
Image data follows the same idea. If an image array has the shape (334, 500, 3), it means 334 rows, 500 columns, and 3 channels. The final dimension usually represents the RGB channels.
image = np.zeros((334, 500, 3))
print(image.shape)
# (334, 500, 3)
print(image[0, 0, 0])
# 0.0
image[0, 0, 0] retrieves the first channel value of the top-left pixel. image[0, 0] refers to the three RGB values for that pixel.
Axis, reduction, concatenate, and stack
Axis is the axis a NumPy operation applies to. For reduction operations such as sum or max, values are collected along the specified axis, and that axis disappears from the result.
In a 2-row, 3-column array, axis=0 is the row axis, and rows progress vertically. So sum(axis=0) adds values vertically, compresses the row axis, and leaves one result per column. max(axis=0) uses the same direction, but keeps the maximum value for each column instead of summing.
Axis=1 is the column axis, and columns progress horizontally. sum(axis=1) collects values across each row and produces one sum per row. max(axis=1) keeps the largest value in each row.
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(matrix.sum(axis=0))
# [5 7 9]
print(matrix.max(axis=0))
# [4 5 6]
print(matrix.sum(axis=1))
# [ 6 15]
print(matrix.max(axis=1))
# [3 6]
The same axis number behaves differently with concatenate and stack because the operation itself is different. sum and max reduce an axis. concatenate extends the length of an existing axis without creating a new one. stack creates a new axis and places arrays along it.
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print(np.concatenate([a, b], axis=0).shape)
# (4, 2)
print(np.concatenate([a, b], axis=1).shape)
# (2, 4)
print(np.stack([a, b], axis=0).shape)
# (2, 2, 2)
So sum and max compress an axis, concatenate extends an existing axis, and stack creates a new axis. The axis number matters, but the function decides what happens to that axis.
If keepdims=True is used with reduction operations such as sum or max, the reduced axis remains with size 1. The values are reduced, but the number of dimensions is preserved, which can make later broadcasting or array operations easier to align.
Copy and view
Slicing can create a view into the original array instead of copying new data. If the sliced part is modified, the original array can change as well. When an independent array is needed, copy has to be called explicitly.
patch = image[157:177, 240:260]
safe_patch = patch.copy()
This matters when experimenting with part of an image. A view shares data with the original array, while a copy creates a separate array.
Broadcasting and masking
Broadcasting lets arrays with different shapes work together when their shapes follow NumPy’s broadcasting rules. For example, the same value can be added to an entire matrix, or the same vector can be added to each row, without writing an explicit loop.
Masking selects or updates only the elements that satisfy a condition. The condition produces a boolean array, and that boolean array is used as an index.
array1 = np.arange(16).reshape(4, 4)
mask = array1 < 10
array1[~mask] = 100
print(array1)
The same result could be written with a loop, but NumPy expresses the selection and assignment as array operations. The code becomes shorter, and the internal work is still handled at the array level.
Wrap-up
This lesson moved from vectors and matrices into NumPy array structure. Vectors and matrices are the basic language for working with numeric data, and NumPy arrays store values with a unified dtype in contiguous memory.
Axis is not just a number to memorize. It is the axis a function uses to reduce, extend, or create shape. sum and max compress an axis, concatenate extends an existing axis, and stack creates a new one. Reading those operations together with shape makes the result easier to predict.