Command Palette

Search for a command to run...

Hectal
PHASE 14Intermediate ~30 min· topic 4 of 10

Topic 14.4

Aggregations, Statistics and Random Numbers

In one line

Aggregations like sum, mean, std and argmax collapse an array along an axis into fewer numbers. A seeded random generator, np.random.default_rng(seed), gives random-looking data that you can repeat exactly.

Think of it like this

A class mark sheet. The total of one student is a sum across the columns. The class average of one subject is a mean down the rows. "Who got the highest mark?" is an argmax. Aggregations answer questions like these, and axis says which direction you add along.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Aggregation (reduction)
An operation that collapses many numbers into fewer, like sum or mean.
Mean
The average: the sum divided by the count.
Standard deviation (std)
How spread out numbers are around their mean.
argmax / argmin
The position of the largest or smallest value.
np.where
A vector if/else: choose between two values per element, based on a condition.
Seed
A starting number for a random generator. The same seed gives the same sequence.
Generator (rng)
The object from np.random.default_rng that produces random numbers.
keepdims
Keep the reduced axis as size 1 so the result can broadcast back.

Step by step

01Totals, means and spread

For m = [[3, 1, 2], [9, 7, 8]], m.sum() adds all six numbers. m.mean(axis=0) gives one mean per column and m.mean(axis=1) one per row. m.std(axis=1) gives the spread of each row. The axis you name is the one that disappears.

main.pywhole filepython
m = np.array([[3.0, 1.0, 2.0],
              [9.0, 7.0, 8.0]])
print(m.mean(axis=0))    # [6. 4. 5.]
print(m.mean(axis=1))    # [2. 8.]

02Which one is biggest?

m.argmax(axis=1) returns the column index of the largest value in each row: [0 0]. Without an axis, argmin works on the flattened array and returns a position in that flat order (1 here). np.argsort(m[0]) returns [1 2 0]: the order of positions that sorts the row ascending.

03np.where as a vector if

np.where(m > 5, "high", "low") builds an array of labels in one go. With a number in both slots it replaces the values: np.where(m > 5, m, 0.0) keeps the big values and zeroes the rest.

04Reproducible randomness

Two generators created from seed 42 produce the same numbers, so any experiment that uses one can be re-run. Choose a seed once at the top, pass rng into functions that need randomness, and write the seed in your notes.

Different methods draw different kinds of numbers: random for floats in [0, 1), integers(low, high) with high excluded, normal for a bell curve, choice for sampling and permutation for shuffling.

main.pywhole filepython
rng = np.random.default_rng(42)
print(rng.random(3).round(4))   # same on every run

05The law of large numbers, in miniature

A thousand draws from a normal distribution with mean 50 and std 10 give a sample mean close to 50 but not exactly. The more draws, the closer you get. Statistics on samples are estimates, which is why a model's score on 100 test examples is a rougher guide than on 100,000.

The law of large numbers, in miniaturediagram
Rendering diagram…

Try it yourself

  1. 1

    Rank the scores

    With s = np.array([72, 55, 90, 41]), print np.argsort(-s). What does the first number mean? (It is the position of the best score.)

  2. 2

    Roll a die 6000 times

    Use rng.integers(1, 7, size=6000) and np.bincount on the result (np.bincount counts how many times each whole number appears). Each face should come up near 1000 times, but not exactly.

  3. 3

    Same seed, same shuffle

    Call np.random.default_rng(1).permutation(5) twice in a row and compare. Then create one rng and call permutation(5) twice on it. Why do the two calls differ?

Code & diagrams

Aggregations along an axis New tab

Add axis=0 to argmax and predict the three indices.

Sign in to run this example in your browser.

Expected output

sum: 30.0 mean: 5.0
mean axis 0: [6. 4. 5.]
mean axis 1: [2. 8.]
std axis 1: [0.816 0.816]
argmax axis 1: [0 0] argmin: 1
min/max: 1.0 9.0
keepdims: [[2.0], [8.0]]
where: [['low', 'low', 'low'], ['high', 'high', 'high']]
where 3 args: [[0.0, 0.0, 0.0], [9.0, 7.0, 8.0]]
sorted idx: [1 2 0]
Seeded random numbers New tab

