5.4.2 · D5Scientific Computing (Python)

Question bank — Array creation — np.zeros, np.ones, np.linspace, np.arange, np.random

1,868 words8 min readBack to topic
Figure — Array creation — np.zeros, np.ones, np.linspace, np.arange, np.random

True or false — justify

Every "answer" here is the reason, not just the verdict.

np.zeros(3, 4) makes a 3×4 grid of zeros.
False. Shape is ONE argument. The second positional slot is the dtype (the data type), so 4 is read as a (nonsense) dtype and it errors. You must write np.zeros((3, 4)) with the tuple.
np.linspace(0, 1, 5) has a step of 1/5 = 0.2.
False. 5 points fence off only 4 gaps (see the top line of the figure), so the step is . Divide by points minus one, never by points.
np.arange(0, 1, 0.25) includes the value 1.0.
False. arange stop is exclusive, like Python range — that is the hollow amber ring in the figure. It gives [0, 0.25, 0.5, 0.75] and stops before reaching 1.0.
np.linspace(0, 1, 5) includes both 0 and 1.
True. endpoint=True is the default, so both ends are kept (the two filled amber dots) — that is exactly what linspace is for.
The default dtype of np.zeros(4) is integer.
False. It is float64. Scientific math wants floats, so you see 0. not 0. Pass dtype=int if you truly need whole numbers.
np.empty(5) returns five zeros.
False. It returns uninitialized memory — whatever bytes were already there (garbage). It only looks like zeros by luck on a fresh page; never rely on it.
np.linspace(0, 10, 10) has step 1.
False. 10 points → 9 gaps → step For a step of exactly 1 use np.linspace(0, 10, 11) or np.arange(0, 11).
Passing the same seed to default_rng always gives the same numbers.
True. The seed fixes the generator's starting state, so the whole sequence is reproducible — that is the entire point of a seed.
np.full((2,2), 7) and np.ones((2,2))*7 give the same result.
True in values, but np.full says the intent directly and does it in one pass; multiplying ones builds an intermediate array. Prefer full for clarity.

Spot the error

Say what is wrong and why, then how to fix it.

out = [] then a loop doing out.append(i**2) — is this the "pro pattern"?
It works but it is the slow pattern: a Python list grows by repeated reallocation. Preallocate with np.zeros(n) and assign into out[i] so the buffer never moves.
np.arange(0, 1, 0.1) — someone asserts it has exactly 10 elements.
Wrong to rely on it. 0.1 is not exact in floating-point, so the accumulated stop test may include or exclude the last value; length can be 10 or 11. Use np.linspace(0, 1, 11) (or 10) when the count matters.
np.zeros((3, 4), int) — is int the shape or the dtype?
It is the dtype — the data type in the second positional slot. This is correct and gives an integer 3×4 array of zeros. The trap is only when you forget the tuple around the shape.
rng = np.random.rand(3) — is this the modern generator API?
No — np.random.rand is the legacy global function sharing hidden state. The modern form is rng = np.random.default_rng(seed) then rng.random(3).
np.linspace(5, 0, 4) — will this error because start > stop?
No error. linspace happily counts downward: the step is negative, giving [5, 3.33, 1.67, 0]. It only cares about producing num evenly spaced points.

Why questions

np.linspace divides by num - 1 but np.arange uses ceil((stop - start)/step). Why the different formulas?
linspace is told the count and must compute the gap between fixed endpoints — hence "gaps = points − 1". arange is told the gap and must compute the count of steps that fit before the exclusive stop — hence the ceiling (justified in the next question).
Why does arange round up with a ceiling to count its elements?
Because it keeps stepping as long as it is still below stop. Suppose start = 0, stop = 1, step = 0.3. The exact number of steps is . You cannot take a third-of-a-step, but you do land at — four values, all still under 1. Rounding the up to 4 captures that last partial step's landing point ; rounding down to 3 would wrongly drop it. So counts "every step that starts below stop."
Why is np.zeros(10**6) far cheaper than [0]*10**6?
The list stores a million pointers to full Python int objects; the array is one contiguous typed buffer of raw floats. Less memory, no per-element object overhead, and ready for vectorized math.
Why does linspace include the endpoint but arange excludes it?
linspace answers "sample this closed interval " — you want both ends for plotting. arange mimics Python range, whose half-open convention makes lengths add cleanly and avoids double-counting when chaining ranges.
Why prefer linspace over arange when you know how many points you need?
arange derives the count from a float step, and float rounding makes that count unreliable. linspace is given the exact count, so it is guaranteed correct regardless of floating-point wobble.
Why does np.empty exist if it returns garbage?
It skips the zero-filling pass, so it is the fastest allocator. It is safe only when you immediately overwrite every element — a real speedup in tight preallocate-then-fill code.
Why pass a seed to a random generator at all?
For reproducibility: a teammate or your future self running the same code gets identical "random" numbers, so bugs and results can be replayed exactly.

Edge cases

What does np.linspace(3, 3, 4) return?
[3., 3., 3., 3.]. Start equals stop, so the gap is 0/(4-1)=0; all four points collapse onto 3. No error — just a degenerate (constant) sample.
What does np.linspace(0, 1, 0) return?
A valid empty float array of shape (0,). Asking for zero points is legal — NumPy just returns nothing at all, which lets generic code that computes num on the fly never crash on the num = 0 case.
What does np.linspace(0, 1, 1) return?
A single-element array [0.]. With one point there are zero gaps, so num-1 = 0; NumPy avoids dividing by zero and just returns the start.
What does np.arange(5, 0, 1) (positive step, decreasing range) give?
An empty array. With a positive step you can never advance from 5 down to below 0, so zero elements fit before the exclusive stop.
What does np.arange(5, 0, -1) (negative step, decreasing range) give?
[5, 4, 3, 2, 1] — the natural counterpart. A negative step counts down, stopping before the exclusive stop = 0, so 0 itself is dropped. Sign of the step must match the direction from start to stop, or you get the empty array above.
What does np.linspace(0, 1, 5, endpoint=False) change?
Now the stop is excluded, so you divide by num not num-1: step 1/5 = 0.2, giving [0, 0.2, 0.4, 0.6, 0.8]. Useful for periodic sampling where the last point would duplicate the first.
What is the length of np.arange(0)?
Zero — an empty array. arange(0) means "0, 1, … up to but excluding 0", and nothing satisfies that.
What is the expected mean of many rng.random() samples, and why?
About 0.5. Uniform draws on are symmetric about the midpoint, so the long-run average sits at ; rng.normal(loc, scale, ...) averages to loc instead.
What does np.zeros(0) return?
A valid empty float array of shape (0,). A length-zero container is legal and often the base case of loops that fill it — not an error.

Connections

  • 5.4.02 Array creation — np.zeros, np.ones, np.linspace, np.arange, np.random (Hinglish)
  • NumPy arrays — shape, dtype, ndim
  • Vectorization vs Python loops
  • Array indexing and slicing
  • Floating-point representation and rounding errors
  • Random sampling and distributions
  • Plotting functions with Matplotlib