Python de cero a experto: Numpy#

Autor: Luis Miguel de la Cruz Salas

Python de cero a experto by Luis M. de la Cruz Salas is licensed under Attribution-NonCommercial-NoDerivatives 4.0 International

Objetivo general

  • Conocer a fondo las ventajas de la biblioteca Numpy.

Objetivos particulares

  • Identificar algunas de las funciones más usadas de la biblioteca Numpy.

  • Hacer énfasis en los métodos y conceptos que se aprenderán y en algunas cuestiones computacionales.

Contenido#

Numpy#

Es una biblioteca de Python que permite crear y gestionar arreglos multidimensionales, junto con una gran colección de funciones matemáticas de alto nivel que operan sobre estos arreglos. El sitio oficial es https://numpy.org/

Para usar todas las herramientas de numpy debemos importar la biblioteca como sigue:

import numpy as np
np.version.version
'1.21.5'
# Función para obtener los atributos de arreglos
info_array = lambda x: print(f' tipo  : {type(x)} \n dtype : {x.dtype} \n dim   : {x.ndim} \n shape : {x.shape} \n size(bytes) : {x.itemsize} \n size(elements) : {x.size}')

Creación de arreglos simples#

Crear un arreglo de números del 1 al 10 usando: np.array, np.arange, np.linspace, np.zeros, np.ones, np.random.rand

x = np.array([1,2,3,4,5,6,7,8,9,10])
x
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 4 
 size(elements) : 10
x = np.arange(10)
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x = np.arange(1,11,1)
x
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 4 
 size(elements) : 10

Ojo np.arange() acepta parámetros flotantes:

xf = np.arange(1, 11, 1.0)
xf
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
info_array(xf)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
xf = np.arange(0.3, 0.7, 0.12)
xf
array([0.3 , 0.42, 0.54, 0.66])
x = np.linspace(1,10,10)
x
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10

Ojo: con np.linspace es posible generar un número exacto de elementos, por ejemplo:

xf = np.linspace(0.3, 0.7, 6)
xf
array([0.3 , 0.38, 0.46, 0.54, 0.62, 0.7 ])
x = np.zeros(10)
x
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
for i,val in enumerate(x):
    x[i] = i+1
x
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
x = np.ones(10)
x
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
for i,val in enumerate(x):
    x[i] = i+val
x
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
x *= 2
x
array([ 2.,  4.,  6.,  8., 10., 12., 14., 16., 18., 20.])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
x = np.random.rand(10)
info_array(x)
x
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
array([0.95264144, 0.0307895 , 0.70781213, 0.86148072, 0.95815789,
       0.4442273 , 0.61359623, 0.99906471, 0.80411922, 0.48092923])
x = np.random.rand(2,5)
info_array(x)
x
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 2 
 shape : (2, 5) 
 size(bytes) : 8 
 size(elements) : 10
array([[0.14520303, 0.97016093, 0.55601282, 0.77542234, 0.143609  ],
       [0.19220465, 0.36647977, 0.53623488, 0.06378879, 0.18409342]])

Nota sobre número pseudoaleatorios#

No es posible generar números aleatorios con base en un algoritmo, solo se pueden obtener números pseudoaleatorios (para obtener números aleatorios es necesario muestrear algún parámetro físico cuyo valor sea realmente aleatorio).

Las series de números aleatorios que se pueden generar en una computadora son secuencias deterministas a partir de un valor inicial, al que se llama semilla. Al fijar la semilla se fija completamente los valores de toda la serie.

np.random.seed(0)
print("Serie 1:", np.random.rand(3))
np.random.seed(3)
print("Serie 2:", np.random.rand(3))
np.random.seed(0)
print("Serie 3:", np.random.rand(3))
Serie 1: [0.5488135  0.71518937 0.60276338]
Serie 2: [0.5507979  0.70814782 0.29090474]
Serie 3: [0.5488135  0.71518937 0.60276338]

Se puede generar una serie de números aleatorios usando una semilla aleatoria. Por ejemplo:

import time
np.random.seed(int(time.time()))
np.random.rand(3)
array([0.27741954, 0.43947286, 0.67275793])

Modificar el tipo de dato de los elementos del arreglo#

x = np.linspace(1,10,10)
# La modificación afecta al arreglo 'y' pero no al arreglo 'x' (no es inplace)
y = x.astype(int)
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
info_array(y)
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 4 
 size(elements) : 10
