Comment obtenir la magnitude d'un vecteur dans Numpy?

158

En accord avec le "Il n'y a qu'une seule façon évidente de le faire", comment obtenir 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 doive spécifier moi-même une fonction aussi triviale et fondamentale.

Nick T
la source
1
J'utilise habituellement linalg.normcomme mentionné ci-dessous. Mais un peu plus simple que votre truc lambda, sans importation nécessaire, est justesum(x*x)**0.5
wim
7
Au fait, il n'y a jamais de bonne raison d'assigner une fonction lambda à un nom.
wim
@wim pourquoi est-ce? Je ne devrais utiliser que deflors de la déclaration d'une fonction comme ça? Je pense que si c'est légitimement une ligne, cela facilite la lecture.
Nick T
6
lambda est destiné à être une fonction anonyme, donc en lui donnant un nom, vous le faites mal. c'est juste une version paralysée de def alors. et, si vous insistez, vous pouvez également mettre une def sur une ligne. L'endroit habituel où vous pourriez être justifié d'utiliser lambda est d'utiliser le passage d'une liste d'arguments comme appelable. les gens qui l'utilisent à mauvais escient comme indiqué ci-dessus est l'une des raisons pour lesquelles il a figuré sur la liste des regrets python de guido (voir diapositive 4)
wim
6
Le lien est mort! Vive le lien!
daviewales

Réponses:

209

La fonction que vous recherchez est numpy.linalg.norm. (Je pense qu'il devrait être en base numpy 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 ajouter une option ordpour la norme de nième ordre que vous souhaitez. Disons que vous vouliez la norme 1:

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

Etc.

mathématique.café
la source
14
"Devrait être une propriété d'un tableau: x.norm ()" Je suis totalement d'accord. Habituellement, lorsque je travaille avec numpy, j'utilise mes propres sous-classes Array et Matrix qui ont toutes les fonctions que j'utilise couramment comme méthodes. Matrix.randn([5,5])
mdaoust le
3
Aussi, pour les matrices composées de vecteurs, a np.linalg.normmaintenant un nouvel axisargument, discuté ici: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis
95

Si vous vous inquiétez du tout de la vitesse, vous devriez plutôt utiliser:

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: La véritable amélioration 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
user545424
la source
1
J'ai en fait utilisé cette méthode légèrement moins explicite après avoir découvert qu'il np.linalg.norms'agissait d'un goulot d'étranglement, mais je suis ensuite allé plus loin et j'ai simplement utilisé math.sqrt(x[0]**2 + x[1]**2)ce qui était une autre amélioration significative.
Nick T
@NickT, voir ma modification pour la réelle amélioration lors de l'utilisation de fonctions numpy pures.
user545424
2
Cool application du produit scalaire!
vktec
1
numpy.linalg.normcontient des garanties contre les débordements que cette implémentation ignore. Par exemple, essayez de calculer la norme de [1e200, 1e200]. Il y a une raison si c'est plus lent ...
Federico Poloni
@FedericoPoloni, au moins avec la version numpy 1.13.3 que j'obtiens inflors de l'informatique np.linalg.norm([1e200,1e200]).
user545424
16

Une autre alternative consiste à utiliser la einsumfonction dans numpy pour l'un ou l'autre des 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

Cependant, il semble y avoir une surcharge associée à son appel qui peut 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
n8yoder
la source
numpy.linalg.normcontient des garanties contre les débordements que cette implémentation ignore. Par exemple, essayez de calculer la norme de [1e200, 1e200]. Il y a une raison si c'est plus lent ...
Federico Poloni
7

Le moyen le plus rapide que j'ai trouvé est via inner1d. Voici comment cela se compare aux 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

Fnord
la source
En fait, d'après ce que vous écrivez ci-dessus, linalg.normc'est le plus rapide car il fait 9 appels en 29 ms, donc 1 appel en 3,222 ms contre 1 appel en 4,5 ms pour inner1d.
patapouf_ai
@bisounours_tronconn utilise le minutage du temps total d'exécution. Si vous exécutez le code ci-dessus, vous obtiendrez une ventilation de la synchronisation par appel de fonction. Si vous avez encore des doutes, changez le nombre de vecteurs en quelque chose de très très grand, comme ((10**8,3,))puis exécutez manuellement np.linalg.norm(V,axis=1)suivi de np.sqrt(inner1d(V,V)), vous remarquerez un linalg.normdécalage par rapport à inner1d
Fnord
D'accord. Merci pour la clarification.
patapouf_ai
numpy.linalg.normcontient des garanties contre les débordements que cette implémentation ignore. Par exemple, essayez de calculer la norme de [1e200, 1e200]. Il y a une raison si c'est plus lent ...
Federico Poloni
3

utiliser la fonction norm dans 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
doug
la source
1

Vous pouvez le faire de manière concise en utilisant le toolbelt vg . C'est une couche légère au-dessus de numpy et il prend en charge 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 ma dernière startup, où elle était motivée par des usages comme celui-ci: des idées simples et beaucoup trop verbeuses dans NumPy.

paulmelnikow
la source