🛠️ NumPy ufunc Create Function – Define Your Own Vectorized ufuncs
🧲 Introduction – Why Create Custom ufuncs in NumPy?
While NumPy provides many built-in universal functions (ufuncs) like add
, subtract
, and sqrt
, sometimes you need custom behavior—such as a domain-specific formula or a multi-step transformation.
Using np.frompyfunc()
or np.vectorize()
, you can create your own ufuncs that behave just like NumPy’s built-ins: they work on entire arrays, support broadcasting, and improve code readability.
🎯 By the end of this guide, you’ll:
- Understand how to build custom NumPy ufuncs
- Use
frompyfunc()
andvectorize()
for vectorized function creation - Handle multi-input and multi-output functions
- Know when to use (and not use) custom ufuncs
🔬 Step 1: Create ufunc Using np.frompyfunc()
import numpy as np
def custom_add(x, y):
return x + y
add_ufunc = np.frompyfunc(custom_add, 2, 1)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(add_ufunc(a, b))
🔍 Explanation:
2
→ number of input arguments1
→ number of outputs- Works element-wise on
a
andb
✅ Output:
[5 7 9]
📏 Step 2: Create ufunc with np.vectorize()
def custom_power(x):
return x ** 3 + 2
vec_power = np.vectorize(custom_power)
arr = np.array([1, 2, 3])
print(vec_power(arr))
🔍 Explanation:
- Converts regular function to act element-wise on NumPy arrays
- Automatically infers number of inputs/outputs
✅ Output:
[ 3 10 29]
🔀 Step 3: Multi-Input Custom ufunc Example
def weighted_sum(x, y, w):
return x * w + y * (1 - w)
weighted = np.vectorize(weighted_sum)
x = np.array([10, 20, 30])
y = np.array([1, 2, 3])
w = 0.75
print(weighted(x, y, w))
✅ Output:
[7.75 15.5 23.25]
🧮 Step 4: Multi-Output Custom ufuncs
def div_mod(a, b):
return divmod(a, b)
multi_output = np.vectorize(div_mod)
a = np.array([10, 20, 30])
b = np.array([3, 7, 5])
quotients, remainders = multi_output(a, b)
print("Quotients:", quotients)
print("Remainders:", remainders)
✅ Output:
Quotients: [3 2 6]
Remainders: [1 6 0]
⚠️ Performance Note
Method | Pros | Cons |
---|---|---|
np.frompyfunc() | True ufunc, returns object | Slower, lacks type info |
np.vectorize() | Simple, returns native dtype | Still uses Python loops under the hood |
📌 Important: These methods provide convenience, not performance gains over vectorized NumPy functions. Use NumPy built-ins or numba
for speed-critical tasks.
🧠 When Should You Create Custom ufuncs?
✅ Use when:
- No built-in NumPy function fits your logic
- You want to apply element-wise operations cleanly
- You need broadcasting behavior on custom logic
❌ Avoid when:
- You need high-performance processing
- You can refactor your logic using existing NumPy functions
📌 Summary – Recap & Next Steps
Creating ufuncs in NumPy allows you to extend vectorization to your own functions, keeping code clean, readable, and broadcast-compatible. While not a performance boost, they offer tremendous convenience for element-wise logic.
🔍 Key Takeaways:
- Use
frompyfunc(func, nin, nout)
for explicit ufuncs - Use
vectorize(func)
for quick vectorization with native types - Supports multi-input and multi-output operations
- Best for readability and simplicity, not speed
⚙️ Real-world relevance: Used in data preprocessing, simulation modeling, and custom math transformations in NumPy pipelines.
❓ FAQs – NumPy Custom ufuncs
❓ What’s the difference between frompyfunc()
and vectorize()
?
✅ frompyfunc()
returns object arrays; vectorize()
preserves native data types.
❓ Are custom ufuncs faster than Python loops?
❌ No. They use Python under the hood. For speed, use NumPy built-ins or numba
.
❓ Can I return multiple outputs from a ufunc?
✅ Yes, especially with np.vectorize()
, which unpacks into multiple arrays.
❓ Do custom ufuncs support broadcasting?
✅ Yes, they follow NumPy’s broadcasting rules.
❓ Is vectorize()
the same as JIT compilation?
❌ No. vectorize()
improves syntax, not performance. Use numba
for JIT.
Share Now :