print(id(x), id(y))
3035013915152 3035013915056
print(x)
print(y)
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]
[ 1  2  3  4  5  6  7  8  9 10]

Arreglos multidimensionales#

x = np.array([[1,2.0],[0,0],(1+1j,3.)])
x
array([[1.+0.j, 2.+0.j],
       [0.+0.j, 0.+0.j],
       [1.+1.j, 3.+0.j]])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : complex128 
 dim   : 2 
 shape : (3, 2) 
 size(bytes) : 16 
 size(elements) : 6
x = np.array( [ [1,2], [3,4] ], dtype=complex )
x
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : complex128 
 dim   : 2 
 shape : (2, 2) 
 size(bytes) : 16 
 size(elements) : 4
x = np.array( [ [[1,2], [3,4]], [[5,6], [7,8]] ])
x
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 3 
 shape : (2, 2, 2) 
 size(bytes) : 4 
 size(elements) : 8
x = np.zeros((10,10))
x
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 2 
 shape : (10, 10) 
 size(bytes) : 8 
 size(elements) : 100
x = np.ones((4,3,2))
x
array([[[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.]]])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 3 
 shape : (4, 3, 2) 
 size(bytes) : 8 
 size(elements) : 24
x = np.empty((2,3,4))
x
array([[[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]],

       [[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]]])
info_array(x)
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 3 
 shape : (2, 3, 4) 
 size(bytes) : 8 
 size(elements) : 24

Cambiando el shape de los arreglos#

Función reshape#

x = np.array([ [[ 1, 2, 3, 4],
                [ 5, 6, 7, 8],
                [ 9,10,11,12]],
               [[13,14,15,16],
                [17,16,19,20],
                [21,22,23,24]] ])
info_array(x)
print(f'x = \n {x}')
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 3 
 shape : (2, 3, 4) 
 size(bytes) : 4 
 size(elements) : 24
x = 
 [[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 16 19 20]
  [21 22 23 24]]]
y = x.reshape(6,4)
info_array(y)
print(f'y = \n {y}')
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 2 
 shape : (6, 4) 
 size(bytes) : 4 
 size(elements) : 24
y = 
 [[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]
 [13 14 15 16]
 [17 16 19 20]
 [21 22 23 24]]
info_array(x)
print(f'x = \n {x} \n')
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 3 
 shape : (2, 3, 4) 
 size(bytes) : 4 
 size(elements) : 24
x = 
 [[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 16 19 20]
  [21 22 23 24]]] 
y = x.reshape(24)
info_array(y)
print(f'y = \n {y}')
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 1 
 shape : (24,) 
 size(bytes) : 4 
 size(elements) : 24
y = 
 [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 16 19 20 21 22 23 24]
y = x.reshape(2,3,4)
info_array(y)
print(f'y = \n {y}')
print(f'x = \n {x}')
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 3 
 shape : (2, 3, 4) 
 size(bytes) : 4 
 size(elements) : 24
y = 
 [[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 16 19 20]
  [21 22 23 24]]]
x = 
 [[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 16 19 20]
  [21 22 23 24]]]
# Otra manera
np.reshape(x, (6,4))
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12],
       [13, 14, 15, 16],
       [17, 16, 19, 20],
       [21, 22, 23, 24]])
x
array([[[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]],

       [[13, 14, 15, 16],
        [17, 16, 19, 20],
        [21, 22, 23, 24]]])

Atributo shape (inplace)#

y.shape
(2, 3, 4)
y.shape = (6,4)
y.shape
(6, 4)
info_array(y)
print(f'y = \n {y}')
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 2 
 shape : (6, 4) 
 size(bytes) : 4 
 size(elements) : 24
y = 
 [[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]
 [13 14 15 16]
 [17 16 19 20]
 [21 22 23 24]]

Creando un arreglo y modificando su shape al vuelo#

x = np.arange(24).reshape(2,3,4)
x
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
x = np.arange(1,25,1).reshape(2,3,4)
info_array(x)
x
 tipo  : <class 'numpy.ndarray'> 
 dtype : int32 
 dim   : 3 
 shape : (2, 3, 4) 
 size(bytes) : 4 
 size(elements) : 24
array([[[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]],

       [[13, 14, 15, 16],
        [17, 18, 19, 20],
        [21, 22, 23, 24]]])

Copias y vistas de arreglos#

x = np.array([1,2,3,4])
z = x  # z es un sinónimo de x, no se crea una copia!
print(id(z), id(x))
print(z is x)
print(x is z)
3035013730000 3035013730000
True
True

