Index Ai Academy
Phase 2 · Math, when you need it · Linear algebra

Matrices

Tables of numbers that hold datasets and model weights.

About 25 minutes · A free account opens the whole lesson

Create a free account Try part of it here

Already have an account? Sign in to open this lesson

Why it matters

A spreadsheet of customers, a batch of AI embeddings, the settings inside a neural network: all of them are matrices, which is the math word for a table of numbers. AI code almost never deals with records one at a time. It puts them all in one table and works on the whole thing at once. Being comfortable with rows and columns is what makes AI code readable instead of mysterious.

What you'll be able to do

  • Read a matrix's shape and its entries in math notation and in NumPy
  • Treat a dataset as a matrix: rows are examples, columns are features
  • Select rows and columns, and compute per-column statistics with axis
  • Standardize the columns of a data matrix so features are comparable

What it covers

  1. A matrix is a spreadsheet
  2. Rows, columns and single cells in Python
  3. Down the columns or along the rows
  4. Building a table from data
  5. Putting columns on a fair scale
  6. Worked example: a z-score by hand
  7. Rows are examples, columns are features

A matrix is a spreadsheet

You already know matrices. Any spreadsheet that holds only numbers is one. Here are three ad campaigns, with spend, clicks and sales in the columns:

blankspendclickssales
campaign A31205470301
campaign B1890205088
campaign C6400380041

A matrix is that grid of numbers without the labels. Its shape is (rows, columns). This one is 3 rows by 3 columns, written "3 × 3" and said "3 by 3".

In data work there's one fixed habit: each row is one example (a campaign, a customer, a document) and each column is one feature. So each row is the vector from the vectors lesson, and a matrix is those vectors stacked on top of each other.

In plain words: a matrix is a table of numbers. Rows are the things; columns are what you measured about them.

Go deeper How math writes a matrix
A=[3120547030118902050886400380041]∈R3×3A = \begin{bmatrix} 3120 & 5470 & 301 \\ 1890 & 2050 & 88 \\ 6400 & 3800 & 41 \end{bmatrix} \in \mathbb{R}^{3 \times 3}

Matrices get capital letters. The number in row ii, column jj is written AijA_{ij}. Read the line above as: "A is a 3-by-3 table of real numbers."

Rows, columns and single cells in Python

NumPy stores a matrix as a 2-D array. You pick things out with square brackets: first the row, then the column, both counted from 0.

import numpy as np

A = np.array([[3120, 5470, 301],
              [1890, 2050, 88],
              [6400, 3800, 41]])
print(A.shape)       # (rows, columns)
print(A[1, 2])       # row 1, column 2 (counting from 0): conversions of the second campaign
print(A[0])          # the whole first row: one campaign
print(A[:, 1])       # the whole second column: clicks for every campaign
It runs right here in your browser, with nothing to install.

A[:, 1] reads "every row, column 1". The colon means "all of them".

Quick check
A matrix holds 500 customers (rows) and 12 measurements about each (columns). What is its shape?
  1. (6000,)
  2. (12, 500)
  3. (500, 12)
  4. (512,)
Show the answer

(500, 12)

Shape is (rows, columns): 500 customers by 12 features.

Down the columns or along the rows

When you total or average a table, you have to say which way. Think of a spreadsheet's total row at the bottom versus a total column at the right.

  • axis=0 goes down each column. You get one answer per column: "average spend, average clicks, average sales".
  • axis=1 goes along each row. You get one answer per row: one total per campaign.

Try both below.

Try it now

Switch between axis=0 and axis=1, and between sum and mean. Hover a cell to see what's combined.

For a data table, A.mean(axis=0) is the average of each column: the "typical campaign". A.sum(axis=1) adds up each row, which only makes sense if the columns are in the same units. Adding dollars to clicks gives a meaningless number.

Building a table from data

Here a whole list of campaigns from the practice API becomes one table. argmax finds the position of the largest number in a column, which you can then use to look up the campaign.

import numpy as np
import requests

FEATURES = ["spend", "clicks", "conversions", "revenue"]
campaigns = requests.get("https://api.gradient.test/v1/campaigns").json()["data"]
X = np.array([[c[f] for f in FEATURES] for c in campaigns], dtype=float)
print(X.shape)
print("column means:", X.mean(axis=0).round(1))
print("best revenue row:", int(X[:, 3].argmax()), campaigns[int(X[:, 3].argmax())]["name"])
Quick check
X has shape (500, 12): 500 customers, 12 features. What's the shape of X.mean(axis=0)?
  1. A single number
  2. (12,)
  3. (500, 12)
  4. (500,)
