Pular para o conteúdo

    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:

    intermediario/cap21_condicoes.pylinhas 10 a 15
    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))
    
    Saída
    ['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:

    intermediario/cap21_condicoes.pylinhas 20 a 22
    condicoes = [notas >= 90, notas >= 70, notas >= 60]
    rotulos = ["A", "B", "C"]
    print(np.select(condicoes, rotulos, default="D"))
    
    Saída
    ['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:

    intermediario/cap21_condicoes.pylinhas 27 a 30
    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]))
    
    Saída
    [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:

    intermediario/cap21_condicoes.pylinhas 35 a 38
    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)
    
    Saída
    [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):

    intermediario/cap21_condicoes.pylinhas 40 a 41
    seguro = np.divide(1.0, den, out=np.zeros_like(den), where=den != 0)
    print(seguro)
    
    Saída
    [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.