Chapter 15, Basic
Universal functions (ufuncs)
Square root, logarithm, exponential, trigonometry: all elementary mathematics applied to the entire array at once, in compiled code, with broadcasting included.
Code for this chapter: básico/cap15_ufuncs.py
Mathematics over the entire array
A ufunc (universal function) applies an operation to each element. The operators +, -, * and / are, underneath, np.add, np.subtract, np.multiply and np.divide:
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))
[1. 2. 3.] [1. 2.71828183] [0. 1.]
[ 2. 5. 10.] [ 1. 16. 81.]
[3 2] [1.2 5.7]
[ 0 3 10]
The np.clip(x, mínimo, máximo) "squeeze" the values into a range: everything below the minimum becomes the minimum, and everything above the maximum becomes the maximum. It is widely used to clean data with impossible values.
Impossible results become `nan` and `inf`
An impossible calculation (square root of a negative number, division by zero) does not crash the program: NumPy issues a warning and returns a special value, nan ("not a number") or inf (infinity). Execution continues:
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__)
[ 2. nan] RuntimeWarning
This behavior is convenient, and also dangerous: the nan spreads. Any calculation with a nan produces nan, and the error appears far from where it was born. Chapter 25 deals with missing data. If you know what you're doing, the np.errstate silences warnings in a snippet:
with np.errstate(invalid="ignore", divide="ignore"):
print(np.array([1.0, 0.0]) / np.array([0.0, 0.0]))
[inf nan]
Choose where to calculate: `where` and `out`
Ufuncs accept two parameters that save memory and avoid unwanted calculations. The where tells in which positions to calculate, and the out tells where to store the result, instead of creating a new array. Where the where is false, the value of out remains:
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)
[0. 0. 3. 4.]
Integer division and remainder
The operators // and % are also ufuncs, and integer division rounds down, even with negative numbers:
print(np.array([7, -7]) // 2, np.mod(-7, 3))
[ 3 -4] 2
Do not mix math with NumPy
The module
mathworks with a number at a time and fails with arrays (chapter 12). NumPy not only accepts arrays but also accepts a single number, so in numerical code, prefernp.sqrt,np.expand company, even for single values, and the code works for both cases.
Exercise 1
The sigmoid function
Write sigmoide(x), equal to 1 / (1 + e^(-x)). It is the function that "compresses" any number to the range between 0 and 1, and it is the basis of much in machine learning.