Numpy¶
Introducción¶
Numpy s una biblioteca de Python que se utiliza para trabajar con matrices y vectores de datos numéricos. Proporciona un conjunto de funciones y métodos eficientes y optimizados para el procesamiento de datos numéricos, incluyendo operaciones matriciales, estadísticas, álgebra lineal, entre otras.
NumPy es ampliamente utilizado en áreas como la ciencia de datos, el aprendizaje automático, la ingeniería y la física, entre otras. Es una herramienta fundamental en el ecosistema de Python para el procesamiento y análisis de datos numéricos.
Algunas de las características principales de NumPy son:
- Arrays multidimensionales eficientes y optimizados para operaciones numéricas.
- Funciones y métodos para operaciones matemáticas y estadísticas.
- Integración con otras bibliotecas de Python para el procesamiento de datos, como Pandas y Matplotlib.
- Capacidad de procesar grandes conjuntos de datos numéricos de manera eficiente y escalable.
Python Lists vs Numpy Arrays¶
Las listas de Python y los arrays de NumPy son dos estructuras de datos diferentes que se utilizan para almacenar y manipular conjuntos de datos en Python.
Las listas de Python son una colección de elementos que pueden ser de diferentes tipos de datos, como enteros, flotantes, cadenas, etc. Pueden ser de longitud variable y se pueden modificar en tiempo de ejecución. Las listas de Python son más flexibles y versátiles que los arrays de NumPy, pero pueden ser más lentas para operaciones matemáticas y numéricas.
Los arrays de NumPy son una estructura de datos más especializada que se utiliza para trabajar con datos numéricos, como matrices y vectores. Son más rápidos y eficientes que las listas de Python para operaciones numéricas y matemáticas, y proporcionan muchas funciones y métodos útiles para el procesamiento de datos, como operaciones matriciales y de álgebra lineal. Los arrays de NumPy tienen una longitud fija y no se pueden modificar una vez creados.
Una pregunta común para principiantes es cuál es la verdadera diferencia aquí. La respuesta es el rendimiento. Las estructuras de datos de Numpy funcionan mejor en:
- Tamaño: las estructuras de datos de Numpy ocupan menos espacio
- Rendimiento: necesitan velocidad y son más rápidos que las listas
- Funcionalidad: SciPy y NumPy tienen funciones optimizadas, como las operaciones de álgebra lineal integradas.
Diferencias Tiempo - Memoria
Los principales beneficios del uso de matrices NumPy deberían ser un menor consumo de memoria y un mejor comportamiento en tiempo de ejecución.
Para las listas de Python podemos concluir de esto que para cada elemento nuevo, necesitamos otros ocho bytes para la referencia al nuevo objeto. El nuevo objeto entero en sí consume 28 bytes. El tamaño de una lista lst sin el tamaño de los elementos se puede calcular con:
64 + 8 * len (lst) + + len (lst) * 28
Los array NumPy ocupan menos espacio. Esto significa que una matriz entera arbitraria de longitud n en necesidades numpy se calcula por:
96 + n * 8 bytes
Veamos un ejemplo:
import numpy as np
import time
import sys
def arreglo_python(n):
t1 = time.time()
X = range(n)
Y = range(n)
Z = [X[i] + Y[i] for i in range(len(X)) ]
return (time.time() - t1,sys.getsizeof(Z) )
def arreglo_numpy(n):
t1 = time.time()
X = np.arange(n)
Y = np.arange(n)
Z = X + Y
return (time.time() - t1,sys.getsizeof(Z) )
# generar varios casos
for size_of_vec in [10,100,1000,10000,100000,1000000]:
print(f"size of vector: {size_of_vec}")
#list vs numpy
t1, size1 = arreglo_python(size_of_vec)
t2, size2 = arreglo_numpy(size_of_vec)
# resultados
print(f"python list -- time: {round(t1,8)} seg, size: {size1} bytes")
print(f"numpy array -- time: {round(t2,8)} seg, size: {size2} bytes\n")
size of vector: 10 python list -- time: 0.0 seg, size: 184 bytes numpy array -- time: 0.0 seg, size: 152 bytes size of vector: 100 python list -- time: 0.0 seg, size: 904 bytes numpy array -- time: 0.0 seg, size: 512 bytes size of vector: 1000 python list -- time: 0.0 seg, size: 9016 bytes numpy array -- time: 0.0 seg, size: 4112 bytes size of vector: 10000 python list -- time: 0.002177 seg, size: 87616 bytes numpy array -- time: 0.00052881 seg, size: 40112 bytes size of vector: 100000 python list -- time: 0.01605535 seg, size: 824456 bytes numpy array -- time: 0.0 seg, size: 400112 bytes size of vector: 1000000 python list -- time: 0.14644384 seg, size: 8697456 bytes numpy array -- time: 0.00261927 seg, size: 4000112 bytes
Objetos en Numpy¶
En NumPy, el objeto principal es el array multidimensional (llamado ndarray
), que es una estructura de datos eficiente para el procesamiento de datos numéricos. Los arrays en NumPy son objetos homogéneos y de tamaño fijo que contienen elementos del mismo tipo de datos.
Aquí hay algunos ejemplos de objetos en NumPy:
# Crear un array 1D de longitud 3
arr_1d = np.array([1, 2, 3])
print(arr_1d)
[1 2 3]
# Crear un array 2D de tamaño 2x3
arr_2d = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(arr_2d)
[[1 2 3] [4 5 6]]
# Crear un array 3D de tamaño 2x2x3
arr_3d = np.array([
[
[1, 2, 3],
[4, 5, 6]
],
[
[7, 8, 9],
[10, 11, 12]
]
])
print(arr_3d)
[[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]]
Veamos algunos atributos de los arreglos en Numpy:
ndarray.shape
: devuelve la forma (dimensiones) del array.ndarray.ndim
: devuelve el número de dimensiones del array.ndarray.size
: devuelve el número total de elementos en el array.ndarray.dtype
: devuelve el tipo de datos de los elementos del array.
# crear objeto
obj_numpy =np.array(
[1, 2, 3, 4, 5, 6, 7, 8, 9])
# shape
obj_numpy.shape
(9,)
# ndim
obj_numpy.ndim
1
# size
obj_numpy.size
9
# dtype
obj_numpy.dtype
dtype('int32')
Operaciones¶
NumPy proporciona una variedad de operaciones básicas que se pueden realizar en los arrays, como operaciones aritméticas, operaciones de comparación y operaciones de agregación. Aquí hay algunos ejemplos de operaciones básicas en NumPy:
Operaciones aritméticas¶
import numpy as np
# Crear dos arrays
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# Suma de arrays
c = a + b
print(c)
[5 7 9]
# Resta de arrays
c = a - b
print(c)
[-3 -3 -3]
# Multiplicación de arrays
c = a * b
print(c)
[ 4 10 18]
# División de arrays
c = a / b
print(c)
[0.25 0.4 0.5 ]
Operaciones de comparación¶
# Crear dos arrays
a = np.array([1, 2, 3])
b = np.array([2, 2, 4])
# Comparación de arrays
c = a == b
print(c)
[False True False]
# Comparación de arrays
c = a > b
print(c)
[False False False]
# Comparación de arrays
c = a <= b
print(c)
[ True True True]
Operaciones de estadísticas¶
# Crear un array
a = np.array([1, 2, 3, 4, 5])
# Suma de los elementos en un array
sum_a = np.sum(a)
print(f"Suma de elementos: {sum_a}")
Suma de elementos: 15
# Mínimo y máximo de los elementos en un array
min_a = np.min(a)
max_a = np.max(a)
print(f"minimo: {min_a}")
print(f"maximo: {max_a}")
minimo: 1 maximo: 5
# Promedio y desviación estándar de los elementos en un array
mean_a = np.mean(a)
std_a = np.std(a)
print(f"promedio: {mean_a}")
print(f"desviacion estandar: {std_a}")
promedio: 3.0 desviacion estandar: 1.4142135623730951
Indexación¶
La indexación en NumPy es similar a la indexación en listas de Python, pero se extiende a múltiples dimensiones. En NumPy, los arrays se pueden indexar y rebanar para acceder a elementos y subarrays específicos. Aquí hay algunos ejemplos de indexación en NumPy:
Acceder a elementos individuales de un array unidimensional
# Crear un array unidimensional
a = np.array([1, 2, 3, 4, 5])
# Acceder al primer elemento
print(a[0])
1
# Acceder al último elemento
print(a[-1])
5
# Acceder al tercer elemento
print(a[2])
3
Acceder a elementos individuales de un array multidimensional
# Crear un array bidimensional
b = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
)
# Acceder al primer elemento
print(b[0, 0])
1
# Acceder al último elemento
print(b[-1, -1])
9
# Acceder al elemento en la segunda fila y tercer columna
print(b[1, 2])
6
Rebanar un array unidimensional
# Rebanar un array unidimensional
a = np.array([1, 2, 3, 4, 5])
# Rebanar los primeros tres elementos
print(a[:3])
[1 2 3]
# Rebanar los últimos dos elementos
print(a[-2:])
[4 5]
# Rebanar todos los elementos saltando de dos en dos
print(a[::2])
[1 3 5]
Rebanar un array multidimensional
# Rebanar un array bidimensional
b = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
)
# Rebanar la primera y segunda fila
print(b[:2, :])
[[1 2 3] [4 5 6]]
# Rebanar la segunda y tercera columna
print(b[:, 1:])
[[2 3] [5 6] [8 9]]
# Rebanar la diagonal principal
print(b.diagonal())
[1 5 9]
Algebra Lineal¶
NumPy es una biblioteca popular en Python para el álgebra lineal, que es una rama de las matemáticas que se enfoca en el estudio de vectores, matrices y sistemas de ecuaciones lineales. NumPy proporciona una gran cantidad de funciones y métodos para realizar operaciones de álgebra lineal de manera eficiente en Python.
Aquí hay algunos ejemplos de operaciones de álgebra lineal que se pueden realizar con NumPy:
Vectores/Matrices especializadas
# Arreglo de ceros: np.zeros(shape)
print("Zeros:")
print( np.zeros((3,3)) )
Zeros: [[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]]
# Arreglos de uno: np.ones(shape)
print("\nOnes:")
print( np.ones((3,3)) )
Ones: [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]]
# Arreglo vacio: np.empty(shape)
print("\nEmpty:")
print( np.empty([2, 2]) )
Empty: [[2.12199579e-314 4.67296746e-307] [7.09478267e-321 3.79442416e-321]]
# Rango de valores: np.range(start, stop, step)
print("\nRange:")
np.arange(0., 10., 1.)
Range:
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
# Grilla de valores: np.linspace(start, end, n_values)
print("\nRegular grid:")
print( np.linspace(0., 1., 9) )
Regular grid: [0. 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1. ]
# fijar semilla
np.random.seed(42)
# Sequencia de valores aleatorios: np.random
print("\nRandom sequences:")
print( np.random.uniform(10, size=6) )
Random sequences: [6.62913893 1.44357124 3.41205452 4.61207364 8.59583224 8.59604932]
Operaciones con Matrices
# crear matrices
A = np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
B = np.array([
[9,8,7],
[6,5,4],
[3,2,1]]
)
print(f"Matrix A: \n {A}\n")
print(f"Matrix B: \n {B}")
Matrix A: [[1 2 3] [4 5 6] [7 8 9]] Matrix B: [[9 8 7] [6 5 4] [3 2 1]]
# sumar dos matrices
print("Sum:")
print( A+B )
Sum: [[10 10 10] [10 10 10] [10 10 10]]
# restar dos matrices
print("\nSubtraction")
print( A-B )
Subtraction [[-8 -6 -4] [-2 0 2] [ 4 6 8]]
# producto uno a uno
print("\nProduct")
print( A*B )
Product [[ 9 16 21] [24 25 24] [21 16 9]]
# producto matricial
print("\nMatricial Product")
print( np.dot(A,B) )
Matricial Product [[ 30 24 18] [ 84 69 54] [138 114 90]]
# potencia
print("\n Power")
print( A**2 )
Power [[ 1 4 9] [16 25 36] [49 64 81]]
Algunas funciones especiales
# crear matriz
A = np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
print("funcion exponencial")
print( np.exp(A) )
funcion exponencial [[2.71828183e+00 7.38905610e+00 2.00855369e+01] [5.45981500e+01 1.48413159e+02 4.03428793e+02] [1.09663316e+03 2.98095799e+03 8.10308393e+03]]
print("funcion seno")
print( np.sin(A) )
funcion seno [[ 0.84147098 0.90929743 0.14112001] [-0.7568025 -0.95892427 -0.2794155 ] [ 0.6569866 0.98935825 0.41211849]]
print("funcion coseno")
print( np.cos(A))
funcion coseno [[ 0.54030231 -0.41614684 -0.9899925 ] [-0.65364362 0.28366219 0.96017029] [ 0.75390225 -0.14550003 -0.91113026]]
print("funcion tangente")
print( np.tan(A) )
funcion tangente [[ 1.55740772 -2.18503986 -0.14254654] [ 1.15782128 -3.38051501 -0.29100619] [ 0.87144798 -6.79971146 -0.45231566]]
Operaciones álgebra líneal
# crear matriz
A = np.array([[1,2],
[3,4]])
# matriz transpuesta
print("Transpose: ")
print( A.T )
Transpose: [[1 3] [2 4]]
# determinante
print("determinant")
print( round(np.linalg.det(A) ,2))
determinant -2.0
# Matriz Inversa
print("Inverse")
print( np.linalg.inv(A) )
Inverse [[-2. 1. ] [ 1.5 -0.5]]
# traza
print("Trace")
print( np.trace(A))
Trace 5
# Valores y vectores propios
eigenvalues, eigenvectors = np.linalg.eig(A)
print("eigenvalues")
print( eigenvalues )
print("\neigenvectors")
print( eigenvectors )
eigenvalues [-0.37228132 5.37228132] eigenvectors [[-0.82456484 -0.41597356] [ 0.56576746 -0.90937671]]
# Valores y vectores propios
eigenvalues, eigenvectors = np.linalg.eig(A)
print("eigenvalues")
print( eigenvalues )
print("\neigenvectors")
print( eigenvectors )
eigenvalues [-0.37228132 5.37228132] eigenvectors [[-0.82456484 -0.41597356] [ 0.56576746 -0.90937671]]
Resolver sitema de ecuaciones
# crear matriz
A = np.array([[1,2],
[3,4]])
# sistemas lineales: Ax = b
b = np.array([[5.], [7.]])
print("linear system: Ax=b")
print("\nx = ")
print( np.linalg.solve(A, b) )
linear system: Ax=b x = [[-3.] [ 4.]]
Broadcasting¶
Broadcasting es un término utilizado en NumPy para describir la forma en que las operaciones entre arrays con diferentes formas se manejan automáticamente por NumPy. En otras palabras, cuando se realizan operaciones aritméticas entre dos arrays de diferentes formas, NumPy ajusta automáticamente la forma del array más pequeño para que coincida con la forma del array más grande antes de realizar la operación. Esto permite que las operaciones se realicen de manera eficiente sin la necesidad de crear copias adicionales de los datos.
Las reglas de broadcasting en NumPy son las siguientes:
Si los dos arrays tienen el mismo número de dimensiones, pero las formas no son iguales, NumPy agrega 1 a la forma del array más pequeño para que coincida con la forma del array más grande.
Si la forma de los dos arrays no es la misma y no tienen el mismo número de dimensiones, NumPy agrega 1 a las dimensiones menos y expande las formas de los arrays para que sean iguales en la dimensión más alta.
Si la forma de los dos arrays no es la misma y alguna dimensión no coincide, NumPy genera un error.
# example 01
a = np.arange(3)+ 5
print(f"np.arange(3)+ 5:\n{a}" )
np.arange(3)+ 5: [5 6 7]
# example 02
b = np.ones((3,3))+np.arange(3)
print(f"np.ones((3,3))+np.arange(3):\n{b}" )
np.ones((3,3))+np.arange(3): [[1. 2. 3.] [1. 2. 3.] [1. 2. 3.]]
# example 03
c = np.arange(3).reshape((3,1)) + np.arange(3)
print(f"np.arange(3).reshape((3,1)) + np.arange(3):\n{c }" )
np.arange(3).reshape((3,1)) + np.arange(3): [[0 1 2] [1 2 3] [2 3 4]]