Show the answer

(12,)

axis=0 goes down each column, leaving one average per feature: 12 numbers.

Putting columns on a fair scale

The campaign columns live on very different scales. Revenue runs into thousands; sales are in tens. Many AI methods treat a bigger number as more important, so the big columns drown out the small ones. It's like comparing a person's height in millimeters with their age in years.

The usual fix is to standardize each column. For every number:

  1. subtract the column's average, so "typical" becomes 0;
  2. divide by the column's standard deviation (its typical distance from the average), so every column has the same spread.

The result is called a z-score. A z-score of 1.5 means "1.5 typical steps above average for this column", which you can compare across columns.

In plain words: z-score = (value − column average) ÷ column spread. It says how unusual a number is for its own column.

import numpy as np

X = np.array([[3120, 5470, 301], [1890, 2050, 88], [6400, 3800, 41], [120, 1920, 132]], dtype=float)
Z = (X - X.mean(axis=0)) / X.std(axis=0)
print(Z.round(2))
print("means:", Z.mean(axis=0).round(6), " stds:", Z.std(axis=0).round(6))

After standardizing, every column averages 0 with a spread of 1. NumPy did this for the whole table in one line: it subtracted the row of averages from every row at once. That stretching of one row across many is called broadcasting.

Go deeper The formula
Zij=Xij−μjσjZ_{ij} = \frac{X_{ij} - \mu_j}{\sigma_j}

Read it as: "the z-score in row i, column j is the original value minus that column's mean μj\mu_j (the Greek letter mu), divided by that column's standard deviation σj\sigma_j (the Greek letter sigma)."

Quick check
Centering a column subtracts its mean from every value. What does this print?
scores = [50, 70, 90]
mean = sum(scores) / len(scores)
print([s - mean for s in scores])
  1. [0.0, 0.0, 0.0]
  2. [50, 70, 90]
  3. [-1.0, 0.0, 1.0]
  4. [-20.0, 0.0, 20.0]
Show the answer

[-20.0, 0.0, 20.0]

The mean is 70.0, so each score becomes its distance from 70. Dividing by the spread would then give the z-scores.

Worked example

Worked example: a z-score by hand

Three campaigns spent $100, $200 and $300. How unusual is the $300 one? Work out its z-score by hand, then check with NumPy.

import numpy as np

spend = np.array([100.0, 200.0, 300.0])
mean = spend.mean()
spread = spend.std()
print("average:", mean)
print("spread:", round(float(spread), 1))
print("z-score of 300:", round(float((300 - mean) / spread), 2))
In the lesson you can change this and run it.

The average is 200, so $300 is 100 above it. The spread (standard deviation) is about 81.6. 100 ÷ 81.6 ≈ 1.22, so the $300 campaign is about 1.2 typical steps above average. Do the same to every column and they all become comparable.

Rows are examples, columns are features

Nearly every data table in AI follows one convention: each row is one example, such as a customer, and each column is one feature, such as their spend. Keep that picture in mind and much of what follows gets easier.

It also settles the axis question that trips up everyone. axis=0 works down the columns and gives you one answer per feature. axis=1 works along the rows and gives you one answer per example. Ask yourself what you want one answer for, then choose.

Two more things to watch. Don't add up across columns with different units, since orders plus dollars isn't a meaningful number. That's why you put columns on a fair scale first. And check for a column that never changes before you divide by its spread. A standard deviation of 0 means dividing by zero, and every value in that column turns into nan. If a whole column goes nan after scaling, that's the first thing to look for.

Practice what you just read

  • 4 code examples you can edit and run
  • a practice exercise that checks your code
  • a 5-question quiz
  • 4 review cards for spaced repetition

Create a free account

Try a quiz question

In a data matrix, what does each row usually represent?

  1. One example (a customer, a campaign, a document)
  2. One feature (a price, a date, a click count)
  3. One model weight (a number the model learned)
  4. One time step (and each column is one example)

Answer it at the end of the lesson to see if you're right, and why.

Before this lesson