▦ MATRIX THINKING

READ AN EXCERPT · VOLUME 1 · MODULE 6

Draw with
whole regions.

Rows, columns, and rectangles: select a group of numbers, change their values, and see a picture emerge.

One value for a whole region · 0 is light, 1 is dark
matrix[1:, 1:4] = 1
Before you begin

This continuous excerpt assumes basic Python indexing and NumPy arrays. Set up the environment, then start a Python console or file with import numpy as np. Run the snippets in reading order: they share the matrix created at the beginning. Blocks following “The result is” show output, not code to run.

Try the browser matrix lab without installing Python. Its field is 8×8; this excerpt starts with a 3×5 matrix.

Select Rows and Columns of a Matrix

A one-dimensional array has one axis to select along. A two-dimensional matrix has two: rows and columns. Inside the square brackets, specify the row selection and column selection separately.

Create a small rectangular matrix with three rows and five columns:

matrix = np.array([
    [10, 11, 12, 13, 14],
    [20, 21, 22, 23, 24],
    [30, 31, 32, 33, 34]
])

A pair of indices accesses one element. For example, matrix[1, 2] gives 22: the number at row index 1 and column index 2.

The order remains the same: rows before the comma, columns after it. Either position can now contain a slice.

Select the entire row at index 1:

print(matrix[1, :])

The result is:

[20 21 22 23 24]

Read the expression in parts. The 1 before the comma fixes one row, the second row. The colon after the comma selects every column in that row. The result contains the whole row.

Select the entire column at index 2:

print(matrix[:, 2])

The result is:

[12 22 32]

The : before the comma selects all rows. The 2 after it fixes column index 2, the third column. The result contains the value at that column in each selected row: 12, 22, and 32.

The first example fixes a row and selects all columns. The second fixes a column and selects all rows. The colon's position shows which axis contributes its entire sequence of elements.

Why is the selected column printed horizontally? The result of matrix[:, 2] is a one-dimensional array of three values, a vector made from the column's elements. NumPy prints this short array horizontally. Its display does not change where the data came from: the selection still refers to a column of the original matrix.

Familiar negative indices work here too. Use them to access the last row or column without calculating its index:

Expression Selection Values in This Example
matrix[0, :] First row 10, 11, 12, 13, 14
matrix[-1, :] Last row 30, 31, 32, 33, 34
matrix[:, 0] First column 10, 20, 30
matrix[:, -1] Last column 14, 24, 34

The last row index in this matrix is 2; the last column index is 4. The notation -1 works for both because it counts from the end of the corresponding axis.

Putting a colon in both positions selects all rows and all columns:

print(matrix[:, :])

The result is the whole matrix:

[[10 11 12 13 14]
 [20 21 22 23 24]
 [30 31 32 33 34]]

The indexing expression determines which part of the data you work with: a scalar value, a vector of row or column elements, or a whole matrix. The data themselves remain unchanged: these examples only select and print them.

Select a Rectangular Region

Select some rows and some columns at the same time. Keep the matrix from the previous example:

[[10 11 12 13 14]
 [20 21 22 23 24]
 [30 31 32 33 34]]

To select the first two rows and the columns at indices 1, 2, and 3, describe each selection separately:

Now place the slices in their familiar positions: rows before the comma, columns after it.

print(matrix[0:2, 1:4])

The result is:

[[11 12 13]
 [21 22 23]]

Find the selected values in the table:

Row / column 0 1 2 3 4
0 10 11 12 13 14
1 20 21 22 23 24
2 30 31 32 33 34

Each selected row contributes an element for every selected column. In row 0, these are 11, 12, 13; in row 1, they are 21, 22, 23. The result is a rectangle: two rows of three elements, six values in total.

The stop boundary is excluded independently along each axis. Row index 2 and column index 4 do not appear in the result. Both slices omit the step, which defaults to 1, so the rows and columns are consecutive.

Four bounds describe the rectangle: the starting and ending rows, and the starting and ending columns. From the expression alone, you can work out the position and size of the selected region.

Try negative indices. Select the matrix's interior, excluding its first and last rows and columns:

print(matrix[1:-1, 1:-1])

The result is:

[[21 22 23]]

Consider the two slices. The row slice starts at index 1 and stops before the last row. With only three rows in the matrix, that leaves the middle row. The column slice also omits the first and last positions, leaving columns at indices 1, 2, and 3.

The result is a small matrix with one row and three columns. The double square brackets in the output help make this visible: the outer brackets contain one row of values.

A single selected row can remain a matrix. Specifying a row with one index, as in matrix[1, 1:4], gives the one-dimensional array [21 22 23]. Specifying it with a slice, as in matrix[1:2, 1:4], keeps the result two-dimensional: [[21 22 23]]. The values are the same, but their organization has a different shape.

Change a Selected Region

The examples so far have selected values and printed them. Use the same selection methods to write new data into the chosen cells.

Return to the three-row, five-column matrix with values from 10 to 34. Select rows at indices 0 and 1 and columns at indices 1, 2, and 3, then fill the region with zeros:

matrix[0:2, 1:4] = 0
print(matrix)

The result is:

[[10  0  0  0 14]
 [20  0  0  0 24]
 [30 31 32 33 34]]

The left side of = specifies where to write the data: the slices select six elements. The right side specifies what to write: the single number 0. NumPy assigns that value to every selected element.

Printing the whole matrix shows the changes in place. Values outside the selected rectangle remain unchanged: the first and last positions in the top two rows, and the entire bottom row.

The same action can be written with familiar loops:

for row in range(0, 2):
    for col in range(1, 4):
        matrix[row, col] = 0

The loops explicitly visit each required pair of indices. Slice notation describes the whole region affected by the assignment. Both versions produce the same matrix values.

Clear the entire matrix by selecting all rows and columns:

matrix[:, :] = 0
print(matrix)

The result is:

[[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]

The matrix keeps its shape: it still has three rows and five columns. The values of all its elements have changed.

matrix[:, :] = 0 and matrix = 0 perform different actions. The first writes zeros into the elements of an existing array. The second associates the name matrix with the single number 0: that name no longer refers to the previous array. To clear the cells, keep the element selection on the left.

Build a small binary picture on this empty field. Fill columns at indices 1, 2, and 3 with ones in every row starting at row index 1:

matrix[1:, 1:4] = 1
print(matrix)

The result is:

[[0 0 0 0 0]
 [0 1 1 1 0]
 [0 1 1 1 0]]

The result is a rectangle of six ones. Pass this matrix to the familiar display code, and those six cells appear dark while the zero cells appear light. Changing the slice bounds changes the rectangle's position or dimensions.

On the right of the assignment is a scalar: the number 1. On the left is a region containing several matrix elements. One value sets the state of every selected cell. The expression specifies which data to change and which value to write. This numerical description produces a visible pattern.

END OF THE EXCERPT

Predict the picture.
Then change the data.

In the browser lab, start with an empty field. Predict how many cells image[2:6, 2:6] selects, then apply the operation. Compare it with the other available selections.

Read the beginner excerpt: your first window →

Original text from Module 6 of Matrix Thinking by Oleg Gornov, PhD. Introductory guidance, the preview illustration and links are website additions. Book text and artwork remain reserved.