NumPy ufunc Create Function

NumPy lets you convert an ordinary Python function into a ufunc with np.frompyfunc(), so your own logic can run element-wise across arrays the same way built-in functions like np.add do.

Why Create a Custom ufunc?

Sometimes the operation you need doesn't exist as a built-in ufunc -- maybe it's business logic specific to your project, like a custom scoring formula or a piecewise rule. NumPy's np.frompyfunc() wraps any Python callable so it can be applied element-wise across arrays, giving it the same broadcasting-friendly calling convention as native ufuncs.

Using np.frompyfunc()

The signature is numpy.frompyfunc(function, nin, nout), where function is the Python callable, nin is the number of input arguments it takes, and nout is the number of values it returns. The result is a new ufunc-like object you can call just like np.add or np.multiply.

Example

import numpy as np

def custom_add(x, y):
    return x + y

my_add_ufunc = np.frompyfunc(custom_add, 2, 1)

print(my_add_ufunc([1, 2, 3, 4], [10, 20, 30, 40]))
print(type(my_add_ufunc))
Note: np.frompyfunc() is perfect for prototyping custom element-wise logic, but because it still calls your Python function once per element, it will not match the raw speed of a true compiled ufunc like np.add.

Confirming It Behaves Like a ufunc

Checking type() on the object created by np.frompyfunc() confirms it reports itself as numpy.ufunc, meaning it participates in the same dispatch machinery as built-in functions -- including being usable inside expressions that expect a ufunc.

Example

import numpy as np

def celsius_to_label(temp):
    if temp < 0:
        return 'freezing'
    elif temp < 20:
        return 'cool'
    else:
        return 'warm'

classify = np.frompyfunc(celsius_to_label, 1, 1)

temps = np.array([-5, 10, 25, 0, 18])
labels = classify(temps)
print(labels)
print(type(classify))
  • The wrapped function always returns Python objects, so the output array has dtype=object rather than a numeric dtype.
  • nin is the number of inputs your function accepts; nout is the number of outputs it returns.
  • It supports broadcasting rules just like built-in ufuncs.
  • For pure numeric element-wise math, np.vectorize() or writing the logic with existing ufuncs is often faster.
  • It's a convenient bridge when you need custom branching logic (if/elif/else) applied across an array.
FunctionPurpose
np.frompyfunc(func, nin, nout)Wrap func as a ufunc-like object
np.vectorize(func)Similar convenience wrapper with more type-inference options
type(obj)Confirm the result reports as numpy.ufunc

Example

import numpy as np

def divmod_custom(x, y):
    return x // y, x % y

custom_divmod = np.frompyfunc(divmod_custom, 2, 2)

quotients, remainders = custom_divmod([10, 21, 33], [3, 4, 5])
print(quotients)
print(remainders)

Exercise: NumPy ufunc Create Function

Which NumPy function turns an ordinary Python function into a ufunc?