NumPy: Element-wise Operations
Last updated: 2026-08-26
1. What You'll Learn
- ❶ Arithmetic —
+ - * / // % **and their corresponding functions - ❷ Comparison —
> < >= <= == !=generating boolean masks - ❸ Logical operations —
& | ^ ~andnp.logical_andetc. - ❹ Vectorization vs loops — performance comparison and principles
- ❺ ufunc introduction — the concept and properties of universal functions
2. Story
Bob uses a for loop to square 1 million numbers — it takes 0.3 seconds. Alice writes one line a ** 2 — 3 milliseconds.
"NumPy arithmetic isn't a Python loop. It's a C-level ufunc batch operation, 100x faster."
Bob asks: "What's a ufunc?" Alice opens her terminal and types np.add — it all starts with element-wise operations.
3. Key Concepts
(1) Arithmetic Operators and Functions
NumPy arithmetic operates on every element of the array, automatically broadcasting shapes:
import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])
print(a + b) # [11 22 33 44]
print(a * b) # [10 40 90 160]
print(a ** 2) # [ 1 4 9 16]
print(b // a) # [10 10 10 10]
print(b % a) # [0 0 0 0]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
Each operator corresponds to a NumPy function:
| Operator | Function | Description |
|---|---|---|
+ |
np.add |
Addition |
- |
np.subtract |
Subtraction |
* |
np.multiply |
Multiplication |
/ |
np.true_divide |
True division |
// |
np.floor_divide |
Floor division |
% |
np.mod |
Modulo |
** |
np.power |
Power |
(2) Comparison Operations
Comparisons are element-wise, returning boolean arrays (masks):
a = np.array([3, 7, 1, 9, 5])
print(a > 4) # [False True False True True]
print(a == 7) # [False True False False False]
print((a >= 3) & (a <= 7)) # [ True True False False True]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(3) Logical Operations
Use bitwise operators & | ^ ~ (not and or not) for element-wise logical combinations:
a = np.array([True, True, False, False])
b = np.array([True, False, True, False])
print(a & b) # [ True False False False]
print(a | b) # [ True True True False]
print(~a) # [False False True True]
print(np.logical_and(a, b)) # [ True False False False]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(4) ufunc Concept
ufunc (universal function) is NumPy's low-level abstraction for element-wise operations:
- Each ufunc is a C-level loop that applies the same operation to every element
- Supports broadcasting, type promotion, and output buffering
- Has
.types,.nin,.noutproperties
print(np.add.nin) # 2 (number of inputs)
print(np.add.nout) # 1 (number of outputs)
print(np.add.types) # list of supported type signatures
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
▶ Example
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: Arithmetic operations (Difficulty ⭐)
import numpy as np
a = np.array([2, 4, 6, 8])
b = np.array([1, 3, 5, 7])
print("a + b =", a + b) # [ 3 7 11 15]
print("a - b =", a - b) # [1 1 1 1]
print("a * b =", a * b) # [ 2 12 30 56]
print("a / b =", a / b) # [2. 1.33 1.2 1.14]
print("a // b =", a // b) # [2 1 1 1]
print("a % b =", a % b) # [0 1 1 1]
print("a ** 3 =", a ** 3) # [ 8 64 216 512]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
▶ Example
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: Comparison masks (Difficulty ⭐)
import numpy as np
scores = np.array([55, 82, 91, 47, 73, 100])
pass_mask = scores >= 60
print("Pass mask:", pass_mask)
print("Pass scores:", scores[pass_mask])
excellent = scores >= 90
print("Excellent:", scores[excellent])
mid_range = (scores >= 60) & (scores < 90)
print("Mid-range:", scores[mid_range])
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
▶ Example: Using ufunc out parameter (Difficulty ⭐⭐)
import numpy as np
a = np.array([10, 20, 30, 40])
b = np.array([3, 4, 5, 6])
# In-place addition using out parameter
result = np.empty(4)
np.add(a, b, out=result)
print("Result:", result)
# Chain operations with out
np.multiply(result, 2, out=result)
print("Doubled:", result)
# Comparison ufunc
print("a > 15:", np.greater(a, 15))
Output:
TEXT 📖 Display onlyResult: [13 24 35 46] Doubled: [26 48 70 92] a > 15: [False True True True]
❓ FAQ
np.add instead of +?+ calls np.add under the hood. Use the function form when you need extra parameters like dtype, out, or where, or when passing a ufunc as a callback.inf or -inf, and 0/0 produces nan, with an optional warning based on np.seterr. Integer division by zero raises ZeroDivisionError.📖 Summary
- Arithmetic operators
+ - * / // % **are element-wise, each with a correspondingnp.xxxfunction - Comparison operators generate boolean masks for conditional filtering
- Logical operations use
& | ^ ~, notand or not;np.logical_*functions also work - Vectorization is 50-200x faster than Python loops, powered by C-level ufuncs
- ufuncs are NumPy's element-wise engine, supporting broadcasting and type promotion
📝 Exercises
-
Beginner (Difficulty ⭐): Create
x = np.linspace(-5, 5, 100). Compute the sigmoid function1 / (1 + np.exp(-x))using vectorization. Count how many results are > 0.5. -
Intermediate (Difficulty ⭐⭐): Compute
np.sin(x)for 1 million random numbers using both a for loop and vectorization. Compare the time and calculate the speedup factor. -
Advanced (Difficulty ⭐⭐⭐): Implement a vectorized ReLU function
max(0, x)forx = np.linspace(-3, 3, 20). Implement it using both a boolean maskx * (x > 0)andnp.maximum(0, x).