Capítulo 21, Intermediário
Condições vetorizadas: where, select e clip
O `if` não aceita um array. Para aplicar uma decisão a cada elemento, você usa funções que fazem a escolha para o array inteiro de uma vez.
Código deste capítulo: intermediario/cap21_condicoes.py
`np.where`: o `if` do array
Com três argumentos, o np.where(condição, se_verdadeiro, se_falso) escolhe, elemento a elemento, o valor de uma das duas saídas. Com um só argumento, devolve as posições onde a condição é verdadeira:
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]),)
O resultado de um argumento só é uma tupla com um array de posições por eixo, e por isso aparece o (array([1, 2]),).
`np.select`: várias faixas
Com mais de duas opções, encadear where vira bagunça. O np.select recebe uma lista de condições e uma lista de resultados, e usa o resultado da primeira condição verdadeira. Por isso a ordem importa: as condições mais restritivas vêm primeiro:
condicoes = [notas >= 90, notas >= 70, notas >= 60]
rotulos = ["A", "B", "C"]
print(np.select(condicoes, rotulos, default="D"))
['D' 'B' 'A' 'D' 'C']
Limitar e comparar elemento a elemento
O np.clip limita os valores a um intervalo. O np.maximum e o np.minimum comparam dois arrays (ou um array e um número) elemento a elemento, e o np.maximum(x, 0) é a função ReLU, uma das mais usadas em redes neurais:
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. ]
O np.piecewise aplica uma função diferente em cada faixa, útil para funções definidas por partes.
A armadilha: `where` calcula os dois lados
O np.where avalia as duas expressões inteiras antes de escolher. Se uma delas gera um erro ou um aviso em algum elemento (uma divisão por zero), o aviso aparece mesmo que aquele elemento nunca fosse escolhido:
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]
Para não calcular onde não deve, use o where e o out da própria ufunc (capítulo 15):
seguro = np.divide(1.0, den, out=np.zeros_like(den), where=den != 0)
print(seguro)
[0.5 0. 0.25]
O resultado é o mesmo, mas a segunda forma nem tenta dividir por zero.
Exercício 1
Classificar o IMC
Escreva classificar_imc(imc) que devolva "abaixo" (menor que 18,5), "normal" (menor que 25), "sobrepeso" (menor que 30) ou "obesidade", para um array de valores, com np.select.