Come si ottiene la grandezza di un vettore in Numpy?


157

In linea con "C'è solo un modo ovvio per farlo", come si ottiene la grandezza di un vettore (array 1D) in Numpy?

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

Quanto sopra funziona, ma non posso credere di dover specificare una funzione così banale e fondamentale.


1
Di solito uso linalg.normcome indicato di seguito. Ma leggermente più semplice della tua cosa lambda, senza importazioni necessarie, è solosum(x*x)**0.5
wim

7
A proposito, non c'è mai alcun buon motivo per assegnare una funzione lambda a un nome.
mercoledì

@wim perché è quello? Dovrei usare solo defquando dichiaro una funzione del genere? Penso che se è legittimamente una riga, rende più facile la lettura.
Nick T

6
lambda vuole essere una funzione anonima, quindi assegnandogli un nome lo stai sbagliando. allora è solo una versione paralizzata di def. e, se insisti, puoi anche mettere un def su una riga. il solito posto in cui potresti essere giustificato nell'usare lambda è per l'uso passando un elenco di argomenti come richiamabile. le persone che lo usano male come mostrato sopra sono una delle ragioni per cui sono entrate nella lista dei rimpianti di Python di Guido (vedi diapositiva 4)
wim

6
Il link è morto! Lunga vita al link!
daviewales,

Risposte:


209

La funzione che stai cercando è numpy.linalg.norm. (Immagino che dovrebbe essere in base numpy come una proprietà di un array - diciamo x.norm()- ma vabbè).

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

Puoi anche inserire un facoltativo ordper la norma del n ordine che desideri. Di 'che volevi la norma 1:

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

E così via.


14
"Dovrebbe essere una proprietà di un array: x.norm ()" Sono totalmente d'accordo. Di solito quando lavoro con numpy uso le mie sottoclassi Array e Matrix che hanno tutte le funzioni che uso comunemente come metodo. Matrix.randn([5,5])
martedì

3
Inoltre, per le matrici composte da vettori, np.linalg.normora ha un nuovo axisargomento, discusso qui: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis,

95

Se sei preoccupato per la velocità, dovresti invece usare:

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

Ecco alcuni parametri di riferimento:

>>> 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: il vero miglioramento della velocità arriva quando devi prendere la norma di molti vettori. L'uso di funzioni intorpidite pure non richiede alcun ciclo. Per esempio:

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
In realtà ho usato questo metodo leggermente meno esplicito dopo aver scoperto che np.linalg.normera un collo di bottiglia, ma poi ho fatto un passo ulteriore e ho usato solo math.sqrt(x[0]**2 + x[1]**2)un altro miglioramento significativo.
Nick T,

@NickT, vedi la mia modifica per il vero miglioramento quando si usano le funzioni intorpidite pure.
user545424

2
Fantastica applicazione del prodotto dot!
vktec,

1
numpy.linalg.normcontiene garanzie contro l'overflow che questa implementazione salta. Ad esempio, prova a calcolare la norma di [1e200, 1e200]. C'è una ragione se è più lento ...
Federico Poloni,

@FedericoPoloni, almeno con la versione numpy 1.13.3 che ottengo infdurante il calcolo np.linalg.norm([1e200,1e200]).
user545424

16

Un'altra alternativa è usare la einsumfunzione numpy per entrambi gli array:

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

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

Tuttavia, sembra esserci qualche sovraccarico associato alla chiamata che può rallentare con piccoli input:

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.normcontiene garanzie contro l'overflow che questa implementazione salta. Ad esempio, prova a calcolare la norma di [1e200, 1e200]. C'è una ragione se è più lento ...
Federico Poloni,

7

Il modo più veloce che ho trovato è tramite inner1d. Ecco come si confronta con altri metodi intorpiditi:

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 è ~ 3 volte più veloce di linalg.norm e un pelo più veloce di einsum


In realtà da quanto scritto sopra, linalg.normè il più veloce poiché fa 9 chiamate in 29ms, quindi 1 chiamata in 3.222ms contro 1 chiamata in 4.5ms per inner1d.
patapouf_ai

@bisounours_tronconneuse i tempi per il tempo totale di esecuzione. Se esegui il codice sopra, otterrai una suddivisione dei tempi per chiamata di funzione. Se hai ancora dubbi, cambia il conteggio dei vettori in qualcosa di molto grande, come ((10**8,3,))e poi esegui manualmente np.linalg.norm(V,axis=1)seguito da np.sqrt(inner1d(V,V)), noterai un linalg.normritardo rispetto a inner1d
Fnord

Ok. Grazie per il chiarimento.
patapouf_ai,

numpy.linalg.normcontiene garanzie contro l'overflow che questa implementazione salta. Ad esempio, prova a calcolare la norma di [1e200, 1e200]. C'è una ragione se è più lento ...
Federico Poloni,

3

usa la norma di funzione in 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

1

Puoi farlo in modo conciso usando la toolbelt vg . È uno strato leggero sopra intorpidito e supporta valori singoli e vettori impilati.

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

Ho creato la libreria al mio ultimo avvio, dove è stata motivata da usi come questo: idee semplici che sono troppo dettagliate in NumPy.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.