Chapter 21, Intermediate
Vectorized conditions: where, select and clip
The `if` does not accept an array. To apply a decision to each element, you use functions that make the choice for the entire array at once.
Code for this chapter: intermediario/cap21_condicoes.py
`np.where`: the `if` of the array
With three arguments, the np.where(condição, se_verdadeiro, se_falso) chooses, element by element, the value of one of the two outputs. With a single argument, it returns the positions where the condition is true:
import numpy as np
notas = np.array([55, 72, 91, 40, 68])
print(np.where(notas >= 60, "aprovado", "reprovado"))
print(np.where(notas >= 60, notas, 0))
print(np.where(notas > 70))
['reprovado' 'aprovado' 'aprovado' 'reprovado' 'aprovado']
[ 0 72 91 0 68]
(array([1, 2]),)
The result of a single argument is a tuple with an array of positions per axis, and that is why it shows the (array([1, 2]),).
`np.select`: multiple ranges
With more than two options, chaining where becomes messy. The np.select takes a list of conditions and a list of results, and uses the result of the first true condition. Therefore, the order matters: the more restrictive conditions come first:
condicoes = [notas >= 90, notas >= 70, notas >= 60]
rotulos = ["A", "B", "C"]
print(np.select(condicoes, rotulos, default="D"))
['D' 'B' 'A' 'D' 'C']
Limit and compare element by element
The np.clip limits the values to a range. The np.maximum and np.minimum compare two arrays (or an array and a number) element by element, and the np.maximum(x, 0) is the ReLU function, one of the most used in neural networks:
print(np.maximum(notas, 50))
x = np.array([-2.0, -0.5, 0.5, 2.0])
print(np.piecewise(x, [x < 0, x >= 0], [lambda t: t ** 2, lambda t: t]))
[55 72 91 50 68]
[4. 0.25 0.5 2. ]
The np.piecewise applies a different function in each range, useful for piecewise defined functions.
The trap: `where` calculates both sides
The np.where evaluates both entire expressions before choosing. If one of them generates an error or a warning on some element (a division by zero), the warning appears even if that element was never chosen:
den = np.array([2.0, 0.0, 4.0])
with np.errstate(divide="ignore"):
ingenuo = np.where(den != 0, 1 / den, 0.0)
print(ingenuo)
[0.5 0. 0.25]
To not calculate where it shouldn't, use the where and out of the ufunc itself (chapter 15):
seguro = np.divide(1.0, den, out=np.zeros_like(den), where=den != 0)
print(seguro)
[0.5 0. 0.25]
The result is the same, but the second form doesn't even attempt to divide by zero.
Exercise 1
Classify the BMI
Write classificar_imc(imc) that returns "abaixo" (less than 18.5), "normal" (less than 25), "sobrepeso" (less than 30) or "obesidade", for an array of values, with np.select.