Change the seed 42 to 43 and see every number change; keep 42 and the output never changes.

Sign in to run this example in your browser.

Expected output

same seed A: [0.774  0.4389 0.8586]
same seed B: [0.774  0.4389 0.8586]
integers: [6 4 5 6 4]
normal: [-0.891 -0.455 -0.992]
choice: ['d' 'c']
permutation: [3 8 4 6 5 2 0 7 1 9]
mean ~50: 49.33 std ~10: 9.45
Missing readings: NaN spreads, and sentinel codes poison averages New tab

Some systems store "no reading" as a magic number such as -999. Averaging it in gives a nonsense mean. Convert it to NaN first, then use the nan-aware functions. A NaN in a plain mean makes the whole answer NaN, which is NumPy telling you that data is missing.

Sign in to run this example in your browser.

Expected output

naive mean: -313.1666666666667
with NaN: [31.5  nan 29.  30.5  nan 28. ]
mean of NaN data: nan
nanmean: 29.75
missing count: 2
NaN == NaN: False
argmax with NaN: 1 nanargmax: 0
max with NaN: nan nanmax: 31.5

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Expecting random to repeat without a seed

Compare two unseeded generators.

main.pywhole filepython
import numpy as np

a = np.random.default_rng().random(2)
b = np.random.default_rng().random(2)
print(np.array_equal(a, b))
terminal
$ python main.py
── what you'll see ──
False

Myth vs fact

Myth

A seeded generator is not random.

Fact

It produces a fixed pseudo-random sequence that passes statistical tests but repeats given the same seed. That is exactly what testing needs. It is not suitable for passwords: use the secrets module.

Myth

np.std and pandas std are the same.

Fact

NumPy divides by n by default; pandas uses n - 1. Use ddof=1 in NumPy to match.

Myth

argmax returns the maximum.

Fact

It returns the position of the maximum. Use max for the value.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    default_rng uses the PCG64 bit generator and supports independent child streams with SeedSequence.spawn, so parallel workers can have separate, reproducible streams. Seeding each worker with the same number would give identical data.

  • ▸

    Floating-point sums depend on the order of addition. NumPy uses pairwise summation, which is more accurate than a naive loop, but results can differ slightly between machines and array layouts, so compare floats with np.allclose, never ==.

  • ▸

    Aggregations over a very large integer array can overflow its dtype (Topic 14.1). np.mean on integers computes in float64, while np.sum keeps the integer type. Cast to float64 or int64 for large totals.

  • ▸

    NaN propagates. Any arithmetic with NaN gives NaN, so np.mean of data with one gap is nan, and NaN == NaN is False (test with np.isnan). The nan-aware family (np.nanmean, np.nansum, np.nanstd, np.nanmax, np.nanargmax) skips the gaps. Plain argmax and max on data with NaN return the NaN, which is usually a bug. pandas does the opposite by default (skipna=True), see Topic 14.7. Integer arrays cannot hold NaN at all, which is why a column with a gap becomes float.

  • ▸

    argmax and argmin return the first position when there are ties, and argsort is not stable by default: pass kind="stable" when equal scores must keep their original order, for example when ranking search results. np.argpartition(-scores, k)[:k] finds the top k without fully sorting, which is cheaper for millions of items but gives them unordered.

  • ▸

    The legacy API (np.random.seed, np.random.rand, RandomState) shares one hidden global generator. RandomState promises the same stream forever, which is the reason old code and tutorials still use it. For new code use default_rng, and pass the generator explicitly so tests, threads and libraries do not disturb each other.

