Matrices
Tables of numbers that hold datasets and model weights.
Create a free account Try part of it here
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
- A matrix is a spreadsheet
- Rows, columns and single cells in Python
- Down the columns or along the rows
- Building a table from data
- Putting columns on a fair scale
- Worked example: a z-score by hand
- 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:
| blank | spend | clicks | sales |
|---|---|---|---|
| campaign A | 3120 | 5470 | 301 |
| campaign B | 1890 | 2050 | 88 |
| campaign C | 6400 | 3800 | 41 |
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
Matrices get capital letters. The number in row , column is written . 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
A[:, 1] reads "every row, column 1". The colon means "all of them".
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=0goes down each column. You get one answer per column: "average spend, average clicks, average sales".axis=1goes along each row. You get one answer per row: one total per campaign.
Try both below.
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"])
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:
- subtract the column's average, so "typical" becomes 0;
- 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
Read it as: "the z-score in row i, column j is the original value minus that column's mean (the Greek letter mu), divided by that column's standard deviation (the Greek letter sigma)."
scores = [50, 70, 90] mean = sum(scores) / len(scores) print([s - mean for s in scores])
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: 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))
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
Try a quiz question
In a data matrix, what does each row usually represent?