Los objetos que son mutables se pasan por referencia a una función:

def f(a):
    print(id(a))

print(id(x))
print(f(x))
3035013730000
3035013730000
None

Copia superficial o vista de un arreglo#

z = x.view()
print(id(z), id(x))
print(z is x)
print(x is z)
print(z.base is x) # Comparten la memoria
print(z.flags.owndata) # Propiedades de la memoria
print(x.flags.owndata) # Propiedades de la memoria
3035013882192 3035013730000
False
False
True
False
True
print(z.flags)
  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False
z.shape =(2,2)
print(z.shape, z, sep = '\n')
print(x.shape, x, sep = '\n')
(2, 2)
[[1 2]
 [3 4]]
(4,)
[1 2 3 4]
z[1,1] = 1000
print(z.shape, z, sep = '\n')
print(x.shape, x, sep = '\n')
(2, 2)
[[   1    2]
 [   3 1000]]
(4,)
[   1    2    3 1000]

Copia completa de arreglos#

z = x.copy()
print(id(z), id(x))
print(z is x)
print(x is z)
print(z.base is x) # Comparten la memoria
print(z.flags.owndata) # Propiedades de la memoria
print(x.flags.owndata) # Propiedades de la memoria
3035014003856 3035013730000
False
False
False
True
True
print('z = ', z)
print('x = ', x)
z =  [   1    2    3 1000]
x =  [   1    2    3 1000]
z[3] = 4
print('z = ', z)
print('x = ', x)
z =  [1 2 3 4]
x =  [   1    2    3 1000]

Las rebanadas son vistas de arreglos#

Las vistas de arreglos pueden ser útiles en ciertos casos, por ejemplo si tenemos un arreglo muy grande y solo deseamos mantener unos cuantos elementos del mismo, debemos hacer lo siguiente:

a = np.arange(int(1e5)) # Arreglo de 100000 elementos
b = a[:200].copy()      # Copia completa de 200 elementos de 'a'
del a                   # Eliminar la memoria que usa 'a'
b
array([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,
        13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
        26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
        39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
        52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
        65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
        78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
        91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
       117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
       143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
       156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
       169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
       182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
       195, 196, 197, 198, 199])

Pero si usamos rebanadas, el comportamiento es distinto:

a = np.arange(int(1e5)) # Arreglo de 100000 elementos
b = a[:200]             # Vista de 200 elementos de 'a'
b[0] = 1000
print('b = ', b)
print('a = ', a)
b =  [1000    1    2    3    4    5    6    7    8    9   10   11   12   13
   14   15   16   17   18   19   20   21   22   23   24   25   26   27
   28   29   30   31   32   33   34   35   36   37   38   39   40   41
   42   43   44   45   46   47   48   49   50   51   52   53   54   55
   56   57   58   59   60   61   62   63   64   65   66   67   68   69
   70   71   72   73   74   75   76   77   78   79   80   81   82   83
   84   85   86   87   88   89   90   91   92   93   94   95   96   97
   98   99  100  101  102  103  104  105  106  107  108  109  110  111
  112  113  114  115  116  117  118  119  120  121  122  123  124  125
  126  127  128  129  130  131  132  133  134  135  136  137  138  139
  140  141  142  143  144  145  146  147  148  149  150  151  152  153
  154  155  156  157  158  159  160  161  162  163  164  165  166  167
  168  169  170  171  172  173  174  175  176  177  178  179  180  181
  182  183  184  185  186  187  188  189  190  191  192  193  194  195
  196  197  198  199]
a =  [ 1000     1     2 ... 99997 99998 99999]

Rebanadas (slicing)#

