¿Cómo se obtiene la magnitud de un vector en Numpy?

157

De acuerdo con el "Solo hay una forma obvia de hacerlo", ¿cómo se obtiene la magnitud de un vector (matriz 1D) en Numpy?

def mag(x): 
    return math.sqrt(sum(i**2 for i in x))

Lo anterior funciona, pero no puedo creer que deba especificar yo mismo una función tan trivial y esencial.

Nick T
fuente
1
Usualmente uso linalg.normcomo se menciona a continuación. Pero un poco más simple que su cosa lambda, sin necesidad de importaciones, es solosum(x*x)**0.5
wim
77
Por cierto, nunca hay una buena razón para asignar una función lambda a un nombre.
wim
@wim ¿por qué es eso? ¿Solo debería usar defal declarar una función como esa? Creo que si es legítimamente una línea, hace que sea más fácil de leer.
Nick T
66
lambda está destinado a ser una función anónima, por lo que al darle un nombre lo estás haciendo mal. es solo una versión lisiada de def entonces. y, si insiste, también puede poner una definición en una línea. el lugar habitual donde podría estar justificado para usar lambda es usar una lista de argumentos como invocable. las personas que lo usan mal como se muestra arriba es una de las razones por las que llegó a la lista de lamentaciones de Python de guido (ver diapositiva 4)
wim
66
¡El enlace está muerto! ¡Viva el enlace!
daviewales

Respuestas:

209

La función que buscas es numpy.linalg.norm. (Creo que debería estar en la base numpy como una propiedad de una matriz, por ejemplo x.norm(), pero bueno).

import numpy as np
x = np.array([1,2,3,4,5])
np.linalg.norm(x)

También puede alimentar de forma opcional ordla norma de enésimo orden que desee. Digamos que quería la norma 1:

np.linalg.norm(x,ord=1)

Y así.

matemático café
fuente
14
"Debería ser una propiedad de una matriz: x.norm ()" Estoy totalmente de acuerdo. Por lo general, cuando trabajo con numpy, uso mis propias subclases de matriz y matriz que tienen todas las funciones que uso habitualmente como métodos. Matrix.randn([5,5])
mdaoust
3
Además, para las matrices compuestas de vectores, np.linalg.normahora tiene un nuevo axisargumento, discutido aquí: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis
95

Si está preocupado por la velocidad, debería usar:

mag = np.sqrt(x.dot(x))

Aquí hay algunos puntos de referencia:

