Wie erhält man die Größe eines Vektors in Numpy?

156

Wie erhält man in Übereinstimmung mit "Es gibt nur einen offensichtlichen Weg, dies zu tun" die Größe eines Vektors (1D-Arrays) in Numpy?

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

Das Obige funktioniert, aber ich kann nicht glauben, dass ich selbst eine solche triviale und Kernfunktion spezifizieren muss.

Nick T.
quelle
1
Ich benutze normalerweise linalg.normwie unten erwähnt. Aber etwas einfacher als Ihr Lambda-Ding, ohne dass Importe erforderlich sind, ist nursum(x*x)**0.5
wim
7
Es gibt übrigens nie einen guten Grund, einem Namen eine Lambda-Funktion zuzuweisen.
wim
@wim warum ist das so? Ich sollte nur verwenden, defwenn ich eine solche Funktion deklariere? Ich denke, wenn es legitimerweise eine Zeile ist, erleichtert es das Lesen.
Nick T
6
Lambda soll eine anonyme Funktion sein. Wenn Sie ihm also einen Namen geben, machen Sie es falsch. Es ist nur eine verkrüppelte Version von def. und wenn Sie darauf bestehen, können Sie auch eine Def in eine Zeile setzen. Der übliche Ort, an dem Sie möglicherweise berechtigt sind, Lambda zu verwenden, ist die Übergabe einer Argumentliste als aufrufbar. Leute, die es wie oben gezeigt falsch benutzen, sind ein Grund, warum es auf Guidos Liste der Python-Bedauern geschafft hat (siehe Folie 4)
wim
6
Der Link ist tot! Es lebe der Link!
Daviewales

Antworten:

209

Die Funktion, nach der Sie suchen, ist numpy.linalg.norm. (Ich denke, es sollte in Base Numpy als Eigenschaft eines Arrays sein - sagen wir x.norm()- aber na ja ).

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

Sie können auch eine Option ordfür die gewünschte Norm n-ter Ordnung eingeben. Angenommen, Sie wollten die 1-Norm:

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

Und so weiter.

mathematisch.Kaffee
quelle
14
"Sollte eine Eigenschaft eines Arrays sein: x.norm ()" Ich stimme vollkommen zu. Wenn ich mit numpy arbeite, verwende ich normalerweise meine eigenen Array- und Matrix-Unterklassen, die alle Funktionen enthalten, die ich normalerweise als Methoden verwende. Matrix.randn([5,5])
Mdaoust
3
Auch für Matrizen, die aus Vektoren bestehen, gibt es np.linalg.normjetzt ein neues axisArgument, das hier diskutiert wird: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis
95

Wenn Sie sich Sorgen um die Geschwindigkeit machen, sollten Sie stattdessen Folgendes verwenden:

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

Hier einige 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: Die wirkliche Geschwindigkeitsverbesserung kommt, wenn Sie die Norm vieler Vektoren nehmen müssen. Für die Verwendung von reinen Numpy-Funktionen sind keine for-Schleifen erforderlich. Beispielsweise:

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
quelle
1
Ich habe diese etwas weniger explizite Methode tatsächlich verwendet, nachdem ich festgestellt hatte, dass dies np.linalg.normein Engpass war, aber dann bin ich noch einen Schritt weiter gegangen und habe nur eine Methode verwendet, math.sqrt(x[0]**2 + x[1]**2)die eine weitere signifikante Verbesserung darstellt.
Nick T
@NickT, siehe meine Bearbeitung für die echte Verbesserung bei der Verwendung von reinen Numpy-Funktionen.
user545424
2
Coole Anwendung des Punktprodukts!
vktec
1
numpy.linalg.normenthält Schutzmaßnahmen gegen Überlauf, die diese Implementierung überspringt. Versuchen Sie beispielsweise, die Norm von zu berechnen [1e200, 1e200]. Es gibt einen Grund, wenn es langsamer ist ...
Federico Poloni
@FedericoPoloni, zumindest mit numpy Version 1.13.3 bekomme ich infbeim Rechnen np.linalg.norm([1e200,1e200]).
user545424
16

Eine weitere Alternative besteht darin, die einsumFunktion in numpy für beide Arrays zu verwenden:

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

oder Vektoren:

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

Es scheint jedoch einen gewissen Overhead mit dem Aufrufen zu geben, der es bei kleinen Eingaben langsamer machen kann:

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
quelle
numpy.linalg.normenthält Schutzmaßnahmen gegen Überlauf, die diese Implementierung überspringt. Versuchen Sie beispielsweise, die Norm von zu berechnen [1e200, 1e200]. Es gibt einen Grund, wenn es langsamer ist ...
Federico Poloni
7

Der schnellste Weg, den ich gefunden habe, ist über inner1d. So vergleicht es sich mit anderen Numpy-Methoden:

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 ist ~ 3x schneller als linalg.norm und ein Haar schneller als einsum

Fnord
quelle
Nach dem, was Sie oben geschrieben haben, linalg.normist es das schnellste, da 9 Anrufe in 29 ms getätigt werden, also 1 Anruf in 3,222 ms gegenüber 1 Anruf in 4,5 ms für inner1d.
Patapouf_ai
@bisounours_tronconneuse das Timing für die gesamte Ausführungszeit. Wenn Sie den obigen Code ausführen, erhalten Sie eine Aufschlüsselung des Timings pro Funktionsaufruf. Wenn Sie noch Zweifel haben, ändern Sie die Vektorzählregister zu etwas sehr sehr groß, wie ((10**8,3,))und dann manuell ausführen , np.linalg.norm(V,axis=1)indem gefolgt np.sqrt(inner1d(V,V)), werden Sie feststellen , linalg.normwird hinken im Vergleich zu inner1d
Fnord
OK. Danke für die Klarstellung.
Patapouf_ai
numpy.linalg.normenthält Schutzmaßnahmen gegen Überlauf, die diese Implementierung überspringt. Versuchen Sie beispielsweise, die Norm von zu berechnen [1e200, 1e200]. Es gibt einen Grund, wenn es langsamer ist ...
Federico Poloni
3

verwenden , um die Funktion norm in scipy.linalg (oder 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
quelle
1

Sie können dies mit dem Toolbelt vg präzise tun . Es ist eine leichte Schicht über Numpy und unterstützt einzelne Werte und gestapelte Vektoren.

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

Ich habe die Bibliothek bei meinem letzten Start erstellt, wo sie durch solche Verwendungen motiviert war: einfache Ideen, die in NumPy viel zu ausführlich sind.

paulmelnikow
quelle