NumPy
Intermediate 13 min read NumPy

Indexing and Vectorization

Select and transform whole arrays at once

Fancy indexing, boolean masks and np.where replace row-scavenging Python code with array-native logic — the difference between a 40-line loop and a 4-line pipeline.

fancy indexingboolean masknp.wherenp.selectvectorize
Prerequisites: NumPy Arrays
Before we start

What you will learn

  • Index with arrays, lists and boolean masks
  • Apply conditional selection with np.where/np.select
  • Replace Python loops with vectorized expressions
Introduction

What is this?

Fancy indexing selects rows by an array of positions or booleans: mat[mask] keeps the True rows, mat[idx] gathers listed indices. np.where(cond, a, b) picks element-wise between two arrays, and np.select stacks multi-way rules. Together they encode 'if/else at scale' without a single Python loop.

Motivation

Why do you need it?

  • Conditions on 10M rows are one expression, not a loop-through-Python.
  • Boolean logic composes into readable rules: (arr > t) & (arr < u).
  • Vectorized where/select make rule engines concise and auditable.
  • It is the syntax behind every pandas boolean mask.
Think about it

Mental model

Imagine

A security guard with a checklist scanning a crowd

Think of

Boolean masking

The guard marks each person in or out by the checklist — one pass, no per-person report. The mask is the checklist applied to every queue member simultaneously; np.where then routes each person down the 'admits' or 'denies' corridor.

Mechanics

How it works

  • mat[idx] with an integer array gathers rows in that order.
  • mat[mask] where mask is a same-length boolean array keeps True elements.
  • np.where(cond, then_array, else_array) chooses element-wise; scalar inputs broadcast.
  • np.select([c1, c2], [v1, v2], default=...) handles multiple ordered rules.
  • Boolean ops &, |, ~ compose; comparisons return boolean masks as whole arrays.
  • Where pandas would loop a column, numpy's np.where/np.select usually wins on speed.

Mask → select → assign.

arr
cond
mask
where/select
Assigned
Hands-on

Examples

Rule engine in three lines

Rule engine in three lines
import numpy as np
usage = np.array([5, 95, 12, 300, 40])
seg = np.select(
[usage >= 90, usage < 15],
["upsell", "at-risk"],
default="standard",
)
print(seg) # standard handled by default
print(usage[usage >= 90]) # fancy/mask selection

np.select applies ordered rules without a loop; a boolean mask gathers the high-usage values directly.

Real world

In production

  • A support triage vectorizes SLA breach flags with np.where on response-time arrays.
  • A pricing engine applies tiered discounts with np.select across the whole price vector.
  • A monitoring system masks anomaly windows via boolean selections feeding alarms in bulk.
Guidelines

Best practices

  • Compose masks with & | ~, not Python's and/or.
  • Put the most specific rule first in np.select — order decides winners.
  • Operate on arrays; convert lists to arrays once at the boundary.
  • For pandas columns, keep expressions in numpy space and assign results back once.
Watch out

Common mistakes

  • Using Python and/or on arrays — ambiguity errors.
  • forgetting parens when combining masks with comparisons.
  • Applying np.where(cond, a, b) with different-shaped a/b and being surprised by broadcasting.
  • Treating np.where like a branch that short-circuits — both operands are evaluated first.
When things break

Debugging tips

  • A mask that selects nothing → print the mask.sum() and the raw comparison values.
  • np.select with overlapping rules: verify the first-wins order with boundary samples.
  • Unexpected dtype after where (e.g., all strings because one branch is) — mention dtype=.
  • When a loop version and vectorized version disagree, shrink to 10 rows and diff stepwise.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

arr[arr > 5] returns:

2

np.select evaluates all rules but returns on the first match.

Go deeper

Official documentation