Exercises — Array creation — np.zeros, np.ones, np.linspace, np.arange, np.random
Below is our reference figure. On a dark chalkboard it draws two rulers stacked vertically. The top ruler (yellow dots) is np.linspace(0, 1, 5): five glowing fenceposts at 0, 0.25, 0.5, 0.75, 1, with four blue "gap" braces between them — showing that 5 points make only 4 gaps. The bottom ruler (pink dots) is np.arange(0, 1, 0.25): marks at 0, 0.25, 0.5, 0.75, with a dashed white stop wall at 1.0 carrying a white "×" to signal that the stop is excluded. Keep this picture in mind — throughout the page "the top ruler" means linspace's included-endpoint behaviour and "the bottom ruler" means arange's excluded-stop behaviour.

The symbol is the ceiling: round up to the next whole number (e.g. , ). We need "round up" because arange keeps taking whole steps and stops the moment the next step would reach or cross stop; it never emits a value at or beyond stop. The ceiling simply counts how many full steps fit strictly inside the interval.
Level 1 — Recognition
Can you read a call and predict its output shape, dtype, and contents?
L1.1
What does np.zeros((2, 3)) return? Give shape, dtype, and the array.
Recall Solution L1.1
WHAT: shape is passed as one tuple (2, 3) → 2 rows, 3 columns.
WHY float: the default dtype of np.zeros is float64, so the entries print with a dot (0.).
Shape (2, 3), dtype float64, six zeros.
L1.2
What is the dtype and the array for np.ones(4, dtype=int)?
Recall Solution L1.2
A bare 4 (not a tuple) means a 1-D array of length 4. We overrode the default float with dtype=int.
Result: array([1, 1, 1, 1]). The dtype is the platform's default integer — typically int64 on 64-bit Linux/macOS, but int32 on some systems (e.g. Windows). The reliable takeaway: it is an integer dtype (note no dots), not the concrete bit-width.
L1.3
Predict the exact output of np.arange(3).
Recall Solution L1.3
One argument means start=0, step=1, stop=3 (exclusive).
array([0, 1, 2]) — an integer dtype (arange keeps integer dtype when all args are ints; the exact width is platform-dependent). The value 3 is not included.
Level 2 — Application
Plug into the two formulas and produce the actual numbers.
L2.1
Compute the step and list every element of np.linspace(0, 1, 5).
Recall Solution L2.1
WHY num−1: 5 points create 4 gaps (fenceposts vs gaps — see the top ruler in the figure).
Elements for :
[0. , 0.25, 0.5 , 0.75, 1. ]. The stop 1.0 is included (endpoint=True).
L2.2
How many elements does np.arange(2, 20, 3) produce, and what is the last one?
Recall Solution L2.2
Elements: 2, 5, 8, 11, 14, 17. The last is 17; the next would be 20 which equals stop and is excluded.
L2.3
Give the step of np.linspace(0, 10, 10). (Students expect step 1 — check!)
Recall Solution L2.3
Not 1. To get step exactly 1 with endpoint 10, you need 11 points: np.linspace(0, 10, 11).
Level 3 — Analysis
Reason about edge cases, floats, and why a call behaves oddly.
L3.1
np.arange(0, 1, 0.1) — is its length 10 or 11? Explain in terms of floating point.
Recall Solution L3.1
By the formula, , if arithmetic were exact.
WHY it wobbles: 0.1 cannot be stored exactly in binary (Floating-point representation and rounding errors). The stored value is slightly less than 0.1, so 10 accumulated steps land at roughly 0.9999999… — just under 1. Since that is < stop, one more value squeezes in, and you can get length 11 with a final entry near 0.9999999999999999.
In practice CPython/NumPy here returns length 10 for this exact case, but the point stands: the count is not guaranteed. Never rely on it.
Fix: for a known count over an interval, use np.linspace(0, 1, 10, endpoint=False) — deterministic.
L3.2
What does np.linspace(5, 5, 4) return? (Degenerate: start = stop.)
Recall Solution L3.2
. Every point sits on top of the other:
array([5., 5., 5., 5.]). No error — linspace happily makes a constant array of length num.
L3.3
What does np.linspace(2, 0, 5) give? (Descending: start > stop.)
Recall Solution L3.3
The formula does not care about direction:
, a negative step.
[2. , 1.5, 1. , 0.5, 0. ] — counts down, endpoints 2 and 0 both included.
L3.4
Now the arange mirror of L3.3: what does np.arange(2, 0, -0.5) give, and how does the count formula behave when step < 0?
Recall Solution L3.4
WHY the formula still works: with a negative step, both numerator and denominator flip sign. For np.arange(2, 0, -0.5):
The two minus signs cancel, so stays positive — exactly what you want (a count can't be negative). Values 2. , 1.5, 1. , 0.5, marching downward, with stop = 0 excluded (just like the ascending case excludes its stop).
Sanity check on direction: arange only produces a non-empty array when the step points from start toward stop. np.arange(2, 0, +0.5) would ask "walk up from 2 to reach 0" — impossible, so it returns an empty array of length 0 (the ratio is negative, and NumPy floors the count at 0).
Level 4 — Synthesis
Combine several tools to hit a target result.
L4.1
Build a 1-D array of the odd numbers 1, 3, 5, …, 19 two different ways: once with arange, once by transforming a linspace. Confirm they match.
Recall Solution L4.1
arange way: start 1, step 2, stop just past 19 → np.arange(1, 20, 2).
Count . Values 1,3,…,19. ✅
linspace way: 10 evenly spaced points from 1 to 19 (both included):
np.linspace(1, 19, 10) → → 1,3,…,19. ✅
Both give the same 10 values. Because came out to a round integer here, linspace is exact.
L4.2
Preallocate a length-6 float buffer with np.zeros, then fill entry with . What is the final array, and why preallocate?
Recall Solution L4.2
out = np.zeros(6)
for i in range(6):
out[i] = i**3Result: [ 0., 1., 8., 27., 64., 125.] (floats, because the buffer is float64).
WHY preallocate: the buffer is one fixed block of memory. Filling it in place avoids the repeated re-allocation and copying that growing a Python list would incur — the standard fast pattern (see Vectorization vs Python loops).
L4.3
Make a array where every element equals 7 — without using np.full. Then confirm its shape and total.
Recall Solution L4.3
Start from ones and scale: 7 * np.ones((3, 3)).
Each of the 9 entries is 7.0. Shape (3, 3). Sum .
(np.full((3,3), 7) is the direct route, but scaling np.ones proves you understand that array creators return real arrays you can do arithmetic on.)
Level 5 — Mastery
Full control: reproducibility, statistics, and multi-constraint construction.
L5.1
You set rng = np.random.default_rng(0) and draw a large sample rng.normal(3, 2, 100000). What do you expect for the sample mean and standard deviation, and why can a teammate reproduce your exact numbers?
Recall Solution L5.1
rng.normal(loc, scale, size) draws from a Gaussian with mean loc = 3 and std scale = 2 (Random sampling and distributions).
For a large sample the sample mean and sample std (with small random wobble that shrinks as size grows).
Reproducibility: the seed 0 fixes the generator's internal state, so default_rng(0) on any machine produces the identical stream of numbers — same mean, same std, byte-for-byte.
L5.2
Design one line that produces the array [0. , 0.2, 0.4, 0.6, 0.8] — five points, step 0.2, but excluding 1.0 — using linspace (not arange, to dodge float drift). Justify the argument choice.
Recall Solution L5.2
Use endpoint=False: np.linspace(0, 1, 5, endpoint=False).
WHY: with endpoint=False, linspace treats stop as a phantom fencepost it never keeps, so it divides the interval into num gaps:
, giving 0, 0.2, 0.4, 0.6, 0.8.
This is the deterministic replacement for the drifty np.arange(0, 1, 0.2).
L5.3
Given rng = np.random.default_rng(0), rng.integers(10, 20, 4) returns four integers. What is the allowed range of each, and could the value 20 ever appear?
Recall Solution L5.3
rng.integers(low, high, size) draws uniform integers in — high is exclusive, mirroring arange.
So each value is one of 10, 11, 12, …, 19. The value 20 can never appear. (If you needed 20 possible, you'd pass high=21 or set endpoint=True.)
Recall Feynman: the whole ladder in one breath
Two rulers rule everything. linspace = "give me num marks, both ends painted" → gap (b−a)/(num−1). arange = "step by s, stop before the wall" → count ceil((b−a)/s), and a negative s walks downward (two minus signs keep the count positive). Floats crack arange when the step is fractional, so switch to linspace. zeros/ones are blank cartons (shape is one tuple). And default_rng(seed) is dice with a magic replay word.
Connections
- Parent: Array creation (topic note)
- NumPy arrays — shape, dtype, ndim
- Vectorization vs Python loops
- Array indexing and slicing
- Reshaping — reshape, ravel, newaxis
- Plotting functions with Matplotlib
- Random sampling and distributions
- Floating-point representation and rounding errors