Remember this

  1. 1

    The core aggregations are sum, mean, min, max, std and var. The mean is the usual average: add everything up, divide by how many there are. The standard deviation (std) says how spread out the numbers are around the mean. Picture two classes that both average 50 marks. In class A everyone scored 48 to 52, so the std is small. In class B half scored 10 and half scored 90, so the std is large. The variance (var) is the std squared. Each works on the whole array by default and returns one number, or along one axis, returning an array with that axis removed. They exist as methods (m.sum()) and as functions (np.sum(m)).

  2. 2

    argmax and argmin return the position of the largest or smallest value, not the value. Along an axis they give one position per row or column. np.argsort(a) gives the positions that would sort a, which is how you rank items and how top-k search works in Topic 14.5.

  3. 3

    np.where(condition, x, y) is a vector if: it picks x where the condition is true and y elsewhere. With only a condition it returns the positions where it is true. Use it to label data ("high" or "low") or to replace values without a loop.

  4. 4

    keepdims=True keeps the reduced axis with size 1. That makes the result broadcast back against the original array, for example m - m.mean(axis=1, keepdims=True) centres every row. Without it you would need a reshape.

  5. 5

    Random numbers: create a generator with rng = np.random.default_rng(seed), then call rng.random(3) (floats from 0 to 1), rng.integers(1, 7, size=5) (dice rolls, stop excluded), rng.normal(loc, scale, size) (numbers that cluster around loc and thin out symmetrically, the bell-curve distribution; scale is the std), rng.choice(items, size, replace=False) (note that the default is replace=True, so the same item can be drawn twice unless you say otherwise) and rng.permutation(n) (a shuffled 0..n-1). The same seed gives the same numbers on every run, which is how you make experiments reproducible. Keep the NumPy version fixed too: NumPy does not promise that a Generator produces the same stream in every release.

  6. 6

    Prefer default_rng over the older np.random.seed(...) and np.random.rand global functions. A generator is an object you pass around, so two parts of a program cannot disturb each other's random streams. For machine learning, default_rng(seed) is how you shuffle data into train and test sets repeatably (see the exercises).

  7. 7

    Statistics with a catch: np.std divides by n by default (it treats your numbers as the whole population), while pandas Series.std divides by n - 1 (it treats them as a sample drawn from a bigger group). They give different numbers for the same data. Know which one a library uses.

  8. 8

    Recall from Topic 14.2: axis is the direction you work along, and the axis you name disappears from the result's shape. Everything below uses that rule.

Explain it without notes

01

What does axis mean in m.sum(axis=0) versus m.sum(axis=1) for a table?

02

What is the difference between max and argmax, and where is argsort used?

03

Why use np.random.default_rng(seed) rather than unseeded randomness in a data experiment?

04

What does np.where(cond, a, b) do and why is it better than a loop?

05

A sensor file has gaps stored as -999. Why is values.mean() wrong, and what do you do instead?

06

Why do np.std and pandas Series.std disagree on the same data?

Practice

01

For m = np.array([[1.0, 2.0], [3.0, 4.0]]), print the column means and the row sums.

02

Using default_rng(0), make an array of 5 dice rolls and print how many are above 3.

03

Replace negative numbers in x = np.array([-2.0, 3.0, -1.0, 5.0]) by 0 using np.where.

04

For x = np.array([2.0, 4.0, 6.0, 8.0, 10.0]) print the standard deviation the NumPy way and the pandas way (the sample version), rounded to 3 places.

Trade-offs

  • ↔

    Population versus sample statistics. ddof=0 and ddof=1 differ; for tiny samples the gap is large.

  • ↔

    Reproducibility versus unpredictability. Seeds are ideal for tests and papers and wrong for security.

  • ↔

    Simple aggregates hide structure. A mean can hide a distribution (the way values are spread out) with two humps, such as a class where half scored 10 and half scored 90. Look at a histogram (Topic 14.9) too.

Done when you can

  • Done when you can use sum, mean, std, min and max with and without axis.

  • Done when you can explain argmax and rank items with argsort.

  • Done when you can write an np.where that labels or replaces values.

  • Done when you can create a seeded generator and draw floats, integers, normals, choices and shuffles.

  • Done when you can explain why a seed makes results repeatable but not secure.