>>> import timeit
>>> timeit.timeit('np.linalg.norm(x)', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0450878
>>> timeit.timeit('np.sqrt(x.dot(x))', setup='import numpy as np; x = np.arange(100)', number=1000)
0.0181372

EDITAR: La mejora de la velocidad real se produce cuando tienes que tomar la norma de muchos vectores. El uso de funciones numpy puras no requiere ninguna para bucles. Por ejemplo:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 4.23 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 18.9 us per loop

In [5]: np.allclose([np.linalg.norm(x) for x in a],np.sqrt((a*a).sum(axis=1)))
Out[5]: True
usuario545424
fuente
1
De hecho, utilicé este método un poco menos explícito después de descubrir que np.linalg.normera un cuello de botella, pero luego fui un paso más allá y simplemente utilicé, lo math.sqrt(x[0]**2 + x[1]**2)que fue otra mejora significativa.
Nick T
@NickT, mira mi edición para la mejora real cuando utilizo funciones numpy puro
user545424
2
¡Aplicación genial del producto dot!
vktec
1
numpy.linalg.normcontiene protecciones contra el desbordamiento que esta implementación omite. Por ejemplo, intente calcular la norma de [1e200, 1e200]. Hay una razón si es más lento ...
Federico Poloni
@FedericoPoloni, al menos con la versión numpy 1.13.3 que obtengo infal computar np.linalg.norm([1e200,1e200]).
user545424
16

Otra alternativa es usar la einsumfunción en numpy para cualquiera de las matrices:

In [1]: import numpy as np

In [2]: a = np.arange(1200.0).reshape((-1,3))

In [3]: %timeit [np.linalg.norm(x) for x in a]
100 loops, best of 3: 3.86 ms per loop

In [4]: %timeit np.sqrt((a*a).sum(axis=1))
100000 loops, best of 3: 15.6 µs per loop

In [5]: %timeit np.sqrt(np.einsum('ij,ij->i',a,a))
100000 loops, best of 3: 8.71 µs per loop

o vectores:

In [5]: a = np.arange(100000)

In [6]: %timeit np.sqrt(a.dot(a))
10000 loops, best of 3: 80.8 µs per loop

In [7]: %timeit np.sqrt(np.einsum('i,i', a, a))
10000 loops, best of 3: 60.6 µs per loop

Sin embargo, parece haber algo de sobrecarga asociada con la llamada que puede hacerlo más lento con entradas pequeñas:

In [2]: a = np.arange(100)

In [3]: %timeit np.sqrt(a.dot(a))
100000 loops, best of 3: 3.73 µs per loop

In [4]: %timeit np.sqrt(np.einsum('i,i', a, a))
100000 loops, best of 3: 4.68 µs per loop
n8yoder
fuente
numpy.linalg.normcontiene protecciones contra el desbordamiento que esta implementación omite. Por ejemplo, intente calcular la norma de [1e200, 1e200]. Hay una razón si es más lento ...
Federico Poloni
7

La forma más rápida que encontré es a través de inner1d. Así es como se compara con otros métodos numpy:

import numpy as np
from numpy.core.umath_tests import inner1d

V = np.random.random_sample((10**6,3,)) # 1 million vectors
A = np.sqrt(np.einsum('...i,...i', V, V))
B = np.linalg.norm(V,axis=1)   
C = np.sqrt((V ** 2).sum(-1))
D = np.sqrt((V*V).sum(axis=1))
E = np.sqrt(inner1d(V,V))

print [np.allclose(E,x) for x in [A,B,C,D]] # [True, True, True, True]

import cProfile
cProfile.run("np.sqrt(np.einsum('...i,...i', V, V))") # 3 function calls in 0.013 seconds
cProfile.run('np.linalg.norm(V,axis=1)')              # 9 function calls in 0.029 seconds
cProfile.run('np.sqrt((V ** 2).sum(-1))')             # 5 function calls in 0.028 seconds
cProfile.run('np.sqrt((V*V).sum(axis=1))')            # 5 function calls in 0.027 seconds
cProfile.run('np.sqrt(inner1d(V,V))')                 # 2 function calls in 0.009 seconds

inner1d es ~ 3 veces más rápido que linalg.norm y un cabello más rápido que einsum

Fnord
fuente
En realidad, de lo que escribe arriba, linalg.normes el más rápido ya que hace 9 llamadas en 29 ms, por lo que 1 llamada en 3.222 ms frente a 1 llamada en 4.5 ms inner1d.
patapouf_ai
@bisounours_tronconneuse usa el tiempo para el tiempo de ejecución total. Si ejecuta el código anterior, obtendrá un desglose del tiempo por llamada a la función. Si todavía tiene dudas, cambie el recuento de vectores a algo muy grande, como, ((10**8,3,))y luego ejecute manualmente np.linalg.norm(V,axis=1)seguido de np.sqrt(inner1d(V,V)), notará linalg.normun retraso en comparación con inner1d
Fnord
Okay. Gracias por la aclaración.
patapouf_ai
numpy.linalg.normcontiene protecciones contra el desbordamiento que esta implementación omite. Por ejemplo, intente calcular la norma de [1e200, 1e200]. Hay una razón si es más lento ...
Federico Poloni
3

use la norma de función en scipy.linalg (o numpy.linalg )

>>> from scipy import linalg as LA
>>> a = 10*NP.random.randn(6)
>>> a
  array([  9.62141594,   1.29279592,   4.80091404,  -2.93714318,
          17.06608678, -11.34617065])
>>> LA.norm(a)
    23.36461979210312

>>> # compare with OP's function:
>>> import math
>>> mag = lambda x : math.sqrt(sum(i**2 for i in x))
>>> mag(a)
     23.36461979210312
Doug
fuente
1

Puede hacer esto de manera concisa usando el toolbelt vg . Es una capa ligera encima de numpy y admite valores individuales y vectores apilados.

import numpy as np
import vg

x = np.array([1, 2, 3, 4, 5])
mag1 = np.linalg.norm(x)
mag2 = vg.magnitude(x)
print mag1 == mag2
# True

Creé la biblioteca en mi último inicio, donde estaba motivada por usos como este: ideas simples que son demasiado detalladas en NumPy.

paulmelnikow
fuente