Module 6 · Lesson 2
Vectorization Performance Patterns
Three habits keep NumPy code fast:
- Never grow an array in a loop. Appending to a Python list and
converting once at the end is fine; calling
np.append/np.concatenateinside a loop reallocates the whole buffer every iteration. - Preallocate when you must fill incrementally —
np.empty(shape)ornp.zeros(shape)once, then assign into slices. - Reuse buffers with
out=when you don’t need the original array anymore —np.add(a, b, out=a)writes the result intoa’s existing memory instead of allocating a new array.
import time
n = 2_000_000
a = np.arange(n, dtype=np.float64)
b = np.ones(n)
start = time.perf_counter()
c = a + b # allocates a brand-new array
print(f"new allocation: {time.perf_counter() - start:.5f}s")
start = time.perf_counter()
np.add(a, b, out=a) # reuses a's memory, no new allocation
print(f"in-place (out=): {time.perf_counter() - start:.5f}s")
Output:
new allocation: 0.00262s
in-place (out=): 0.00099s
2.1
Increment in place
Write increment_in_place(arr, amount) that adds amount to every
element of arr in place (using the out= parameter so no new array
is allocated) and returns arr itself.
💡 Hint
np.add(arr, amount, out=arr) writes the result back into arr and also returns arr.
✅ Solution
def increment_in_place(arr, amount):
return np.add(arr, amount, out=arr)
2.2
Running totals, no loop
Write running_totals(values) that returns the cumulative sum of a 1-D
array (result[i] = sum of values[0..i]) without writing a Python for
or while loop.
💡 Hint
There's a single built-in NumPy function that does exactly this: np.cumsum.
✅ Solution
def running_totals(values):
return np.cumsum(values)