x = np.arange(0,10,1.)
x
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
x[:] # El arreglo completo
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
x[3:6] # Una sección del arreglo, de 3 a 5
array([3., 4., 5.])
x[2:9:2] # de 2 a 8, dando saltos de 2 en 2
array([2., 4., 6., 8.])
x[1:7:2] = 100 # modificando algunos elementos del arreglo
x
array([  0., 100.,   2., 100.,   4., 100.,   6.,   7.,   8.,   9.])
y = np.arange(36).reshape(6,6)
y
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])
y[1:4,:] # renglones de 1 a 3
array([[ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
y[:,1:5] # columnas de 1 a 4
array([[ 1,  2,  3,  4],
       [ 7,  8,  9, 10],
       [13, 14, 15, 16],
       [19, 20, 21, 22],
       [25, 26, 27, 28],
       [31, 32, 33, 34]])
y[2:4,2:5] # seccion del arreglo
array([[14, 15, 16],
       [20, 21, 22]])
y[1:5:2,1:5:2] # sección del arreglo
               # con saltos de 2
array([[ 7,  9],
       [19, 21]])
y[1:5:2,1:5:2] = 0 # Modificación de
y                  # algunos elementos
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  0,  8,  0, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18,  0, 20,  0, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

También es posible seleccionar elementos que cumplan cierto criterio.

y[y<25] # Selecciona los elementos del arreglo que son menores que 25
array([ 0,  1,  2,  3,  4,  5,  6,  0,  8,  0, 10, 11, 12, 13, 14, 15, 16,
       17, 18,  0, 20,  0, 22, 23, 24])
y[y%2==0] # Selecciona todos los elementos pares
array([ 0,  2,  4,  6,  0,  8,  0, 10, 12, 14, 16, 18,  0, 20,  0, 22, 24,
       26, 28, 30, 32, 34])
y[(y>8) & (y<20)] # Selecciona todos los elementos mayores que 8 y menores que 20
array([10, 11, 12, 13, 14, 15, 16, 17, 18])
y[(y>8) & (y<20)] = 666
y
array([[  0,   1,   2,   3,   4,   5],
       [  6,   0,   8,   0, 666, 666],
       [666, 666, 666, 666, 666, 666],
       [666,   0,  20,   0,  22,  23],
       [ 24,  25,  26,  27,  28,  29],
       [ 30,  31,  32,  33,  34,  35]])
z = np.nonzero(y == 666) # Determina los renglones y las columnas
z                        # donde se cumple la condición.
(array([1, 1, 2, 2, 2, 2, 2, 2, 3], dtype=int64),
 array([4, 5, 0, 1, 2, 3, 4, 5, 0], dtype=int64))
z[0]
z[1]
array([4, 5, 0, 1, 2, 3, 4, 5, 0], dtype=int64)
indices = list(zip(z[0], z[1])) # Genera una lista de coordenadas donde se cumple la condición.
indices
[(1, 4), (1, 5), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (3, 0)]
print(y[z]) # Imprime los elementos del arreglo 'y' usando las coordenadas de 'z'
[666 666 666 666 666 666 666 666 666]

Operaciones básicas entre arreglos#

v1 = np.array([2.3,3.1,9.6])
v2 = np.array([3.4,5.6,7.8])
(1/3)*v1 # Escalar por arreglo
array([0.76666667, 1.03333333, 3.2       ])
v1+v2 # Suma de arreglos
array([ 5.7,  8.7, 17.4])
v1-v2 # Resta de arreglos
array([-1.1, -2.5,  1.8])
v1*v2 # Multiplicación elemento a elemento
array([ 7.82, 17.36, 74.88])
v1/v2 # División elemento a elemento
array([0.67647059, 0.55357143, 1.23076923])
v1 ** 2 # Potencia de un arreglo
array([ 5.29,  9.61, 92.16])
v1 % 2  # Modulo de un arreglo
array([0.3, 1.1, 1.6])
10 * np.sin(v1) # Aplicación de una función matemática a cada elemento del arreglo
array([ 7.45705212,  0.41580662, -1.74326781])
v1 > 3 # Operación de comparación, devuelve un arreglo Booleano
array([False,  True,  True])

Operaciones entre arreglos Booleanos#

f = np.array([True, False, False, True])
r = np.array([False, True, False, True])
f & r
array([False, False, False,  True])
f | r
array([ True,  True, False,  True])
~f
array([False,  True,  True, False])
b = np.arange(4)
b
array([0, 1, 2, 3])
b[f]
array([0, 3])
b[f] = 100
b
array([100,   1,   2, 100])

Métodos de los arreglos#

Existe una larga lista de métodos definidos para los arreglos, vea más información aquí.

x = np.random.random(100) # arreglo de 100 números aleatorios entre 1 y 0
x
array([0.87291136, 0.4667654 , 0.70839531, 0.11684876, 0.61010161,
       0.7147785 , 0.85947455, 0.41154746, 0.20367318, 0.24597665,
       0.42261173, 0.91721399, 0.72011719, 0.91370418, 0.26290151,
       0.03459652, 0.66887324, 0.46283526, 0.5203149 , 0.00208701,
       0.11384754, 0.27872633, 0.62833527, 0.59686206, 0.89216901,
       0.4018794 , 0.05253424, 0.3615654 , 0.58458147, 0.02306903,
       0.62590551, 0.26816266, 0.0862259 , 0.30367739, 0.70024002,
       0.56309863, 0.1705416 , 0.63663901, 0.88721368, 0.39898073,
       0.02959757, 0.83432856, 0.61962458, 0.2100619 , 0.59540551,
       0.59486019, 0.68973324, 0.46701423, 0.84454763, 0.24243676,
       0.43177763, 0.46203364, 0.68622725, 0.14122981, 0.50100177,
       0.63985902, 0.14659953, 0.28232146, 0.0509266 , 0.93795137,
       0.64457388, 0.2156847 , 0.70847786, 0.36213372, 0.96756227,
       0.96529352, 0.35240117, 0.04446743, 0.29282926, 0.44647528,
       0.48893531, 0.94065941, 0.03167257, 0.22116515, 0.43616742,
       0.16834263, 0.53048185, 0.60647013, 0.51353945, 0.83722147,
       0.26796455, 0.88329564, 0.44291643, 0.38710391, 0.02580616,
       0.80157481, 0.64949162, 0.13565818, 0.66619073, 0.59266059,
       0.38782264, 0.19657226, 0.40097001, 0.14446758, 0.86432544,
       0.15629524, 0.13793043, 0.87147767, 0.80171914, 0.26516875])
x.max()
0.9675622714985623
x.sum()
46.97148771826547
x = np.arange(10).reshape(2,5)
x
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
x.T
array([[0, 5],
       [1, 6],
       [2, 7],
       [3, 8],
       [4, 9]])
x.transpose()
array([[0, 5],
       [1, 6],
       [2, 7],
       [3, 8],
       [4, 9]])
np.transpose(x)
array([[0, 5],
       [1, 6],
       [2, 7],
       [3, 8],
       [4, 9]])
np.flip(x) # Cambiar el orden de los elementos del arreglo
array([[9, 8, 7, 6, 5],
       [4, 3, 2, 1, 0]])
np.flip(x, axis=0)
array([[5, 6, 7, 8, 9],
       [0, 1, 2, 3, 4]])
f1 = x.flatten() # Aplanar un arreglo
f1[0] = 1000
print(x)
print(f1)
[[0 1 2 3 4]
 [5 6 7 8 9]]
[1000    1    2    3    4    5    6    7    8    9]
print(x)
[[0 1 2 3 4]
 [5 6 7 8 9]]
f2 = x.ravel() # Aplanar un arreglo
f2[0] = 1000
print(x)
print(f1)
[[1000    1    2    3    4]
 [   5    6    7    8    9]]
[1000    1    2    3    4    5    6    7    8    9]

Los arreglos deben ser compatibles para poder realizar las operaciones anteriores:

a = np.arange(24).reshape(2,3,4)
b = np.arange(24).reshape(2,3,4)
a + b
array([[[ 0,  2,  4,  6],
        [ 8, 10, 12, 14],
        [16, 18, 20, 22]],

       [[24, 26, 28, 30],
        [32, 34, 36, 38],
        [40, 42, 44, 46]]])
c = np.arange(24).reshape(6,4)
a + c
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_13860\4122427811.py in <module>
      1 c = np.arange(24).reshape(6,4)
----> 2 a + c

ValueError: operands could not be broadcast together with shapes (2,3,4) (6,4) 

Apilación y concatenación de arreglos#

a = np.arange(4).reshape(2,2)
b = np.arange(4,8,1).reshape(2,2)
print(a)
print(b)
[[0 1]
 [2 3]]
[[4 5]
 [6 7]]
np.vstack( (a, b) ) # Apilación vertical
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7]])
np.hstack( (a, b) ) # Apilación horizontal
array([[0, 1, 4, 5],
       [2, 3, 6, 7]])
