This is an excerpt from the Python Data Science Handbook by Jake VanderPlas; Jupyter notebooks are available on GitHub.

The text is released under the CC-BY-NC-ND license, and code is released under the MIT license. If you find this content useful, please consider supporting the work by buying the book!

Cálculos con Arrays 1. Ufuncs

Los Loops son Lentooosss

In [1]:
import numpy as np
np.random.seed(0)

def compute_reciprocals(values):
    output = np.empty(len(values))
    for i in range(len(values)):
        output[i] = 1.0 / values[i]
    return output
        
values = np.random.randint(1, 10, size=5)
compute_reciprocals(values)
Out[1]:
array([ 0.16666667,  1.        ,  0.25      ,  0.25      ,  0.125     ])
In [2]:
big_array = np.random.randint(1, 100, size=1000000)
%timeit compute_reciprocals(big_array)
1 loop, best of 3: 2.91 s per loop

UFuncs

Operaciones vectorizadas

In [3]:
print(compute_reciprocals(values))
print(1.0 / values)
[ 0.16666667  1.          0.25        0.25        0.125     ]
[ 0.16666667  1.          0.25        0.25        0.125     ]
In [4]:
%timeit (1.0 / big_array)
100 loops, best of 3: 4.6 ms per loop
In [5]:
np.arange(5) / np.arange(1, 6)
Out[5]:
array([ 0.        ,  0.5       ,  0.66666667,  0.75      ,  0.8       ])
In [6]:
x = np.arange(9).reshape((3, 3))
2 ** x
Out[6]:
array([[  1,   2,   4],
       [  8,  16,  32],
       [ 64, 128, 256]])

Cálculos usando vectorizacion a través de ufuncs son en general más eficientes que la implementación usando loops

Exploración de UFuncs

Ufuncs:

  • unary ufuncs, operan sobre una sola entrada
  • binary ufuncs, operan sobre dos entradas.

Aritmética de Array

Las operaciones aritméticas de adición, substracción, multiplicación y división standard:

