Pybonacci

Computación Científica con Python en castellano

MicroEntradas: numpy.vectorize

with 2 comments

Hoy vamos a ver numpy.vectorize que, como la documentación oficial indica, sirve para 'vectorizar' funciones que solo aceptan escalares como entrada. La entrada que podemos meter es una lista de objetos o un 'numpyarray' y nos devolverá como resultado un 'numpyarray'.

No os engañéis, normalmente, cuando hablamos de vectorizar pensamos en varios órdenes de magnitud de mejora en el rendimiento pero eso no es lo que hace esta función :-). Podéis pensar en esta función como algo parecido a usar map.

Por ejemplo, la función abs solo acepta un entero o un float. Por ejemplo, lo siguiente:


print(abs(-3))

Nos devolvería el valor 3. Hasta ahí bien. Pero si queremos hacer lo siguiente:


print(abs([-3, -2]))

Nos devolverá un TypeError: bad operand type for abs(): 'list'.

Podríamos usar map para ello pero no obtendríamos un 'numpyarray' como salida y en Python 3 devuelve un iterador que para transformarlo a, por ejemplo, una lista o un 'numpyarray' debemos añadir un paso más, la conversión a lista (en Python 2 devuelve directamente una lista) o dos pasos, la conversión a lista y esta a 'numpyarray'.

Vamos a ver cómo podemos 'vectorizar' la función abs sin usar map y usando numpy.vectorize (antes deberéis importar numpy como np):


vectabs = np.vectorize(abs)

que podemos usar de la siguiente forma:


print(vectabs([-3, -2]))

Et voilà. ya tenemos lo que queríamos. Veamos como va el rendimiento de lo que acabamos de hacer:


In [1]: import numpy as np

In [2]: kk = np.random.randn(1000000)

In [3]: timeit np.abs(kk)
100 loops, best of 3: 4.13 ms per loop

In [4]: vectabs = np.vectorize(abs)

In [5]: timeit vectabs(kk)
10 loops, best of 3: 87.6 ms per loop

In [6]: timeit np.array([abs(i) for i in kk])
1 loops, best of 3: 241 ms per loop

La última opción es equivalente a hacer np.array(list(map(abs, kk))) en tiempo. La versión vectorizada, vectabs, es 21 veces más lenta que la que podemos obtener usando numpy.abs por lo que no tendríamos una gran ganancia pero, sin embargo, vemos que es casi tres veces más rápida que la versión usando map (o una 'list comprehension') por lo que algo ganariamos respecto a Python 3 puro :-).

Como apuntes finales, si sabéis de alguna función en CPython que no existe equivalente en numpy y la necesitáis usar quizá podéis obtener una ganancia usando numpy.vectorize. Si tenéis que escribir la función vosotros, escribidla pensando en operaciones vectorizadas usando 'numpyarrays' y no os hará falta usar numpy.vectorize.

Como punto final. recordad que np.vectorize no es más que un decorador por lo que lo siguiente sería perfectamente válido:


@np.vectorize
def mi_funcion(*args):
    ...

Saludos.

[Editado: corrección de algún bug, disculpas!!]

About these ads

Written by Kiko

31 de enero de 2014 at 6:30

2 comentarios

Subscribe to comments with RSS.

  1. Me ha gustado, algunas veces pensé en esto pero nunca investigué si existía un “vectorizador”.
    Agrego una expresión que falto que asombrosamente le gana hasta a np.abs, usando generadores:


    In [11]: timeit np.abs(kk)
    10 loops, best of 3: 29.1 ms per loop

    In [12]: timeit vect_abs(kk)
    1 loops, best of 3: 387 ms per loop

    In [13]: timeit np.array([abs(i) for i in kk])
    1 loops, best of 3: 1.32 s per loop

    In [14]: timeit np.array(abs(i) for i in kk)
    100000 loops, best of 3: 11.2 µs per loop

    Obligando a timeit a hacer 100 loops me dan los mismo resultados, ¡2600 veces mas rápido que np.abs!

    Cristian Hernán Schmidt

    31 de enero de 2014 at 15:58

    • FE DE ERRATA: como no puedo eliminar mi comentario agrego que acabo de comprobar que la versión con expresión generadora no da los resultados que se necesitan, con lo cual mi conclusión es errónea. Sigue siendo np.abs el mejor :)

      Cristian Hernán Schmidt

      31 de enero de 2014 at 16:04


¡Deja un comentario!

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 1.269 seguidores

%d personas les gusta esto: