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