Conditional Logic, Sorting & Set Logic
What you’ll learn
- Replace
if/elseif/elseloops with the vectorizednp.wherenp.where - Sort arrays with
np.sortnp.sort(copy) vsarr.sort()arr.sort()(in place), and along an axis - Recover a sort order with
argsortargsortinstead of the sorted values - Use set-style operations:
np.uniquenp.unique,np.isinnp.isin,intersect1dintersect1d,union1dunion1d,setdiff1dsetdiff1d - Count and test conditions on whole arrays with
anyany,allall, and booleansumsum
Conditional logic without a loop
Suppose you had two arrays and a boolean array telling you, position by position, which one to pick from. In plain Python you’d reach for a list comprehension:
xarr = [1.1, 1.2, 1.3, 1.4, 1.5]
yarr = [2.1, 2.2, 2.3, 2.4, 2.5]
cond = [True, False, True, True, False]
result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)]
print(result)xarr = [1.1, 1.2, 1.3, 1.4, 1.5]
yarr = [2.1, 2.2, 2.3, 2.4, 2.5]
cond = [True, False, True, True, False]
result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)]
print(result)This works, but it’s slow on large arrays (every step runs in interpreted Python) and it doesn’t generalize to multidimensional data. np.wherenp.where is the vectorized version of the ternary expression x if condition else yx if condition else y:
import numpy as np
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = np.where(cond, xarr, yarr)
print(result)import numpy as np
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = np.where(cond, xarr, yarr)
print(result) flowchart LR
A["cond[i]"] --> D{"cond[i] is True?"}
B["xarr[i]"] --> D
C["yarr[i]"] --> D
D -- "Yes" --> E["take xarr[i]"]
D -- "No" --> F["take yarr[i]"]
E --> G["result[i]"]
F --> G
The second and third arguments don’t have to be arrays
A common analytics pattern: replace every value that matches a condition with a constant, and leave the rest untouched. Either side of np.wherenp.where can be a scalar, an array, or a mix:
import numpy as np
rng = np.random.default_rng(seed=1)
arr = rng.standard_normal((4, 4))
# Replace every positive value with 2, keep negatives as-is
capped = np.where(arr > 0, 2, arr)
# Replace positives with 2 and negatives with -2
signed = np.where(arr > 0, 2, -2)import numpy as np
rng = np.random.default_rng(seed=1)
arr = rng.standard_normal((4, 4))
# Replace every positive value with 2, keep negatives as-is
capped = np.where(arr > 0, 2, arr)
# Replace positives with 2 and negatives with -2
signed = np.where(arr > 0, 2, -2)import numpy as np
arr = np.array([-3, 5, -1, 8, 0, -7])
cleaned = np.where(arr > 0, arr, 0)
print(cleaned)import numpy as np
arr = np.array([-3, 5, -1, 8, 0, -7])
cleaned = np.where(arr > 0, arr, 0)
print(cleaned)Sorting arrays
NumPy arrays sort much like Python lists — but the choice between arr.sort()arr.sort() and np.sort(arr)np.sort(arr) matters:
arr.sort()arr.sort()sorts the array in place (mutates it, returnsNoneNone).np.sort(arr)np.sort(arr)returns a sorted copy, leaving the original untouched — like Python’s built-insorted()sorted().
import numpy as np
arr = np.array([5, -10, 7, 1, 0, -3])
sorted_copy = np.sort(arr) # arr is unchanged
print(sorted_copy)
print(arr)
arr.sort() # now arr itself is sorted
print(arr)import numpy as np
arr = np.array([5, -10, 7, 1, 0, -3])
sorted_copy = np.sort(arr) # arr is unchanged
print(sorted_copy)
print(arr)
arr.sort() # now arr itself is sorted
print(arr)Sorting along an axis
For 2D arrays, pass axisaxis to sort every row or every column independently:
import numpy as np
mat = np.array([
[9, 2, 7],
[1, 8, 3],
])
mat_sorted_by_col = np.sort(mat, axis=0) # sort within each column
mat_sorted_by_row = np.sort(mat, axis=1) # sort within each row
print(mat_sorted_by_col)
print(mat_sorted_by_row)import numpy as np
mat = np.array([
[9, 2, 7],
[1, 8, 3],
])
mat_sorted_by_col = np.sort(mat, axis=0) # sort within each column
mat_sorted_by_row = np.sort(mat, axis=1) # sort within each row
print(mat_sorted_by_col)
print(mat_sorted_by_row)Getting the sort order with argsort
Sometimes you don’t want the sorted values — you want the indices that would put the array in order, so you can reorder other arrays the same way:
import numpy as np
scores = np.array([50, 10, 40, 20])
order = np.argsort(scores)
print(order) # indices that sort scores ascending
print(scores[order]) # apply that order to get sorted valuesimport numpy as np
scores = np.array([50, 10, 40, 20])
order = np.argsort(scores)
print(order) # indices that sort scores ascending
print(scores[order]) # apply that order to get sorted valuesSet logic: unique, membership, and set operations
NumPy has a small toolkit of set-style operations for 1D arrays.
np.uniquenp.unique returns the sorted, unique values in an array — like sorted(set(x))sorted(set(x)), but faster and returning an ndarray:
import numpy as np
names = np.array(["Bob", "Will", "Joe", "Bob", "Will", "Joe", "Joe"])
print(np.unique(names))import numpy as np
names = np.array(["Bob", "Will", "Joe", "Bob", "Will", "Joe", "Joe"])
print(np.unique(names))np.isinnp.isin (the modern name for the older np.in1dnp.in1d) tests membership of one array’s values in another, returning a boolean array:
import numpy as np
values = np.array([6, 0, 0, 3, 2, 5, 6])
print(np.isin(values, [2, 3, 6]))import numpy as np
values = np.array([6, 0, 0, 3, 2, 5, 6])
print(np.isin(values, [2, 3, 6]))intersect1dintersect1d, union1dunion1d, setdiff1dsetdiff1d work like Python set operations, but stay in NumPy and return sorted arrays:
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([3, 4, 5, 6, 7])
print(np.intersect1d(a, b)) # common to both
print(np.union1d(a, b)) # everything, no duplicates
print(np.setdiff1d(a, b)) # in a but not in bimport numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([3, 4, 5, 6, 7])
print(np.intersect1d(a, b)) # common to both
print(np.union1d(a, b)) # everything, no duplicates
print(np.setdiff1d(a, b)) # in a but not in bMethods for boolean arrays
Boolean values act as 11 (TrueTrue) and 00 (FalseFalse) inside NumPy’s aggregation methods. That makes .sum().sum() a quick way to count how many elements satisfy a condition:
import numpy as np
arr = np.array([1, -2, 3, -4, 5, 6, -7])
print((arr > 0).sum()) # count of positive valuesimport numpy as np
arr = np.array([1, -2, 3, -4, 5, 6, -7])
print((arr > 0).sum()) # count of positive values.any().any() and .all().all() test a whole array at once — handy for questions like “did any random walk cross this line?” or “are all values positive?“:
import numpy as np
bools = np.array([False, False, True, False])
print(bools.any()) # True — at least one True
print(bools.all()) # False — not every value is Trueimport numpy as np
bools = np.array([False, False, True, False])
print(bools.any()) # True — at least one True
print(bools.all()) # False — not every value is TrueA quick word on running totals
Not every array method collapses data to a single summary. cumsumcumsum and cumprodcumprod keep every intermediate step, and both accept axisaxis on multidimensional arrays:
import numpy as np
steps = np.array([1, -1, 1, 1, -1, 1, 1, -1])
print(steps.cumsum()) # running total after each stepimport numpy as np
steps = np.array([1, -1, 1, 1, -1, 1, 1, -1])
print(steps.cumsum()) # running total after each stepYou’ll use exactly this pattern — np.wherenp.where to turn coin flips into +1+1/-1-1 steps, then cumsumcumsum to turn steps into a position — to build a random walk in the next page.
Next
Continue to: Structured Arrays & Random Walks to combine np.wherenp.where, cumsumcumsum, and boolean reductions into a full simulation.
🧪 Try It Yourself
Exercise 1 – Clip Negatives with np.where
Exercise 2 – Sort a Copy, Not the Original
Exercise 3 – Unique Values with np.unique
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