x = np.arange(1,25,1).reshape(6,4)
x
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12],
       [13, 14, 15, 16],
       [17, 18, 19, 20],
       [21, 22, 23, 24]])
np.hsplit(x, 2) # División vertical en dos arreglos
[array([[ 1,  2],
        [ 5,  6],
        [ 9, 10],
        [13, 14],
        [17, 18],
        [21, 22]]),
 array([[ 3,  4],
        [ 7,  8],
        [11, 12],
        [15, 16],
        [19, 20],
        [23, 24]])]
np.vsplit(x, 2) #  División horizontal en dos arreglos
[array([[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]]),
 array([[13, 14, 15, 16],
        [17, 18, 19, 20],
        [21, 22, 23, 24]])]

Se recomienda revisar la función np.concatenate para ver más opciones

Agregando dimensiones al arreglo#

x = np.arange(1,11,1.)
info_array(x)
x
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 1 
 shape : (10,) 
 size(bytes) : 8 
 size(elements) : 10
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
x.T
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])
x_row = x[np.newaxis, :]
info_array(x_row)
x_row
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 2 
 shape : (1, 10) 
 size(bytes) : 8 
 size(elements) : 10
array([[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]])
x_row.T
array([[ 1.],
       [ 2.],
       [ 3.],
       [ 4.],
       [ 5.],
       [ 6.],
       [ 7.],
       [ 8.],
       [ 9.],
       [10.]])
