Como você obtém a magnitude de um vetor em Numpy?


157

De acordo com o "Existe apenas uma maneira óbvia de fazer isso", como você obtém a magnitude de um vetor (matriz 1D) em Numpy?

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

O exposto acima funciona, mas não acredito que devo especificar uma função tão trivial e essencial.


1
Eu costumo usar linalg.normcomo mencionado abaixo. Mas um pouco mais simples do que a sua coisa lambda, sem importações necessárias, é apenassum(x*x)**0.5
Wim

7
A propósito, nunca há um bom motivo para atribuir uma função lambda a um nome.
wim

@ wim por que isso? Eu só devo usar defao declarar uma função como essa? Eu acho que se é legitimamente uma linha, facilita a leitura.
Nick T

6
O objetivo do lambda é ser uma função anônima; portanto, ao dar um nome, você está fazendo errado. é apenas uma versão aleijada do def então. e, se você insistir, também pode definir uma definição em uma linha. o local usual em que você pode ser justificado para usar lambda é usar uma lista de argumentos como uma chamada. pessoas mis-a usá-lo como mostrado acima é uma razão para que ele fez-lo em lista guido de lamenta pitão (ver corrediça 4)
wim

6
O link está morto! Viva o link!
daviewales 27/09/14

Respostas:


209

A função que você procura é numpy.linalg.norm. (Eu acho que deve estar na base numpy como uma propriedade de uma matriz - digamos x.norm()- mas tudo bem).

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

Você também pode alimentar um opcional ordpara a norma de enésima ordem que desejar. Digamos que você queira a 1 norma:

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

E assim por diante.


14
"Deve ser uma propriedade de uma matriz: x.norm ()" Eu concordo totalmente. Normalmente, ao trabalhar com numpy, uso minhas próprias subclasses Array e Matrix que possuem todas as funções que normalmente uso como métodos. Matrix.randn([5,5])
mdaoust

3
Além disso, para matrizes compostas de vetores, np.linalg.normagora tem um novo axisargumento, discutido aqui: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis

95

Se você está preocupado com a velocidade, use:

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

Aqui estão alguns benchmarks:

>>> 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: A melhoria real da velocidade ocorre quando você precisa seguir a norma de muitos vetores. O uso de funções numpy puras não requer loops. Por exemplo:

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

1
Na verdade, eu usei esse método um pouco menos explícito depois de descobrir que np.linalg.normera um gargalo, mas depois fui um passo adiante e apenas o usei, o math.sqrt(x[0]**2 + x[1]**2)que foi outra melhoria significativa.
Nick T

@ NickT, veja minha edição para a melhoria real ao usar funções numpy puras.
user545424

2
Aplicação legal do produto escalar!
vktec

1
numpy.linalg.normcontém salvaguardas contra o estouro que esta implementação ignora. Por exemplo, tente calcular a norma de [1e200, 1e200]. Há uma razão para que seja mais lenta ...
Federico Poloni

@FedericoPoloni, pelo menos com a versão numpy 1.13.3 que recebo infao computar np.linalg.norm([1e200,1e200]).
user545424

16

Ainda outra alternativa é usar a einsumfunção numpy para qualquer uma das matrizes:

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

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

No entanto, parece haver alguma sobrecarga associada à chamada que pode torná-lo mais lento com pequenas entradas:

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

numpy.linalg.normcontém salvaguardas contra o estouro que esta implementação ignora. Por exemplo, tente calcular a norma de [1e200, 1e200]. Há uma razão para que seja mais lenta ...
Federico Poloni

7

A maneira mais rápida que encontrei é via inner1d. Veja como ele se compara a outros 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 é ~ 3x mais rápido que linalg.norm e um cabelo mais rápido que einsum


Na verdade, pelo que você escreveu acima, linalg.normé o mais rápido, pois realiza 9 chamadas em 29ms; portanto, 1 chamada em 3,222ms vs. 1 chamada em 4,5ms inner1d.
#

@bisounours_tronconneuse o tempo para o tempo total de execução. Se você executar o código acima, obterá uma divisão do tempo por chamada de função. Se você ainda tiver dúvidas, alterar a contagem de vetor para algo muito, muito grande, como ((10**8,3,))e, em seguida, executar manualmente np.linalg.norm(V,axis=1)seguido por np.sqrt(inner1d(V,V)), você notará linalg.normvai ficar em comparação com inner1d
Fnord

Está bem. Obrigado pelo esclarecimento.
patapouf_ai

numpy.linalg.normcontém salvaguardas contra o estouro que esta implementação ignora. Por exemplo, tente calcular a norma de [1e200, 1e200]. Há uma razão para que seja mais lenta ...
Federico Poloni

3

use a norma de função em 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

1

Você pode fazer isso de forma concisa usando o cinto de ferramentas vg . É uma camada leve em cima de numpy e suporta valores únicos e vetores empilhados.

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

Criei a biblioteca na minha última inicialização, onde foi motivada por usos como este: idéias simples que são muito detalhadas no NumPy.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.