In [7]:
x = np.arange(4)
print("x     =", x)
print("x + 5 =", x + 5)
print("x - 5 =", x - 5)
print("x * 2 =", x * 2)
print("x / 2 =", x / 2)
print("x // 2 =", x // 2)  # floor division
x     = [0 1 2 3]
x + 5 = [5 6 7 8]
x - 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x / 2 = [ 0.   0.5  1.   1.5]
x // 2 = [0 0 1 1]

Negation, operador de exponencia ** y operador de módulo %:

In [8]:
print("-x     = ", -x)
print("x ** 2 = ", x ** 2)
print("x % 2  = ", x % 2)
-x     =  [ 0 -1 -2 -3]
x ** 2 =  [0 1 4 9]
x % 2  =  [0 1 0 1]

Orden standard de las operaciones:

In [9]:
-(0.5*x + 1) ** 2
Out[9]:
array([-1.  , -2.25, -4.  , -6.25])

El operador + es un wrapper para la función add:

In [10]:
np.add(x, 2)
Out[10]:
array([2, 3, 4, 5])

The siguiente tabla enlista los operadores aritméticos implementados en NumPy:

Operator Equivalent ufunc Description
+ np.add Addition (e.g., 1 + 1 = 2)
- np.subtract Subtraction (e.g., 3 - 2 = 1)
- np.negative Unary negation (e.g., -2)
* np.multiply Multiplication (e.g., 2 * 3 = 6)
/ np.divide Division (e.g., 3 / 2 = 1.5)
// np.floor_divide Floor division (e.g., 3 // 2 = 1)
** np.power Exponentiation (e.g., 2 ** 3 = 8)
% np.mod Modulus/remainder (e.g., 9 % 4 = 1)

Adicionalmente, hay operadores Boolean; exploraremos éstos en Comparaciones, mascaras y logica booleana.

Valor Absoluto

In [11]:
# Python abs
x = np.array([-2, -1, 0, 1, 2])
abs(x)
Out[11]:
array([2, 1, 0, 1, 2])
In [12]:
np.absolute(x)
Out[12]:
array([2, 1, 0, 1, 2])
In [13]:
np.abs(x)
Out[13]:
array([2, 1, 0, 1, 2])

Esta ufunc puede operar sobre números complejos:

In [14]:
x = np.array([3 - 4j, 4 - 3j, 2 + 0j, 0 + 1j])
np.abs(x)
Out[14]:
array([ 5.,  5.,  2.,  1.])

Funciones Trigonometricas

In [15]:
theta = np.linspace(0, np.pi, 3)
In [16]:
print("theta      = ", theta)
print("sin(theta) = ", np.sin(theta))
print("cos(theta) = ", np.cos(theta))
print("tan(theta) = ", np.tan(theta))
theta      =  [ 0.          1.57079633  3.14159265]
sin(theta) =  [  0.00000000e+00   1.00000000e+00   1.22464680e-16]
cos(theta) =  [  1.00000000e+00   6.12323400e-17  -1.00000000e+00]
tan(theta) =  [  0.00000000e+00   1.63312394e+16  -1.22464680e-16]
In [17]:
x = [-1, 0, 1]
print("x         = ", x)
print("arcsin(x) = ", np.arcsin(x))
print("arccos(x) = ", np.arccos(x))
print("arctan(x) = ", np.arctan(x))
x         =  [-1, 0, 1]
arcsin(x) =  [-1.57079633  0.          1.57079633]
arccos(x) =  [ 3.14159265  1.57079633  0.        ]
arctan(x) =  [-0.78539816  0.          0.78539816]

Exponentes y Logaritmos

In [18]:
x = [1, 2, 3]
print("x     =", x)
print("e^x   =", np.exp(x))
print("2^x   =", np.exp2(x))
print("3^x   =", np.power(3, x))
x     = [1, 2, 3]
e^x   = [  2.71828183   7.3890561   20.08553692]
2^x   = [ 2.  4.  8.]
3^x   = [ 3  9 27]
In [19]:
x = [1, 2, 4, 10]
print("x        =", x)
print("ln(x)    =", np.log(x))
print("log2(x)  =", np.log2(x))
print("log10(x) =", np.log10(x))
x        = [1, 2, 4, 10]
ln(x)    = [ 0.          0.69314718  1.38629436  2.30258509]
log2(x)  = [ 0.          1.          2.          3.32192809]
log10(x) = [ 0.          0.30103     0.60205999  1.        ]
In [20]:
# for small input
x = [0, 0.001, 0.01, 0.1]
print("exp(x) - 1 =", np.expm1(x))
print("log(1 + x) =", np.log1p(x))
exp(x) - 1 = [ 0.          0.0010005   0.01005017  0.10517092]
log(1 + x) = [ 0.          0.0009995   0.00995033  0.09531018]

Ufuncs especializadas

In [21]:
from scipy import special
In [22]:
# Gamma functions (generalized factorials) and related functions
x = [1, 5, 10]
print("gamma(x)     =", special.gamma(x))
print("ln|gamma(x)| =", special.gammaln(x))
print("beta(x, 2)   =", special.beta(x, 2))
gamma(x)     = [  1.00000000e+00   2.40000000e+01   3.62880000e+05]
ln|gamma(x)| = [  0.           3.17805383  12.80182748]
beta(x, 2)   = [ 0.5         0.03333333  0.00909091]
In [23]:
# Error function (integral of Gaussian)
# its complement, and its inverse
x = np.array([0, 0.3, 0.7, 1.0])
print("erf(x)  =", special.erf(x))
print("erfc(x) =", special.erfc(x))
print("erfinv(x) =", special.erfinv(x))
erf(x)  = [ 0.          0.32862676  0.67780119  0.84270079]
erfc(x) = [ 1.          0.67137324  0.32219881  0.15729921]
erfinv(x) = [ 0.          0.27246271  0.73286908         inf]

Propiedades Avanzadas de Ufunc

Especificando la salida

Todas las ufuncs pueden usar el argumento out para indicar donde asignar el resultado

In [24]:
x = np.arange(5)
y = np.empty(5)
np.multiply(x, 10, out=y)
print(y)
[  0.  10.  20.  30.  40.]
In [25]:
y = np.zeros(10)
np.power(2, x, out=y[::2])
print(y)
[  1.   0.   2.   0.   4.   0.   8.   0.  16.   0.]
In [ ]:
# lo mismo pero crea un array temporal para guardar el resultado de 2**x, 
# seguido de una segunda operacion que copia esos valores a y 
# que puede ser costoso para arrays grandes
y = np.zeros(10)
y[::2] = 2**x

Agregaciones

In [26]:
x = np.arange(1, 6)
np.add.reduce(x)
Out[26]:
15
In [27]:
np.multiply.reduce(x)
Out[27]:
120
In [28]:
np.add.accumulate(x)
Out[28]:
array([ 1,  3,  6, 10, 15])
In [29]:
np.multiply.accumulate(x)
Out[29]:
array([  1,   2,   6,  24, 120])

Exploraremos un poco mas en Agregaciones: Min, Max, etc..

Productos exteriores

In [30]:
x = np.arange(1, 6)
np.multiply.outer(x, x)
Out[30]:
array([[ 1,  2,  3,  4,  5],
       [ 2,  4,  6,  8, 10],
       [ 3,  6,  9, 12, 15],
       [ 4,  8, 12, 16, 20],
       [ 5, 10, 15, 20, 25]])

Otros métodos se exploran en Más índices,asi como la habilidad de operar entre arrays de distintos tamaños y formas, llamado broadcasting, que se ve en Calculos con Arrays 2. Broadcasting.

Ufuncs: Más información

Más información sobre las funciones universales puede consultarse en la documentación de NumPy y SciPy.