Python NumPy ufunc: How to Create Your Own Universal Function

Last updated 1 month, 3 weeks ago | 124 views 75     5

Tags:- Python NumPy

NumPy’s ufuncs (short for universal functions) are high-performance functions that operate on ndarray objects in an element-wise fashion. While NumPy includes many built-in ufuncs, it also allows you to create your own, enabling custom logic with the performance benefits of vectorized operations.

This article covers:

  • ✅ What ufuncs are and why they're useful

  • How to create custom ufuncs using np.frompyfunc()

  • Difference between frompyfunc() and vectorize()

  • Examples of custom ufuncs

  • Tips and Common pitfalls


What is a ufunc?

A ufunc (universal function) in NumPy is designed to operate on arrays element by element, supporting:

  • Broadcasting

  • Type casting

  • Multiple input/output arguments

  • High-speed computation (C-backed functions)

When you want to apply custom logic to NumPy arrays while preserving vectorized performance, you can create your own ufunc.


Creating a ufunc with np.frompyfunc()

NumPy provides a method called frompyfunc() to convert a regular Python function into a ufunc.

Syntax:

numpy.frompyfunc(func, nin, nout)
  • func: The Python function you want to convert

  • nin: Number of input arguments

  • nout: Number of output arguments

✅ Example: Create a custom add ufunc

import numpy as np

# Step 1: Define a regular Python function
def my_add(x, y):
    return x + y

# Step 2: Convert it into a ufunc
my_add_ufunc = np.frompyfunc(my_add, 2, 1)

# Step 3: Use it with NumPy arrays
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])

result = my_add_ufunc(a, b)
print(result)

Output:

[11 22 33]

⚠️ Note: frompyfunc() returns an array of dtype=object, not a numeric dtype.


frompyfunc() vs vectorize()

Feature frompyfunc() vectorize()
Return type Always object array Tries to infer and preserve dtype
Performance Slightly faster for pure Python logic Similar; both are slower than built-ins
Use case Universal functions (broadcastable) General Python vectorization
Output control Supports multiple outputs Only one output supported

vectorize() Example:

def square(x):
    return x ** 2

square_vec = np.vectorize(square)

arr = np.array([1, 2, 3, 4])
print(square_vec(arr))

Output:

[ 1  4  9 16]

More Custom ufunc Examples

Example 1: Custom multiply-add function

def multiply_add(x, y):
    return x * y + 5

mul_add_ufunc = np.frompyfunc(multiply_add, 2, 1)

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(mul_add_ufunc(a, b))

Output:

[9 15 23]

Example 2: String manipulation ufunc

def greet(name):
    return f"Hello, {name}!"

greet_ufunc = np.frompyfunc(greet, 1, 1)

names = np.array(["Alice", "Bob", "Charlie"])
print(greet_ufunc(names))

Output:

['Hello, Alice!' 'Hello, Bob!' 'Hello, Charlie!']

Full Working Example: Create and Use a Custom Ufunc

import numpy as np

# Custom function to convert Celsius to Fahrenheit
def celsius_to_fahrenheit(c):
    return (c * 9/5) + 32

# Create ufunc with 1 input and 1 output
to_fahrenheit = np.frompyfunc(celsius_to_fahrenheit, 1, 1)

# Apply to array
temps_celsius = np.array([0, 20, 37, 100])
temps_fahrenheit = to_fahrenheit(temps_celsius)

print("Fahrenheit:", temps_fahrenheit)

Output:

Fahrenheit: [32.0 68.0 98.6 212.0]

Tips for Creating ufuncs

Tip Explanation
Always match nin and nout Incorrect input/output count causes errors
Wrap logic-heavy code in Python function Then convert using frompyfunc()
Use np.vectorize() for dtype preservation It handles output types better than frompyfunc()
Be cautious with object arrays Use astype() if you need numeric output

Common Pitfalls

Pitfall How to Avoid
Output has dtype=object Use .astype(int) or use vectorize() instead
Wrong number of arguments in frompyfunc() Match exactly the number of input and output arguments
Using ufuncs for performance-critical code Prefer built-in NumPy ufuncs for speed

Conclusion

Creating custom ufuncs with np.frompyfunc() gives you the flexibility of Python functions combined with the powerful broadcasting and vectorization of NumPy.

You now know how to:

  • Write your own Python function

  • Convert it to a universal function

  • Use it with arrays, just like built-in NumPy functions


What’s Next?