Pular para o conteúdo

    Capítulo 15, Básico

    Funções universais (ufuncs)

    Raiz quadrada, logaritmo, exponencial, trigonometria: toda a matemática elementar aplicada ao array inteiro de uma vez, em código compilado, com broadcasting incluído.

    Código deste capítulo: básico/cap15_ufuncs.py

    Matemática sobre o array inteiro

    Uma ufunc (função universal) aplica uma operação a cada elemento. Os operadores +, -, * e / são, por baixo, np.add, np.subtract, np.multiply e np.divide:

    básico/cap15_ufuncs.pylinhas 10 a 16
    import numpy as np
    
    a = np.array([1.0, 4.0, 9.0])
    print(np.sqrt(a), np.exp(np.array([0, 1])), np.log(np.array([1, np.e])))
    print(np.add(a, 1), np.multiply(a, a))
    print(np.abs(np.array([-3, 2])), np.round(np.array([1.234, 5.678]), 1))
    print(np.clip(np.array([-5, 3, 12]), 0, 10))
    
    Saída
    [1. 2. 3.] [1.         2.71828183] [0. 1.]
    [ 2.  5. 10.] [ 1. 16. 81.]
    [3 2] [1.2 5.7]
    [ 0  3 10]
    

    O np.clip(x, mínimo, máximo) "aperta" os valores para dentro de um intervalo: tudo abaixo do mínimo vira o mínimo, e tudo acima do máximo vira o máximo. É muito usado para limpar dados com valores impossíveis.

    Resultados impossíveis viram `nan` e `inf`

    Uma conta impossível (raiz de número negativo, divisão por zero) não derruba o programa: o NumPy emite um aviso e devolve um valor especial, nan ("não é um número") ou inf (infinito). A execução continua:

    básico/cap15_ufuncs.pylinhas 21 a 26
    import warnings
    
    with warnings.catch_warnings(record=True) as avisos:
        warnings.simplefilter("always")
        resultado = np.sqrt(np.array([4.0, -4.0]))
    print(resultado, avisos[0].category.__name__)
    
    Saída
    [ 2. nan] RuntimeWarning
    

    Esse comportamento é conveniente, e também perigoso: o nan se espalha. Qualquer conta com um nan produz nan, e o erro aparece longe de onde nasceu. O capítulo 25 trata dos dados ausentes. Se você sabe o que está fazendo, o np.errstate silencia os avisos em um trecho:

    básico/cap15_ufuncs.pylinhas 28 a 29
    with np.errstate(invalid="ignore", divide="ignore"):
        print(np.array([1.0, 0.0]) / np.array([0.0, 0.0]))
    
    Saída
    [inf nan]
    

    Escolher onde calcular: `where` e `out`

    As ufuncs aceitam dois parâmetros que economizam memória e evitam contas indesejadas. O where diz em quais posições calcular, e o out diz onde guardar o resultado, em vez de criar um array novo. Onde o where é falso, o valor de out permanece:

    básico/cap15_ufuncs.pylinhas 34 a 37
    x = np.array([1.0, 4.0, 9.0, 16.0])
    saida = np.zeros_like(x)
    np.sqrt(x, out=saida, where=x > 5)
    print(saida)
    
    Saída
    [0. 0. 3. 4.]
    

    Divisão inteira e resto

    Os operadores // e % também são ufuncs, e a divisão inteira arredonda para baixo, mesmo com números negativos:

    básico/cap15_ufuncs.pylinha 42
    print(np.array([7, -7]) // 2, np.mod(-7, 3))
    
    Saída
    [ 3 -4] 2
    

    Não misture math com NumPy

    O módulo math trabalha com um número por vez e falha com arrays (capítulo 12). O NumPy não só aceita arrays como também aceita um número isolado, então, em código numérico, prefira np.sqrt, np.exp e companhia, mesmo para valores únicos, e o código serve para os dois casos.

    Exercício 1

    A função sigmoide

    Escreva sigmoide(x), igual a 1 / (1 + e^(-x)). É a função que "comprime" qualquer número para o intervalo entre 0 e 1, e é a base de muita coisa em aprendizado de máquina.