Taller de numpy
Contents
Taller de numpy#
¿Qué es?#
Numpy es un paquete de python dedicado principalmente al manejo de conjuntos o “arrays”. Extendiendose luego al álgebra lineal, matrices y probabilidad.
¿Por qué usar arrays de numpy y no listas o tuplas de python?#
Aunque python base tiene listas, el usar arrays es más rápido y consume menos memoria. Algo altamente necesario cuando se hacen operaciones de gran tamaño como puede ser el algebra matricial, por ejemplo.
import numpy as np
Creación básica de arrays#
Vamos a crear arrays de distintas dimensiones. Para esto podemos usar números, listas o tuplas.
a=np.array(13) #array de dimensión 0
b=np.array([1,2,3,4]) #array de dimensión 1
c=np.array([[1,2,3,4],[5,6,7,8]]) #array de dimensión 2
d=np.array([[[1,2,3,4],[5,6,7,8]],[[9,10,11,12],[13,14,15,16]]]) #array de dimensión 3
print("a =",a,"\n")
print("b =",b,"\n")
print("c =",c,"\n")
print("d =",d,"\n")
a = 13
b = [1 2 3 4]
c = [[1 2 3 4]
[5 6 7 8]]
d = [[[ 1 2 3 4]
[ 5 6 7 8]]
[[ 9 10 11 12]
[13 14 15 16]]]
Para revisar el número de dimensiones, usamos ndim
print(a.ndim)
print(b.ndim)
print(c.ndim)
print(d.ndim)
0
1
2
3
Indexación básica#
El proceso de indexación en numpy es similar al de las listas en python. Cada número implica buscar la posición en cada dimensión del array
print("a =",a)
print("b[2] = ",b[2])
print("c[1] =",c[1])
print("d[1] =",d[1])
a = 13
b[2] = 3
c[1] = [5 6 7 8]
d[1] = [[ 9 10 11 12]
[13 14 15 16]]
print("a =",a)
print("b[2] =",b[2])
print("c[1,2] =",c[1,2])
print("d[1,0,2] =",d[1,0,2])
a = 13
b[2] = 3
c[1,2] = 7
d[1,0,2] = 11
Similarmente podemos segmentar arrays como en el las listas de python,
print(b[1:4]) #b = [1 2 3 4]
print(c[1,1:3]) #c = [[1 2 3 4][5 6 7 8]]
[2 3 4]
[6 7]
arrays a partir de secuencias#
Es posible crear arrays de una forma similar a la función range()
. Y depende de los argumentos se obtendrán cosas distintas.
Solo es posible en este caso hacer arrays de dimensión 1
int_sec_1 = np.arange(5) #un argumento: desde 0 hasta el argumento de uno en uno
print(int_sec_1)
int_sec_2 = np.arange(5, 10) #dos argumentos: desde arg1 hasta arg2 de uno en uno
print(int_sec_2)
int_sec_3 = np.arange(10, 30, 5) #tres argumentos: desde arg1 hasta arg2 de arg3 en arg3
print(int_sec_3)
[0 1 2 3 4]
[5 6 7 8 9]
[10 15 20 25]
También se pueden hacer secuencias con números de punto flotante, pero hay que tener cuidado ya que actúan distinto
print(np.linspace(2,2.5)) # aquí es obligatorio un inicio y un final, por default se hace una lista de 50
print(np.linspace(2,2.5, 5))
[2. 2.01020408 2.02040816 2.03061224 2.04081633 2.05102041
2.06122449 2.07142857 2.08163265 2.09183673 2.10204082 2.1122449
2.12244898 2.13265306 2.14285714 2.15306122 2.16326531 2.17346939
2.18367347 2.19387755 2.20408163 2.21428571 2.2244898 2.23469388
2.24489796 2.25510204 2.26530612 2.2755102 2.28571429 2.29591837
2.30612245 2.31632653 2.32653061 2.33673469 2.34693878 2.35714286
2.36734694 2.37755102 2.3877551 2.39795918 2.40816327 2.41836735
2.42857143 2.43877551 2.44897959 2.45918367 2.46938776 2.47959184
2.48979592 2.5 ]
[2. 2.125 2.25 2.375 2.5 ]
Diferentes tipos de datos en los arrays#
Los arrays no están limitados a números enteros, pueden también tener cadenas, booleanos, o de punto flotante.
frutas = np.array(['Manzana', 'Naranja', 'Uva']) #cadenas
print(frutas.dtype)
<U7
Podemos manipular el tipo de los datos del array dentro de la función de creación del array np.array
, siempre y cuando el cambio sea posible. Por ejemplo podemos pasar enteros a cadenas (integers a strings) pero no viceversa.
number_to_string=np.array([6,1,2,4,623,8], dtype='S') #b denota un string
print(number_to_string.dtype)
print(number_to_string)
|S3
[b'6' b'1' b'2' b'4' b'623' b'8']
try:
error_array = np.array(['a', '2', '3'], dtype='i')
except:
print("hay un error en su lógica")
hay un error en su lógica
Podemos también cambiar el tipo en arrays ya existentes
floating_array = np.array([1.1, 2.1, 3.1]) #punto flotante
int_array = floating_array.astype('i')
print(int_array)
[1 2 3]
Forma de los arrays#
La forma es distinta a la dimensión. Esta se define como el número de elementos de la dimensión.
print(a.shape) # a = 13
print(b.shape) # b = [1 2 3 4]
print(c.shape) # c = [[1 2 3 4][5 6 7 8]]
print(d.shape) # d = [[[ 1 2 3 4][ 5 6 7 8]] [[ 9 10 11 12][13 14 15 16]]]
()
(4,)
(2, 4)
(2, 2, 4)
Reformar arrays#
Podemos cambiar la forma de los arrays. Esto significa aumentar el número de dimensiones o cambiar cuantos elementos hay por dimensión.
print("c =",c,"\n")
c_1D=c.reshape(1,8)
print("c_1D =",c_1D,"\n")
c_4D=c.reshape(4,2)
print("c_4D =",c_4D,"\n")
c = [[1 2 3 4]
[5 6 7 8]]
c_1D = [[1 2 3 4 5 6 7 8]]
c_4D = [[1 2]
[3 4]
[5 6]
[7 8]]
¿Podemos hacer cualquier cambio en la forma? Si, mientras las que se quieren conseguir coincidan con la cantidad de elementos.
print(d)
[[[ 1 2 3 4]
[ 5 6 7 8]]
[[ 9 10 11 12]
[13 14 15 16]]]
d
es un array con 16 elementos, así que podemos hacer reshape con tamaños por ejemplo 1x16, 4x4, 8x2, 2x4x2 Por ejemplo, no podemos reformar un arrey a 3x5.
try:
d.reshape(3,5)
except:
print('Hay un error en sus operaciones')
Hay un error en sus operaciones
d.reshape(2,2,2,2)
array([[[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]]],
[[[ 9, 10],
[11, 12]],
[[13, 14],
[15, 16]]]])
Dimensión desconocida#
Podemos definir el cambio de forma con una dimensión la cual no sabemos el tamaño. Numpy se encargará de definir el tamaño que mejor se ajuste.
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
arr.reshape(3,-1)
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
Podemos usar la dimensión desconocida para “aplanar” el array a una dimensión.
d.reshape(-1)
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
Transpuesta#
Para transponer el array utilizamos .T
print("c =",c,"\n")
print("c.T =",c.T)
c = [[1 2 3 4]
[5 6 7 8]]
c.T = [[1 5]
[2 6]
[3 7]
[4 8]]
Iterar arrays#
Podemos usar los métodos tradicionales para iterar.
for x in d:
print(x)
[[1 2 3 4]
[5 6 7 8]]
[[ 9 10 11 12]
[13 14 15 16]]
Si queremos iterar cada elemento, necesitamos un for
por cada dimensión
for x in d:
for y in x:
for z in y:
print(z)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Esto por supuesto no es óptimo. Así que numpy tiene la función nditer()
para estos casos.
for x in np.nditer(d):
print(x)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Aplicar operaciones entre arrays#
numpy tiene sus propias formas de aplicar operaciones:
Suma#
numpy tiene dos funciones, np.add()
y np.sum()
. El primero se aplica entre argumentos, el segundo se aplica en n elementos para que se haga la suma, es necesario que ambos arrays tengan la misma forma
arr1=np.array([[1,2,3],
[4,5,6]])
arr2=np.array([[6,5,4],
[3,2,1]])
np.add(arr1,arr2)
array([[7, 7, 7],
[7, 7, 7]])
arr3=np.array([[1,1,1],[1,1,1]])
print(np.sum([arr1,arr2]))
print(np.sum([arr1,arr2,arr3]))
42
48
Si aplicamos un eje en np.sum()
, se aplica la suma en ese mismo eje. Por ejemplo, con eje 0, se obtienen los mismos resultados que np.add()
np.sum([arr1,arr2, arr3],axis=0)
array([[8, 8, 8],
[8, 8, 8]])
np.sum([arr1,arr2, arr3],axis=1)
array([[5, 7, 9],
[9, 7, 5],
[2, 2, 2]])
Multiplicación#
El primer caso de multiplicación es np.prod()
, que funciona similar que np.sum()
print("arr1=\n",arr1,"\n")
print("arr2=\n",arr2,"\n")
print(np.prod([arr1,arr2]),"\n")
print(np.prod([arr1,arr2],axis=0),"\n")
print(np.prod([arr1,arr2],axis=1))
arr1=
[[1 2 3]
[4 5 6]]
arr2=
[[6 5 4]
[3 2 1]]
518400
[[ 6 10 12]
[12 10 6]]
[[ 4 10 18]
[18 10 4]]
Multiplicación Matricial#
Para la multiplicación de matrices usamos np.matmul()
o np.dot()
. hay que tener en cuenta la forma de las matrices
np.matmul(arr1, arr2.T)
array([[28, 10],
[73, 28]])
np.dot(arr1, arr2.T)
array([[28, 10],
[73, 28]])
Se comportan distinto con matrices de dos dimensiones, para otros casos hacen cosas distintas
print("d =\n",d,"\n") #2,2,4
print("d.reshape =\n",d.reshape(2,4,2),"\n") #2,4,2
np.dot(d,d.reshape(2,4,2)) #2,2,2,2
d =
[[[ 1 2 3 4]
[ 5 6 7 8]]
[[ 9 10 11 12]
[13 14 15 16]]]
d.reshape =
[[[ 1 2]
[ 3 4]
[ 5 6]
[ 7 8]]
[[ 9 10]
[11 12]
[13 14]
[15 16]]]
array([[[[ 50, 60],
[130, 140]],
[[114, 140],
[322, 348]]],
[[[178, 220],
[514, 556]],
[[242, 300],
[706, 764]]]])
print(np.dot(d[0],d.reshape(2,4,2)[0]),"\n")
print(np.dot(d[0],d.reshape(2,4,2)[1]),"\n")
print(np.dot(d[1],d.reshape(2,4,2)[0]),"\n")
print(np.dot(d[1],d.reshape(2,4,2)[1]),"\n")
[[ 50 60]
[114 140]]
[[130 140]
[322 348]]
[[178 220]
[242 300]]
[[514 556]
[706 764]]
Con matmul no se pueden multiplicar escalares c.A (c escalar, A matriz), además Matmul hace las multiplicaciones matriz a matriz (matrices como elementos), de ahí la diferencia con dot, donde este toma los números dentro de las matrices como elementos.
np.matmul(d,d.reshape(2,4,2))
array([[[ 50, 60],
[114, 140]],
[[514, 556],
[706, 764]]])
Filtros#
Con numpy es posible filtrar elementos del array para así crear elementos nuevos. Se puede hacer a partir de “indices booleanos”
arr = np.array([41, 42, 43, 44])
new_arr = arr[arr > 42]
print(new_arr)
[43 44]
Esto se puede hacer en arrays de cualquier dimensión y tamaño, pero el resultado siempre será de dimensión 1, así que recuerden usar reshape(si es posible)
arr = np.arange(10).reshape(2,-1)
print(arr)
new_arr = arr[arr % 2==1]
print(new_arr)
try:
new_arr = new_arr.reshape(2,-1)
print(new_arr)
except:
print("no es posible manipular tamaño")
[[0 1 2 3 4]
[5 6 7 8 9]]
[1 3 5 7 9]
no es posible manipular tamaño
Generación “Aleatoria” básica#
en la forma más básica, numpy tiene la función random
para generar números aleatorios
from numpy import random
x = random.randint(100) #caso entero
print(x)
y = random.rand() #caso flotante entre 0 y 1
print(y)
22
0.5411898246091135
Extendamos esto a arrays aleatorios
x = random.randint(100, size = (5))
print(x)
x = random.randint(100, size=(2,3))
print(x)
[51 89 36 26 43]
[[18 21 27]
[52 34 90]]
x = random.rand(5)
print(x)
x = random.rand(2,3)
print(x)
[0.94558385 0.13040768 0.48297511 0.04325369 0.72284027]
[[0.91861366 0.41791858 0.94285491]
[0.75964072 0.23433761 0.13807193]]