NumPy·Lesson 20·10 min·0/4 exercises

Random numbers and sampling

Seed it, or you can’t tell a real change from noise.

default_rngrng.randomrng.integersrng.normalrng.choicerng.permutationseeds

Watch it happen

Play it through, or step back and forth yourself.

rng = np.random.default_rng(0)rng.random(3)[0.637 0.270 0.041]
rng = np.random.default_rng(0)rng.random(3)[0.637 0.270 0.041]
rng = np.random.default_rng(1)rng.random(3)[0.511 0.951 0.144]

Modern NumPy wants you to create a generator first: rng = np.random.default_rng(0). The 0 is the seed. The older np.random.rand style still works but shares one hidden global generator, which is exactly the thing that makes results irreproducible.

The idea

Modern NumPy asks you to make a generator first, and then draw from it:

rng = np.random.default_rng(0)
rng.random(3)      # [0.637 0.270 0.041]

You'll still see np.random.rand and np.random.seed in older tutorials. They work, but they share one hidden global generator, so any library you import can move your numbers underneath you. The generator form keeps randomness local and explicit.

The seed is the point

That 0 is the seed, and it makes the sequence reproducible: the same numbers, every run, on every machine. Without it you can't tell whether your model improved or you just got a luckier shuffle.

Seed once, at the top. Re-seeding inside a loop is a classic mistake — it correlates your draws instead of decorrelating them, and in the worst case gives you the identical "random" value every iteration.

Hand-drawn notes showing the modern generator object carrying its own seed, against the older global np.random.seed that everything else on the machine shares.

The distributions you'll actually use

rng.random(size)                  # uniform floats in [0, 1)
rng.integers(low, high, size)     # whole numbers, high excluded
rng.normal(loc, scale, size)      # the bell curve
rng.standard_normal(size)         # normal with mean 0, spread 1

rng.integers excludes the high end, matching range. (The old randint did too, but random_integers didn't — one of several reasons the new API exists.)

Sampling from data you already have

rng.choice(x, 3)                    # three draws, repeats allowed
rng.choice(x, 3, replace=False)     # three distinct items
rng.choice(x, 5, p=weights)         # weighted; p must sum to 1

replace=False is the difference between "roll a die three times" and "deal three cards". Asking for more items than exist without replacement is an error, which is the right behaviour.

Hand-drawn notes contrasting sampling without replacement, where each item can be drawn at most once, with sampling with replacement, where the same item can come up twice.

Shuffling

rng.shuffle(x)          # in place, returns None
rng.permutation(x)      # a shuffled copy

The same in-place-versus-copy split as sort. And the same trick as argsort applies when you have parallel arrays — shuffle the indices, then apply them to everything:

order = rng.permutation(len(features))
features, labels = features[order], labels[order]

Shuffling features and labels separately would destroy the correspondence — the same class of bug as sorting one column of a table, and it silently produces a model that learns nothing.

On a 2-D array, rng.shuffle only shuffles along axis 0 — it reorders rows and leaves each row intact, which is nearly always what you want for a dataset.

Practice

Write it yourself. The answer is there when you want it.

Putting the kettle on…

Starting up…

Write it yourself

not graded

Make rng = np.random.default_rng(0), then print three uniforms, five integers under 10, and four normals around 50 rounded to one decimal. Then print np.random.default_rng(0).random(3) twice, to prove the seed decides the numbers. Finish by drawing 3 of [10, 20, 30, 40] without replacement.

Write something and press Run — the output appears here.

Your turn

4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Create a generator seeded with 42 and return three uniform floats from it.

your answer

Using a generator seeded with 0, return five integers in [0, 100).

your answer

From v, draw two distinct values using a generator seeded with 7.

your answer

Return v shuffled with a generator seeded with 3, as a new array — leaving v itself untouched.

your answer