web-dev-qa-db-fra.com

Comment obtenez-vous la magnitude d'un vecteur dans Numpy?

En accord avec le "Il n'y a qu'un seul moyen évident de le faire", comment obtenez-vous la magnitude d'un vecteur (tableau 1D) dans Numpy?

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

Ce qui précède fonctionne, mais je ne peux pas croire que je dois spécifier moi-même une fonction aussi triviale et essentielle.

116
Nick T

La fonction que vous recherchez est numpy.linalg.norm . (Je pense que cela devrait être dans numpy de base en tant que propriété d'un tableau - disons x.norm() - mais bon).

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

Vous pouvez également insérer une variable ord facultative pour la norme de nième ordre souhaitée. Dites que vous vouliez la norme 1:

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

Etc.

155
mathematical.coffee

Si vous êtes inquiet à propos de la vitesse, utilisez plutôt:

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

Voici quelques repères:

>>> 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

EDIT: L'amélioration réelle de la vitesse survient lorsque vous devez prendre la norme de nombreux vecteurs. L'utilisation de fonctions numpy pures ne nécessite aucune boucle for. Par exemple:

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
74
user545424

Une autre alternative consiste à utiliser la fonction einsum dans numpy pour les deux tableaux:

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

ou vecteurs:

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

Il semble toutefois qu'il y ait des frais généraux associés à l'appel qui peuvent le ralentir avec de petites entrées:

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
16
n8yoder

Le moyen le plus rapide que j'ai trouvé est via inner1d. Voici comment cela se compare à d'autres méthodes 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 est ~ 3x plus rapide que linalg.norm et un cheveu plus rapide que einsum

7
Fnord

utilisez la fonctionnormin scipy.linalg (ou 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
2
doug

Vous pouvez le faire de manière concise en utilisant toolbelt vg . C'est une couche légère au-dessus de numpy et elle supporte les valeurs uniques et les vecteurs empilés.

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

J'ai créé la bibliothèque lors de mon dernier démarrage, où elle était motivée par des utilisations telles que celle-ci: des idées simples et trop verbeuses dans NumPy.

0
paulmelnikow