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.

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.

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.

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.

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.

❌ 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.

Categorize temperatures with np.vectorize, then rewrite the same logic the fast way with np.select.

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.