Module 3 · Lesson 3
Vectorization vs. Explicit Loops
“Vectorizing” means replacing an element-by-element Python loop with a single array-level operation that NumPy executes in compiled code. The result is not just shorter code — it’s dramatically faster for large arrays, because Python’s per-element overhead disappears entirely.
def clip_and_square_loop(values, lo, hi):
out = []
for v in values:
v = max(lo, min(hi, v)) # clip
out.append(v * v) # square
return out
def clip_and_square_vectorized(values, lo, hi):
clipped = np.clip(values, lo, hi)
return clipped ** 2
data = list(range(-5, 6))
print(clip_and_square_loop(data, -2, 2))
print(clip_and_square_vectorized(np.array(data), -2, 2))
Output:
[4, 4, 4, 4, 1, 0, 1, 4, 4, 4, 4]
[4 4 4 4 1 0 1 4 4 4 4]
big = list(range(1_000_000))
big_arr = np.array(big)
start = time.perf_counter()
clip_and_square_loop(big, -500_000, 500_000)
print(f"loop: {time.perf_counter() - start:.4f}s")
start = time.perf_counter()
clip_and_square_vectorized(big_arr, -500_000, 500_000)
print(f"vectorized: {time.perf_counter() - start:.4f}s")
Output:
loop: 0.0936s
vectorized: 0.0046s
De-loop a threshold function
Here is a loop-based function:
def flag_outliers_loop(values, threshold):
flags = []
for v in values:
flags.append(abs(v) > threshold)
return flags
Write flag_outliers_vectorized(values, threshold) that takes a NumPy array
and a scalar threshold, and returns a boolean NumPy array with the same
result — no loop, no comprehension.
💡 Hint
np.abs(values) > threshold is already a full boolean array — that's the whole function.
✅ Solution
def flag_outliers_vectorized(values, threshold):
return np.abs(values) > threshold
One more vectorization-friendly function worth knowing: np.cumsum(arr)
returns the running (cumulative) sum — replacing exactly the kind of loop
that keeps adding to a running total as it walks through a sequence.
print(np.cumsum(np.array([1, 2, 3, 4]))) # [1, 3, 6, 10]
Output:
[ 1 3 6 10]
Vectorized running total check
Write matches_cumulative_sum(values, running_totals) that checks, without
any explicit Python loop, whether running_totals equals the cumulative
sum of values (i.e. running_totals[i] == sum(values[:i+1]) for every
i). Return a single bool.
💡 Hint
np.cumsum(values) computes the running total in one call; compare it to running_totals with np.array_equal.
✅ Solution
def matches_cumulative_sum(values, running_totals):
return bool(np.array_equal(np.cumsum(values), running_totals))