x_col = x[:, np.newaxis]
info_array(x_col)
x_col
 tipo  : <class 'numpy.ndarray'> 
 dtype : float64 
 dim   : 2 
 shape : (10, 1) 
 size(bytes) : 8 
 size(elements) : 10
array([[ 1.],
       [ 2.],
       [ 3.],
       [ 4.],
       [ 5.],
       [ 6.],
       [ 7.],
       [ 8.],
       [ 9.],
       [10.]])
# Otra manera
x_row = np.expand_dims(x, axis=0)
x_col = np.expand_dims(x, axis=1)
print(x_row)
print(x_col)
[[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]]
[[ 1.]
 [ 2.]
 [ 3.]
 [ 4.]
 [ 5.]
 [ 6.]
 [ 7.]
 [ 8.]
 [ 9.]
 [10.]]

Constantes#

np.e
2.718281828459045
np.euler_gamma # Euler–Mascheroni constant
0.5772156649015329
np.pi
3.141592653589793
np.inf # Infinito
inf
#Por ejemplo
np.array([1]) / 0.
/tmp/ipykernel_24865/3182226453.py:2: RuntimeWarning: divide by zero encountered in divide
  np.array([1]) / 0.
array([inf])
np.nan # Not a Number: Valor no definido o no representable
nan
# Por ejemplo
np.sqrt(-1)
/tmp/ipykernel_24865/1910582514.py:2: RuntimeWarning: invalid value encountered in sqrt
  np.sqrt(-1)
nan
np.log([-1, 1, 2])
/tmp/ipykernel_24865/55426429.py:1: RuntimeWarning: invalid value encountered in log
  np.log([-1, 1, 2])
array([       nan, 0.        , 0.69314718])
np.NINF  # Infinito negativo
-inf
# Por ejemplo
np.array([-1]) / 0.
/tmp/ipykernel_24865/3889258382.py:2: RuntimeWarning: divide by zero encountered in divide
  np.array([-1]) / 0.
array([-inf])
np.NZERO # Cero negativo
-0.0
np.PZERO # Cero positivo
0.0

Exportando e importando arreglos a archivos#

x = np.arange(1,25,1.0).reshape(6,4)
print(x)
np.savetxt('arreglo.csv', x, fmt='%.2f', delimiter=',', header='1,  2,  3,  4')
[[ 1.  2.  3.  4.]
 [ 5.  6.  7.  8.]
 [ 9. 10. 11. 12.]
 [13. 14. 15. 16.]
 [17. 18. 19. 20.]
 [21. 22. 23. 24.]]
xf = np.loadtxt('arreglo.csv', delimiter=',')
xf
array([[ 1.,  2.,  3.,  4.],
       [ 5.,  6.,  7.,  8.],
       [ 9., 10., 11., 12.],
       [13., 14., 15., 16.],
       [17., 18., 19., 20.],
       [21., 22., 23., 24.]])
#Usando la biblioteca Pandas
import pandas as pd
df = pd.DataFrame(x)
df
0 1 2 3
0 1.0 2.0 3.0 4.0
1 5.0 6.0 7.0 8.0
2 9.0 10.0 11.0 12.0
3 13.0 14.0 15.0 16.0
4 17.0 18.0 19.0 20.0
5 21.0 22.0 23.0 24.0
df.to_csv('arreglo_PD.csv')
y = pd.read_csv('arreglo_PD.csv')
y
Unnamed: 0 0 1 2 3
0 0 1.0 2.0 3.0 4.0
1 1 5.0 6.0 7.0 8.0
2 2 9.0 10.0 11.0 12.0
3 3 13.0 14.0 15.0 16.0
4 4 17.0 18.0 19.0 20.0
5 5 21.0 22.0 23.0 24.0