Skip to main content
NumPy beginner Lesson 6 of 12

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.