NumPy Universal Functions (ufuncs)
Use NumPy's built-in mathematical functions, aggregations, and statistical operations to process arrays without Python loops.
Real-World Scenario
A data scientist processing weather sensor data needs to compute hourly averages across 500 stations, find stations exceeding temperature thresholds, apply exponential smoothing, and compute correlation matrices — all operations that would take thousands of lines and seconds in pure Python, but are single ufunc calls in NumPy.
Mathematical Ufuncs
import numpy as np
arr = np.array([1., 4., 9., 16., 25.])
# Basic math — all element-wise, no loops
print(np.sqrt(arr)) # [1. 2. 3. 4. 5.]
print(np.square(arr)) # [ 1. 16. 81. 256. 625.]
print(np.abs(np.array([-3., 1., -4., 1., -5.]))) # [3. 1. 4. 1. 5.]
# Exponential and logarithm
x = np.array([0., 1., 2., 3.])
print(np.exp(x)) # [1. 2.718 7.389 20.09]
print(np.log(np.exp(x)))# [0. 1. 2. 3.] — natural log
print(np.log2(np.array([1., 2., 4., 8.]))) # [0. 1. 2. 3.]
print(np.log10(np.array([1., 10., 100.]))) # [0. 1. 2.]
# Trigonometry
angles = np.linspace(0, 2 * np.pi, 5)
print(np.sin(angles).round(10)) # [0. 1. 0. -1. 0.] at 0, π/2, π, 3π/2, 2π
print(np.cos(angles).round(10))
# Clip — cap values to a range
noisy = np.array([-2., 0.5, 1.5, 3., -0.3])
clipped = np.clip(noisy, 0.0, 1.0)
print(clipped) # [0. 0.5 1. 1. 0. ]
Aggregation Functions
import numpy as np
rng = np.random.default_rng(42)
X = rng.standard_normal((6, 4)) # 6 rows, 4 columns
# Global aggregations — collapse entire array to a scalar
print(X.sum())
print(X.mean())
print(X.std())
print(X.min())
print(X.max())
print(X.var())
# Axis-wise aggregations
print(X.sum(axis=0)) # shape (4,) — sum of each column
print(X.sum(axis=1)) # shape (6,) — sum of each row
print(X.mean(axis=0)) # column means
print(X.mean(axis=1)) # row means
# keepdims=True — preserve shape for broadcasting
col_means = X.mean(axis=0, keepdims=True) # shape (1, 4)
col_stds = X.std(axis=0, keepdims=True) # shape (1, 4)
X_norm = (X - col_means) / col_stds # (6,4) - (1,4) broadcasts correctly
# Cumulative operations
arr = np.array([1, 2, 3, 4, 5])
print(np.cumsum(arr)) # [ 1 3 6 10 15] — running total
print(np.cumprod(arr)) # [ 1 2 6 24 120] — running product
# Argmin / Argmax — index of minimum/maximum value
scores = np.array([88, 45, 72, 91, 67])
print(np.argmax(scores)) # 3 — index of 91
print(np.argmin(scores)) # 1 — index of 45
# 2-D argmax — index per row or column
matrix = rng.integers(0, 100, (4, 5))
print(np.argmax(matrix, axis=1)) # column index of max in each row
Statistical Functions
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=170, scale=10, size=1000) # heights in cm
print(f"Mean: {data.mean():.2f} cm")
print(f"Median: {np.median(data):.2f} cm")
print(f"Std: {data.std():.2f} cm")
print(f"Variance: {data.var():.2f}")
# Percentiles
p25, p50, p75 = np.percentile(data, [25, 50, 75])
iqr = p75 - p25
print(f"IQR: {iqr:.2f} cm")
# Correlation and covariance
x = rng.standard_normal(100)
y = x * 0.8 + rng.standard_normal(100) * 0.5 # correlated with x
# np.corrcoef returns a correlation matrix
corr_matrix = np.corrcoef(x, y)
print(f"Correlation: {corr_matrix[0, 1]:.3f}") # ~0.85
# Covariance matrix — (n_features, n_features)
features = rng.standard_normal((200, 3))
cov_matrix = np.cov(features.T) # pass features.T: each row = one feature
print(cov_matrix.shape) # (3, 3)
Sorting
import numpy as np
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6, 5, 3])
# np.sort — returns sorted copy
print(np.sort(arr)) # [1 1 2 3 3 4 5 5 6 9]
print(np.sort(arr)[::-1]) # descending
# arr.sort() — sorts in-place
arr_copy = arr.copy()
arr_copy.sort()
print(arr_copy)
# argsort — returns indices that would sort the array (extremely useful)
idx = np.argsort(arr)
print(idx) # [1 3 6 0 9 2 8 4 7 5] — positions in sorted order
# Top-k: get the 3 largest values
top3_idx = np.argsort(arr)[-3:] # last 3 indices of sorted order
print(arr[top3_idx]) # [6 5 9] — wait, let's check
print(arr[np.argsort(arr)[-3:]]) # 3 largest values (not necessarily sorted)
# 2-D sort — sort along an axis
matrix = np.array([[3, 1, 2], [6, 4, 5]])
print(np.sort(matrix, axis=1)) # sort each row
# [[1 2 3]
# [4 5 6]]
Boolean and Set Operations
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 3, 2, 1])
# Unique values — like Python's set(), returns sorted array
unique = np.unique(arr)
print(unique) # [1 2 3 4 5]
# Unique with counts
values, counts = np.unique(arr, return_counts=True)
for v, c in zip(values, counts):
print(f"{v}: {c} times")
# Set operations
a = np.array([1, 2, 3, 4, 5])
b = np.array([3, 4, 5, 6, 7])
print(np.intersect1d(a, b)) # [3 4 5]
print(np.union1d(a, b)) # [1 2 3 4 5 6 7]
print(np.setdiff1d(a, b)) # [1 2] — in a but not b
print(np.in1d(a, b)) # [F F T T T] — membership test
# Any / All
print(np.any(a > 4)) # True — at least one element > 4
print(np.all(a > 0)) # True — all elements > 0
print(np.all(a > 3)) # False — not all > 3 Frequently Asked Questions
What is a ufunc?
A ufunc (universal function) is a compiled C function that operates element-wise on NumPy arrays. All standard math operations (+, -, *, /) and functions like np.sqrt, np.exp, np.sin are ufuncs. They support broadcasting, type casting, and the out= parameter for in-place output.
What does axis= mean in aggregation functions?
axis specifies which dimension to collapse. axis=0 reduces across rows (collapses the first dimension), producing one result per column. axis=1 reduces across columns, producing one result per row. Omitting axis reduces the entire array to a scalar.