Most efficient way to map function over numpy array
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The most efficient way to apply a function over a NumPy array is vectorization — using NumPy's built-in operations that execute in compiled C code. For element-wise math, use NumPy ufuncs directly (np.sqrt(arr), arr * 2). For custom functions, try to rewrite them using NumPy operations. As a last resort, use np.vectorize() or np.frompyfunc(), which are convenience wrappers around Python loops and offer no real speedup. Avoid Python for loops over NumPy arrays — they are 10-100x slower than vectorized operations.
Vectorized Operations (Fastest)
NumPy ufuncs are implemented in C and operate on entire arrays without Python loop overhead. Always prefer these when possible.
np.where for Conditional Logic
np.where is the vectorized equivalent of if-else and runs at C speed.
Rewriting Custom Functions as Vectorized
np.vectorize (Convenience, Not Speed)
np.vectorize does NOT make the function run faster — it is essentially a for loop with broadcasting support. It is only a convenience for making scalar functions accept arrays.
Performance Comparison
Vectorized NumPy is 100-200x faster than Python loops for large arrays.
Using np.frompyfunc
np.frompyfunc creates a ufunc that returns an object array. It is marginally faster than np.vectorize but still a Python-level loop.
Pandas apply vs NumPy
If your data is in a pandas DataFrame, still prefer NumPy operations on the underlying .values array for best performance.
Lookup Table for Discrete Mapping
Common Pitfalls
- Using
np.vectorizeexpecting C-speed:np.vectorizeis a Python loop with a nice API. It does not compile or optimize the function. For real speedups, rewrite using NumPy operations or use Numba (@numba.vectorize). - Python
forloop over large arrays: Iterating over a million-element array in Python takes hundreds of milliseconds. The same operation vectorized takes single-digit milliseconds. Always try vectorization first. - Applying pandas
applyunnecessarily:df['col'].apply(lambda x: x * 2)is much slower thandf['col'] * 2. Use vectorized pandas/NumPy operations before reaching forapply. - Creating intermediate arrays:
np.where(arr > 0, arr, 0)creates a boolean mask array internally. For very large arrays, this doubles memory usage. Use in-place operations (np.clip(arr, 0, None, out=arr)) when memory is constrained. - Ignoring Numba for complex custom functions: When a function cannot be expressed in NumPy operations,
@numba.jitcompiles Python to machine code and achieves near-C performance:@numba.vectorize def f(x): return x**2 + 1.
Summary
- Use NumPy built-in operations (
np.sqrt,np.where, arithmetic) for maximum speed - Rewrite custom functions using NumPy vectorized operations instead of if/else
np.vectorizeis for convenience, not performance — it wraps a Python loop- Avoid Python
forloops andlist(map())on NumPy arrays — they are 100x slower - Use lookup table indexing (
lookup[arr]) for mapping discrete integer values - For functions that cannot be vectorized, use Numba
@jitfor compiled performance

