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
3.1

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]
3.2

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))