Python es conveniente y flexible, pero notablemente más lento que otros lenguajes en cuanto a velocidad computacional bruta. El ecosistema Python lo ha compensado con herramientas que hacen que procesar números a escala en Python sea rápido y conveniente.
NumPy es una de las herramientas Python más comunes que utilizan los desarrolladores y científicos de datos para ayudar con la computación a escala. Proporciona bibliotecas y técnicas para trabajar con arreglos y matrices, todo respaldado por código escrito en lenguajes de alta velocidad como C, C++ y Fortran. Y todas las operaciones de NumPy se llevan a cabo afuera el tiempo de ejecución de Python, por lo que no están restringidos por las limitaciones de Python.
Usando NumPy para matemáticas de matrices y matrices en Python
Muchas operaciones matemáticas, especialmente en aprendizaje automático o Ciencia de los datosimplica trabajar con matrices, o listas de números. La forma ingenua de hacerlo en Python es almacenar los números en una estructura, normalmente una Python. list
, luego recorra la estructura y realice una operación en cada elemento de la misma. Esto es lento e ineficiente, ya que cada elemento debe traducirse de un objeto Python a un número nativo de la máquina.
NumPy proporciona un tipo de matriz especializado que está optimizado para funcionar con tipos numéricos nativos de la máquina, como enteros o flotantes. Las matrices pueden tener cualquier número de dimensiones, pero cada matriz utiliza un tipo de datos uniforme, o tipo dpara representar sus datos subyacentes.
He aquí un ejemplo sencillo:
import numpy as np
np.array([0, 1, 2, 3, 4, 5, 6])
Esto crea una matriz NumPy unidimensional a partir de la lista proporcionada. No especificamos un dtype
para esta matriz, por lo que se infiere automáticamente a partir de los datos proporcionados que será un entero con signo de 32 o 64 bits (según la plataforma). Si quisiéramos ser explícitos sobre el tipo d, podríamos hacer esto:
np.array([0, 1, 2, 3, 4, 5, 6], dtype=np.uint32)
np.uint32
es, como su nombre lo indica, el dtype
para un entero de 32 bits sin signo.
Él es Es posible utilizar objetos Python genéricos como dtype
para una matriz NumPy, pero si hace esto, no obtendrá mejor rendimiento con NumPy que con Python en general. NumPy funciona mejor para nativo de la máquina tipos numéricos (int
s, float
s) en lugar de Nativo de Python tipos (números complejos, los Decimal
tipo).
Cómo NumPy acelera las matemáticas de matrices en Python
Una gran parte de la velocidad de NumPy proviene del uso de tipos de datos nativos de la máquina, en lugar de los tipos de objetos de Python. Pero la otra gran razón por la que NumPy es rápido es porque proporciona formas de trabajar con matrices sin tener que abordar cada elemento individualmente.
Los arreglos NumPy tienen muchos de los comportamientos de los objetos convencionales de Python, por lo que es tentador usar metáforas comunes de Python para trabajar con ellos. Si quisiéramos crear una matriz NumPy con los números del 0 al 1000, en teoría podríamos hacer esto:
x = np.array([_ for _ in range(1000)])
Esto funciona, pero su rendimiento se ve limitado por el tiempo que le toma a Python crear una lista y a NumPy convertir esa lista en una matriz.
Por el contrario, podemos hacer lo mismo de manera mucho más eficiente dentro del propio NumPy:
x = np.arange(1000)
Puede utilizar muchos otros tipos de operaciones integradas de NumPy para creando nuevas matrices sin bucles: crear matrices de ceros (o cualquier otro valor inicial) o utilizar un conjunto de datos, un búfer u otra fuente existente.
Otra forma clave en la que NumPy acelera las cosas es proporcionando formas de no tener que abordar los elementos de la matriz individualmente para trabajar en ellos a escala.
Como se señaló anteriormente, las matrices NumPy se comportan de manera muy similar a otros objetos de Python, por conveniencia. Por ejemplo, pueden ser indexado listas similares; arr[0]
accede al primer elemento de una matriz NumPy. Esto le permite configurar o leer elementos individuales en una matriz.
Sin embargo, si desea modificar todos los elementos de una matriz, es mejor que utilice las funciones de «difusión» de NumPy: formas de ejecutar operaciones en una matriz completa, o en una porción, sin realizar bucles en Python. Nuevamente, esto es para que todo el trabajo sensible al rendimiento se pueda realizar en el propio NumPy.
He aquí un ejemplo:
x1 = np.array(
[np.arange(0, 10),
np.arange(10,20)]
)
Esto crea una matriz NumPy bidimensional, cada dimensión de la cual consta de un rango de números. (Podemos crear matrices de cualquier número de dimensiones simplemente usando listas anidadas en el constructor).
[[ 0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]]
Si quisiéramos transponer los ejes de esta matriz en Python, necesitaríamos escribir un bucle de algún tipo. NumPy nos permite realizar este tipo de operaciones con un solo comando:
x2 = np.transpose(x1)
La salida:
[[ 0 10]
[ 1 11]
[ 2 12]
[ 3 13]
[ 4 14]
[ 5 15]
[ 6 16]
[ 7 17]
[ 8 18]
[ 9 19]]
Operaciones como estas son la clave para utilizar bien NumPy. NumPy ofrece un amplio catálogo de rutinas incorporadas para manipular datos de matriz. Incorporados para álgebra lineal, transformadas discretas de Fouriery generadores de números pseudoaleatorios le evitará la molestia de tener que enrollar esas cosas usted mismo también. En la mayoría de los casos, puede lograr lo que necesita con una o más funciones integradas, sin utilizar operaciones de Python.
Funciones universales NumPy (ufuncs)
Otro conjunto de características que ofrece NumPy que le permiten realizar técnicas de cálculo avanzadas sin bucles de Python se llama funciones universaleso ufunc
s para abreviar. ufunc
Tomemos una matriz, realicemos alguna operación en cada elemento de la matriz y enviemos los resultados a otra matriz o realicemos la operación en el lugar.
Un ejemplo:
x1 = np.arange(1, 9, 3)
x2 = np.arange(2, 18, 6)
x3 = np.add(x1, x2)
Aquí, np.add
toma cada elemento de x1
y lo agrega a x2
con los resultados almacenados en una matriz recién creada, x3
. Esto produce [ 3 12 21]
. Todo el cálculo real se realiza en el propio NumPy.
ufunc
También tenemos métodos de atributos que le permiten aplicarlos de manera más flexible y reducir la necesidad de bucles manuales o lógica del lado de Python. Por ejemplo, si quisiéramos tomar x1
y use np.add
para sumar la matriz, podríamos usar el .add
método np.add.accumulate(x1)
en lugar de recorrer cada elemento de la matriz para crear una suma.
Del mismo modo, digamos que queremos realizar una función de reducción, es decir, aplicar .add
a lo largo de un eje de una matriz multidimensional, y el resultado es una nueva matriz con una dimensión menos. Podríamos realizar un bucle y crear una nueva matriz, pero sería lento. O podríamos usar np.add.reduce
para lograr lo mismo sin bucle:
x1 = np.array([[0,1,2],[3,4,5]])
# [[0 1 2] [3 4 5]]
x2 = np.add.reduce(x1)
# [3 5 7]
También podemos realizar reducciones condicionales, utilizando un where
argumento:
x2 = np.add.reduce(x1, where=np.greater(x1, 1))
esto volveria x1+x2
pero sólo en los casos en que los elementos en x1
El primer eje es mayor que 1
; de lo contrario, simplemente devuelve el valor de los elementos en el segundo eje. Nuevamente, esto nos evita tener que iterar manualmente sobre la matriz en Python. NumPy proporciona mecanismos como este para filtrar y ordenar datos según algún criterio, de modo que no tengamos que escribir bucles (o al menos, los bucles que hacer escritura se mantienen al mínimo.
NumPy y Cython: uso de NumPy con C
El Biblioteca Cython en Python te permite escribir código Python y convertirlo a C para mayor velocidad, usando tipos C para variables. Esas variables pueden incluir matrices NumPy, por lo que cualquier código Cython que escriba puede trabajar directamente con matrices NumPy.
El uso de Cython con NumPy confiere algunas características poderosas:
- Acelerar bucles manuales: A veces no tienes más remedio que recorrer una matriz NumPy. Escribir la operación de bucle en un módulo Cython proporciona una forma de realizar el bucle en C, en lugar de Python, y por lo tanto permite aceleraciones espectaculares. Tenga en cuenta que esto solo es posible si los tipos de todas las variables en cuestión son matrices NumPy o tipos C nativos de la máquina.
- Usando matrices NumPy con bibliotecas C: Un caso de uso común para Cython es escribir contenedores Python convenientes para bibliotecas C. El código Cython puede actuar como un puente entre una biblioteca C existente y los arreglos NumPy.
Cython permite dos formas de trabajar con matrices NumPy. Uno es a través de un vista de memoria escrita, una construcción de Cython para un acceso rápido y seguro a una matriz NumPy. Otra es obtener un puntero sin procesar a los datos subyacentes y trabajar con ellos directamente, pero esto tiene el costo de ser potencialmente inseguro y requerir que conozca de antemano el diseño de la memoria del objeto.
NumPy y Numba: código Python con aceleración JIT para NumPy
Otra forma de utilizar Python de forma eficaz con matrices NumPy es utilizar Numba, un compilador JIT para Python. Numba traduce código interpretado por Python a código nativo de máquina, con especializaciones para cosas como NumPy. Los bucles en Python sobre matrices NumPy se pueden optimizar automáticamente de esta manera. Pero las optimizaciones de Numba sólo son automáticas hasta cierto punto y es posible que no manifiesten mejoras significativas en el rendimiento de todos los programas.
Copyright © 2024 IDG Communications, Inc.