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))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.
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?