python - transpuesta - numpy tutorial español pdf
Cython: ¿las vistas de memoria escritas son la forma moderna de escribir matrices numpy? (1)
Digamos que me gustaría pasar una matriz numpy a una función cdef
:
cdef double mysum(double[:] arr):
cdef int n = len(arr)
cdef double result = 0
for i in range(n):
result = result + arr[i]
return result
¿Es esta la forma moderna de manejar la numeración de arrays? Compare con esta pregunta: tipo cython / numpy de una matriz
¿Qué pasa si quiero hacer lo siguiente?
cdef double[:] mydifference(int a, int b):
cdef double[:] arr_a = np.arange(a)
cdef double[:] arr_b = np.arange(b)
return arr_a - arr_b
Esto devolverá un error porque -
no está definido para las vistas de memoria. Entonces, ¿ese caso debería haber sido manejado de la siguiente manera?
cdef double[:] mydifference(int a, int b):
arr_a = np.arange(a)
arr_b = np.arange(b)
return arr_a - arr_b
Citaré de los documentos los documentos.
Las vistas de memoria son similares al soporte de búfer de matriz NumPy actual (
np.ndarray[np.float64_t, ndim=2]
), pero tienen más características y una sintaxis más clara.
Esto indica que los desarrolladores de Cython consideran que las vistas de memoria son la forma moderna.
Las vistas de memoria ofrecen algunas grandes ventajas sobre la notación np.ndarray
principalmente en elegancia e interoperabilidad, sin embargo, no son superiores en rendimiento.
Actuación:
En primer lugar, se debe tener en cuenta que, en ocasiones, las opciones de verificación de límites no funcionan con vistas de memoria, lo que da como resultado cifras artificialmente rápidas para las vistas de memoria con límites de verificación = es cierto (es decir, se obtiene una indexación rápida e insegura). sorpresa.
En su mayor parte, una vez que se han aplicado las optimizaciones del compilador, las vistas de memoria y la notación de matriz numpy son iguales en rendimiento, a menudo precisamente así. Cuando hay una diferencia, normalmente no es más del 10-30%.
Índice de rendimiento
El número es el tiempo en segundos para realizar 100,000,000 operaciones. Más pequeño es más rápido.
ACCESS+ASSIGNMENT on small array (10000 elements, 10000 times)
Results for `uint8`
1) memory view: 0.0415 +/- 0.0017
2) np.ndarray : 0.0531 +/- 0.0012
3) pointer : 0.0333 +/- 0.0017
Results for `uint16`
1) memory view: 0.0479 +/- 0.0032
2) np.ndarray : 0.0480 +/- 0.0034
3) pointer : 0.0329 +/- 0.0008
Results for `uint32`
1) memory view: 0.0499 +/- 0.0021
2) np.ndarray : 0.0413 +/- 0.0005
3) pointer : 0.0332 +/- 0.0010
Results for `uint64`
1) memory view: 0.0489 +/- 0.0019
2) np.ndarray : 0.0417 +/- 0.0010
3) pointer : 0.0353 +/- 0.0017
Results for `float32`
1) memory view: 0.0398 +/- 0.0027
2) np.ndarray : 0.0418 +/- 0.0019
3) pointer : 0.0330 +/- 0.0006
Results for `float64`
1) memory view: 0.0439 +/- 0.0037
2) np.ndarray : 0.0422 +/- 0.0013
3) pointer : 0.0353 +/- 0.0013
ACCESS PERFORMANCE (100,000,000 element array):
Results for `uint8`
1) memory view: 0.0576 +/- 0.0006
2) np.ndarray : 0.0570 +/- 0.0009
3) pointer : 0.0061 +/- 0.0004
Results for `uint16`
1) memory view: 0.0806 +/- 0.0002
2) np.ndarray : 0.0882 +/- 0.0005
3) pointer : 0.0121 +/- 0.0003
Results for `uint32`
1) memory view: 0.0572 +/- 0.0016
2) np.ndarray : 0.0571 +/- 0.0021
3) pointer : 0.0248 +/- 0.0008
Results for `uint64`
1) memory view: 0.0618 +/- 0.0007
2) np.ndarray : 0.0621 +/- 0.0014
3) pointer : 0.0481 +/- 0.0006
Results for `float32`
1) memory view: 0.0945 +/- 0.0013
2) np.ndarray : 0.0947 +/- 0.0018
3) pointer : 0.0942 +/- 0.0020
Results for `float64`
1) memory view: 0.0981 +/- 0.0026
2) np.ndarray : 0.0982 +/- 0.0026
3) pointer : 0.0968 +/- 0.0016
ASSIGNMENT PERFORMANCE (100,000,000 element array):
Results for `uint8`
1) memory view: 0.0341 +/- 0.0010
2) np.ndarray : 0.0476 +/- 0.0007
3) pointer : 0.0402 +/- 0.0001
Results for `uint16`
1) memory view: 0.0368 +/- 0.0020
2) np.ndarray : 0.0368 +/- 0.0019
3) pointer : 0.0279 +/- 0.0009
Results for `uint32`
1) memory view: 0.0429 +/- 0.0022
2) np.ndarray : 0.0427 +/- 0.0005
3) pointer : 0.0418 +/- 0.0007
Results for `uint64`
1) memory view: 0.0833 +/- 0.0004
2) np.ndarray : 0.0835 +/- 0.0011
3) pointer : 0.0832 +/- 0.0003
Results for `float32`
1) memory view: 0.0648 +/- 0.0061
2) np.ndarray : 0.0644 +/- 0.0044
3) pointer : 0.0639 +/- 0.0005
Results for `float64`
1) memory view: 0.0854 +/- 0.0056
2) np.ndarray : 0.0849 +/- 0.0043
3) pointer : 0.0847 +/- 0.0056
Código de referencia (solo se muestra para acceso + asignación)
# cython: boundscheck=False
# cython: wraparound=False
# cython: nonecheck=False
import numpy as np
cimport numpy as np
cimport cython
# Change these as desired.
data_type = np.uint64
ctypedef np.uint64_t data_type_t
cpdef test_memory_view(data_type_t [:] view):
cdef Py_ssize_t i, j, n = view.shape[0]
for j in range(0, n):
for i in range(0, n):
view[i] = view[j]
cpdef test_ndarray(np.ndarray[data_type_t, ndim=1] view):
cdef Py_ssize_t i, j, n = view.shape[0]
for j in range(0, n):
for i in range(0, n):
view[i] = view[j]
cpdef test_pointer(data_type_t [:] view):
cdef Py_ssize_t i, j, n = view.shape[0]
cdef data_type_t * data_ptr = &view[0]
for j in range(0, n):
for i in range(0, n):
(data_ptr + i)[0] = (data_ptr + j)[0]
def run_test():
import time
from statistics import stdev, mean
n = 10000
repeats = 100
a = np.arange(0, n, dtype=data_type)
funcs = [(''1) memory view'', test_memory_view),
(''2) np.ndarray'', test_ndarray),
(''3) pointer'', test_pointer)]
results = {label: [] for label, func in funcs}
for r in range(0, repeats):
for label, func in funcs:
start=time.time()
func(a)
results[label].append(time.time() - start)
print(''Results for `{}`''.format(data_type.__name__))
for label, times in sorted(results.items()):
print(''{: <14}: {:.4f} +/- {:.4f}''.format(label, mean(times), stdev(times)))
Estos puntos de referencia indican que, en general, no hay mucha diferencia en el rendimiento. A veces, la notación np.ndarray es un poco más rápida y, a veces, vice-verca.
Una cosa a tener en cuenta con los puntos de referencia es que cuando el código se hace un poco más complicado o "realista", la diferencia se desvanece repentinamente, como si el compilador pierde confianza para aplicar una optimización muy inteligente. Esto se puede ver con el rendimiento de los flotadores donde no hay diferencia alguna presumiblemente ya que no se pueden usar algunas optimizaciones enteras de fantasía.
Facilidad de uso
Las vistas de memoria ofrecen ventajas significativas, por ejemplo, puede utilizar una vista de memoria en una matriz numpy, matriz CPython, matriz cython, matriz c y más, tanto presente como futura. También hay una sintaxis paralela simple para enviar cualquier cosa a una vista de memoria:
cdef double [:, :] data_view = <double[:256, :256]>data
Las vistas de memoria son excelentes en este sentido, porque si escribe una función como tomar una vista de memoria, puede tomar cualquiera de esas cosas. Esto significa que puede escribir un módulo que no tiene una dependencia en numpy, pero que aún puede tomar matrices numpy.
Por otro lado, la notación np.ndarray
da como resultado algo que sigue siendo una matriz numpy y puede llamar a todos los métodos de matriz numpy en ella. No es un gran problema tener una matriz numpy y una vista en la matriz aunque:
def dostuff(arr):
cdef double [:] arr_view = arr
# Now you can use ''arr'' if you want array functions,
# and arr_view if you want fast indexing
Tener tanto la matriz como la vista de matriz funciona bien en la práctica y me gusta bastante el estilo, ya que hace una clara distinción entre los métodos de nivel de python y los métodos de nivel c.
Conclusión
El rendimiento es casi igual y ciertamente no hay suficiente diferencia para que sea un factor decisivo.
La notación de matriz numpy se acerca más al ideal de acelerar el código de Python sin cambiarlo mucho, ya que puede continuar usando la misma variable, mientras obtiene la indexación de matriz a toda velocidad.
Por otro lado, la notación de vista de memoria probablemente sea el futuro. Si le gusta la elegancia y utiliza diferentes tipos de contenedores de datos que solo numerosos arreglos, hay una buena razón para usar vistas de memoria por motivos de coherencia.