Python Essentials for Data Analytics
Back to NUMPY FOUNDATIONS

NumPy Vectorization, Boolean Masks & Statistical Aggregations

Master the 2D axis mental model (axis=0 vs axis=1), NaN-safe statistical aggregations, boolean masking, cumulative sums, and percentiles.

4 minutesReading
🎯 Free Guest Mode: You are learning for free. Sign in to save your completion progress and quiz answers.

NumPy Vectorization, Boolean Masks & Statistical Aggregations

NumPy powers modern numerical computing in Python. This guide covers the 2D Axis mental model, NaN-safe aggregations, Boolean masking, and statistical reductions.


1. The 2D Axis Mental Model

One of the most common points of confusion in NumPy and Pandas is the axis parameter in 2D arrays:

import numpy as np

matrix = np.array([
    [10, 20, 30],
    [40, 50, 60]
])  # Shape: (2, 3) -> 2 rows, 3 columns
Axis Setting Direction of Collapse Code Example Result
None (Default) Entire array into a single scalar matrix.sum() 210
axis=0 Down columns (collapses rows; results in 1 value per column) matrix.sum(axis=0) [50, 70, 90] (length 3)
axis=1 Across rows (collapses columns; results in 1 value per row) matrix.sum(axis=1) [60, 150] (length 2)

2. Handling NaN Values with NaN-Safe Aggregations

In NumPy, standard aggregations propagate np.nan values. If a single element is NaN, the result is NaN:

data = np.array([10.0, 20.0, np.nan, 40.0])

print(np.mean(data))     # Output: nan (Standard aggregation fails)
print(np.nanmean(data))  # Output: 23.333 (Safely ignores NaN)
print(np.nansum(data))   # Output: 70.0
print(np.nanstd(data))   # Output: 12.472

3. Boolean Masking & Conditional Filtering

Boolean indexing allows you to filter elements in a NumPy array without writing a loop:

scores = np.array([55, 82, 90, 45, 78, 95, 60])

# 1. Create a boolean mask
passing_mask = scores >= 70
# Output: [False, True, True, False, True, True, False]

# 2. Extract matching elements
passing_scores = scores[passing_mask]
# Output: [82, 90, 78, 95]

# 3. Compound conditions (use & for AND, | for OR, ~ for NOT):
honors = scores[(scores >= 80) & (scores <= 95)]
# Output: [82, 90, 95]

# 4. Conditional replacement with np.where(condition, if_true, if_false)
grade_labels = np.where(scores >= 70, "PASS", "FAIL")
# Output: ['FAIL', 'PASS', 'PASS', 'FAIL', 'PASS', 'PASS', 'FAIL']

4. Statistical Operations: Percentiles & Cumulative Sums

sales = np.array([100, 150, 200, 250, 300])

# Cumulative Revenue over time:
cum_sales = np.cumsum(sales)
# Output: [100, 250, 450, 700, 1000]

# Percentiles / Quartiles:
q25, q50, q75 = np.percentile(sales, [25, 50, 75])
print(f"25th: {q25}, Median: {q50}, 75th: {q75}")

# Finding index positions of min and max:
arr = np.array([10, 45, 3, 99, 24])
max_idx = np.argmax(arr)  # 3 (points to 99)
min_idx = np.argmin(arr)  # 2 (points to 3)
Loading progress...