Jak obliczyć wielkość wektora w Numpy?

157

Zgodnie z zasadą „Jest tylko jeden oczywisty sposób, aby to zrobić”, jak uzyskać wielkość wektora (tablica 1D) w Numpy?

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

Powyższe działa, ale nie mogę uwierzyć , że muszę sam określić tak banalną i podstawową funkcję.

Nick T.
źródło
1
Zwykle używam, linalg.normjak wspomniano poniżej. Ale nieco prostsze niż twoja lambda, bez konieczności importowania, jest po prostusum(x*x)**0.5
wim,
7
Nawiasem mówiąc, nigdy nie ma dobrego powodu, aby przypisywać funkcję lambda do nazwy.
wim
@wim, dlaczego tak jest? Powinienem używać tylko defpodczas deklarowania takiej funkcji? Myślę, że jeśli jest to prawdziwa jedna linijka, ułatwia to czytanie.
Nick T
6
lambda ma być funkcją anonimową, więc nadając jej nazwę, robisz to źle. to tylko okaleczona wersja def. a jeśli nalegasz, możesz również umieścić def w jednej linii. Zwykłym miejscem, w którym może być uzasadnione użycie lambda, jest przekazanie jakiejś listy argumentów jako możliwej do wywołania. ludzie niewłaściwie go używają, jak pokazano powyżej, jest jednym z powodów, dla których trafił na listę żalu Guido Pythona (patrz slajd 4)
wim,
6
Link nie działa! Niech żyje link!
daviewales

Odpowiedzi:

209

Funkcja, której szukasz, to numpy.linalg.norm. (Myślę, że powinno to być w base numpy jako właściwość tablicy - powiedzmy x.norm()- ale no cóż).

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

Możesz również wprowadzić opcjonalną ordnormę n-tego rzędu, którą chcesz. Powiedzmy, że chciałeś normy 1:

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

I tak dalej.

kawa matematyczna
źródło
14
„Powinien być właściwością tablicy: x.norm ()” Całkowicie się zgadzam. Zwykle podczas pracy z numpy używam własnych podklas Array i Matrix, które mają wszystkie funkcje, których zwykle używam jako metody. Matrix.randn([5,5])
mdaoust,
3
Również dla macierzy złożonych z wektorów np.linalg.normma teraz nowy axisargument, omówiony tutaj: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis
95

Jeśli w ogóle martwisz się o prędkość, powinieneś zamiast tego użyć:

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

Oto kilka testów porównawczych:

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

EDYCJA: Prawdziwa poprawa prędkości następuje, gdy musisz przyjąć normę dla wielu wektorów. Używanie czystych funkcji numpy nie wymaga żadnych pętli for. Na przykład:

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
źródło
1
Właściwie użyłem tej nieco mniej jednoznacznej metody po stwierdzeniu, że np.linalg.normjest to wąskie gardło, ale potem poszedłem o krok dalej i po prostu zastosowałem, math.sqrt(x[0]**2 + x[1]**2)co było kolejną znaczącą poprawą.
Nick T
@NickT, zobacz moją edycję, aby zobaczyć rzeczywistą poprawę podczas używania czystych funkcji numpy.
user545424
2
Fajna aplikacja produktu kropkowego!
vktec,
1
numpy.linalg.normzawiera zabezpieczenia przed przepełnieniem, które ta implementacja pomija. Na przykład spróbuj obliczyć normę [1e200, 1e200]. Jest powód, jeśli wolniej ...
Federico Poloni
@FedericoPoloni, przynajmniej z numpy w wersji 1.13.3, którą otrzymuję infpodczas przetwarzania np.linalg.norm([1e200,1e200]).
user545424
16

Jeszcze inną alternatywą jest użycie einsumfunkcji numpy dla obu tablic:

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

lub wektory:

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

Wydaje się jednak, że z wywoływaniem go wiąże się pewien narzut, który może spowolnić go przy małych wejściach:

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
źródło
numpy.linalg.normzawiera zabezpieczenia przed przepełnieniem, które ta implementacja pomija. Na przykład spróbuj obliczyć normę [1e200, 1e200]. Jest powód, jeśli wolniej ...
Federico Poloni
7

Najszybszy sposób, jaki znalazłem, to Internal1d. Oto, jak wypada to w porównaniu z innymi metodami 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 jest ~ 3x szybsze niż linalg.norm i włos szybciej niż einsum

Fnord
źródło
Właściwie z tego, co napisałeś powyżej, linalg.normjest najszybszy, ponieważ wykonuje 9 połączeń w 29 ms, więc 1 połączenie w 3,222 ms w porównaniu do 1 połączenia w 4,5 ms inner1d.
patapouf_ai
@bisounours_tronconneuse czas dla całkowitego czasu wykonania. Jeśli uruchomisz powyższy kod, otrzymasz podział czasu na wywołanie funkcji. Jeśli nadal masz wątpliwości, zmień liczbę wektorów na bardzo duże, na przykład, ((10**8,3,))a następnie uruchom ręcznie, np.linalg.norm(V,axis=1)a następnie np.sqrt(inner1d(V,V))zauważysz linalg.normopóźnienie w porównaniu do inner1d
Fnord
Dobrze. Dziękuję za wyjaśnienie.
patapouf_ai
numpy.linalg.normzawiera zabezpieczenia przed przepełnieniem, które ta implementacja pomija. Na przykład spróbuj obliczyć normę [1e200, 1e200]. Jest powód, jeśli wolniej ...
Federico Poloni
3

użyj normy funkcji w scipy.linalg (lub 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
źródło
1

Możesz to zrobić zwięźle za pomocą paska narzędzi vg . Jest to lekka warstwa na wierzchu numpy i obsługuje pojedyncze wartości i ułożone wektory.

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

Bibliotekę stworzyłem podczas mojego ostatniego uruchomienia, gdzie była motywowana takimi zastosowaniami: prostymi pomysłami, które są zbyt szczegółowe w NumPy.

paulmelnikow
źródło