Vectorizing Python Functions
np.vectorize wraps an ordinary Python function so it accepts whole arrays and broadcasts its arguments, giving you clean element-wise syntax — though it is a convenience for readability, not a real speed-up.
Learn Vectorizing Python Functions in our free NumPy course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Numpy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
You'll wrap functions with np.vectorize and np.frompyfunc, apply functions along an axis with np.apply_along_axis, and see why true vectorization with ufuncs and broadcasting is dramatically faster.
What You'll Learn in This Lesson
1 np.vectorize: Clean Syntax for Element-wise Logic
Suppose you have a plain Python function with some branching that does not map neatly onto array operations. np.vectorize(func) returns a new callable that accepts arrays, applies func to each element, and broadcasts multiple arguments. The code reads cleanly, with no explicit loop in sight.
2 frompyfunc and apply_along_axis
np.frompyfunc(func, nin, nout) is a lower-level cousin that turns a function of nin inputs and nout outputs into an element-wise ufunc — but it returns object arrays, so you often cast the result. Separately, np.apply_along_axis(func, axis, arr) runs a function over whole 1D slices (rows or columns) of a 2D array.
3 True Vectorization Wins
Whenever a calculation can be written with NumPy's built-in ufuncs and array operations, prefer that. Real vectorization runs in fast compiled C over the whole array at once, while np.vectorize loops in Python. The same logic often rewrites cleanly using np.where for branching or plain arithmetic.
🎯 YOUR TURN: Fill in the Blank
Wrap the function so it can take an array of inputs by filling in the wrapper name.
Answer: vectorize (so np.vectorize(grade) ). Remember it is for convenience; here np.where(arr >= 50, "pass", "fail") would be the faster equivalent.
! Common Errors (And How to Fix Them)
❌ Expecting np.vectorize to speed up a big array
It loops in Python, so a million-element array is still slow:
❌ Doing math on a frompyfunc result without casting
np.frompyfunc returns an object array, which can behave unexpectedly in later math.
✅ Fix: cast it, e.g. result.astype(int) or result.astype(float) , before numeric operations.
Using axis=0 when you meant rows applies the function down columns instead.
✅ Fix: axis=1 runs the function on each row, axis=0 on each column — pick deliberately.
🎯 Mini Challenge: Categorize and Then Optimize
Categorize temperatures with np.vectorize, then rewrite the same logic the fast way with np.select.
❓ Frequently Asked Questions
Lesson complete — convenience and speed in balance!
You can wrap functions with np.vectorize and np.frompyfunc , run logic along an axis with np.apply_along_axis , and you understand why true ufunc vectorization is the fast path.
🚀 Up next: Einstein Summation — express dot products, matrix multiplies, and more with np.einsum .
Practice quiz
Does np.vectorize make your code faster?
- Yes, it runs in compiled C
- Yes, it parallelizes the work
- Only on large arrays
- No, it still calls the function once per element in Python
Answer: No, it still calls the function once per element in Python. np.vectorize is a convenience wrapper at roughly Python loop speed.
What is the main value of np.vectorize?
- Cleaner syntax and automatic broadcasting
- Maximum performance
- Saving memory
- Sorting the input
Answer: Cleaner syntax and automatic broadcasting. It offers tidy element-wise syntax and broadcasting, not speed.
What does np.frompyfunc always return?
- A float array
- An integer array
- An object-dtype array
- A boolean array
Answer: An object-dtype array. frompyfunc returns arrays of Python objects (dtype=object).
What does np.apply_along_axis(func, 1, m) do?
- Applies func to every single element
- Applies func to each row (1D slice along axis 1)
- Transposes m
- Flattens m
Answer: Applies func to each row (1D slice along axis 1). It runs a function over 1D slices along the chosen axis.
Which is the faster, truly vectorized way to replace a vectorize branch?
- A Python for loop
- np.frompyfunc
- math.sqrt
- np.where(cond, a, b)
Answer: np.where(cond, a, b). np.where runs as one compiled operation, much faster than vectorize.
After r = np.frompyfunc(...)(arr), why cast r before math?
- Because r is an object array that behaves unexpectedly in math
- Because r is sorted
- Because r is read-only
- Because r is a list
Answer: Because r is an object array that behaves unexpectedly in math. Cast with r.astype(int) or astype(float) before numeric operations.
When is np.vectorize an acceptable choice?
- For million-element arrays needing speed
- Whenever you add two arrays
- For small arrays with logic that cannot be vectorized
- Never, it is deprecated
Answer: For small arrays with logic that cannot be vectorized. Use it when logic cannot be array-expressed and data is small.
What does np.vectorize give you for multiple arguments?
- Automatic broadcasting of the arguments
- Guaranteed C speed
- In-place modification
- Sorted output
Answer: Automatic broadcasting of the arguments. It broadcasts multiple arguments, like a scalar against an array.
Which wrapper turns a Python function into an element-wise callable that infers a dtype?
- np.apply_along_axis
- np.vectorize
- np.bincount
- np.tile
Answer: np.vectorize. np.vectorize infers an output dtype and supports keyword options.
What does np.frompyfunc(func, nin, nout) take as nin and nout?
- The array shape
- The axis numbers
- The number of inputs and outputs
- The dtype names
Answer: The number of inputs and outputs. nin is the number of inputs, nout the number of outputs.
Continue this course
- Previous: Polynomials
- Next: Einstein Summation