Module 3 · Lesson 4

Comparison & Logical Ufuncs

Comparison operators (==, !=, <, <=, >, >=) are ufuncs too — they return a boolean array instead of raising/short-circuiting like Python’s == on lists does. Combine boolean arrays with &, |, ~ (never the Python keywords and/or/not, which don’t work elementwise).

For floating-point data, never compare with == directly — use np.isclose(a, b) (or np.allclose for whole arrays), which accounts for rounding error. Both also accept an atol= (absolute tolerance) keyword, useful when the default relative tolerance alone isn’t strict enough — e.g. when comparing values near zero. np.where(condition, if_true, if_false) is the vectorized equivalent of a Python ternary/if-else, applied elementwise.

a = np.array([1, 2, 3, 4, 5])

mask = (a > 1) & (a < 5)           # combine with & , not `and`
print(mask)
print(a[mask])

print(np.where(a % 2 == 0, "even", "odd"))

x = np.array([0.1 + 0.2, 0.3])
print(x[0] == x[1])        # False due to floating point!
print(np.isclose(x[0], x[1]))

Output:

[False  True  True  True False]
[2 3 4]
['odd' 'even' 'odd' 'even' 'odd']
False
True
4.1

Classify values

Write classify(arr) that returns an array of strings the same length as arr, where each element is "negative", "zero", or "positive" depending on the sign of the corresponding input value. Use np.where (you'll need it twice, nested) — no loop.

💡 Hint

First handle the positive/non-positive split with one np.where, then wrap that call in a second np.where that handles the zero case.

✅ Solution
def classify(arr):
    return np.where(arr > 0, "positive", np.where(arr == 0, "zero", "negative"))
4.2

Close enough

Write count_close_pairs(a, b, tol) that returns how many positions i satisfy np.isclose(a[i], b[i], atol=tol), as a plain Python int.

💡 Hint

np.isclose(a, b, atol=tol) gives a boolean array; .sum() on a boolean array counts the Trues.

✅ Solution
def count_close_pairs(a, b, tol):
    return int(np.isclose(a, b, atol=tol).sum())