Bagaimana Anda mendapatkan besarnya vektor di Numpy?

157

Sesuai dengan "Hanya ada satu cara yang jelas untuk melakukannya", bagaimana Anda mendapatkan besarnya vektor (array 1D) di Numpy?

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

Di atas berfungsi, tetapi saya tidak percaya bahwa saya harus menentukan sendiri fungsi sepele dan inti itu.

Nick T
sumber
1
Saya biasanya menggunakan linalg.normseperti yang disebutkan di bawah ini. Tetapi sedikit lebih sederhana dari pada lambda Anda, tanpa perlu impor, hanyasum(x*x)**0.5
wim
7
Omong-omong, tidak pernah ada alasan bagus untuk menetapkan fungsi lambda pada sebuah nama.
wim
@mengapa itu? Saya hanya harus menggunakan defketika mendeklarasikan fungsi seperti itu? Saya pikir jika itu sah satu baris, itu membuatnya lebih mudah dibaca.
Nick T
6
lambda dimaksudkan untuk fungsi anonim, jadi dengan memberikan nama Anda salah melakukannya. itu hanya versi cacat dari itu. dan, jika Anda bersikeras, Anda juga dapat menempatkan def pada satu baris. tempat biasa di mana Anda mungkin dibenarkan untuk menggunakan lambda adalah untuk penggunaan lewat daftar argumen sebagai panggilan. orang salah menggunakannya seperti yang ditunjukkan di atas adalah salah satu alasan mengapa itu membuatnya masuk ke daftar guido tentang penyesalan python (lihat slide 4)
wim
6
Tautannya sudah mati! Hidup tautannya!
daviewales

Jawaban:

209

Fungsi yang Anda cari adalah numpy.linalg.norm. (Saya rasa itu harus di dasar numpy sebagai properti dari array - katakan x.norm()- tapi oh well).

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

Anda juga dapat memberi makan dalam opsional orduntuk norma urutan ke-n yang Anda inginkan. Katakanlah Anda menginginkan 1-norma:

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

Dan seterusnya.

matematika. kopi
sumber
14
"Seharusnya properti dari array: x.norm ()" Saya setuju sepenuhnya. Biasanya ketika bekerja dengan numpy saya menggunakan subclass Array dan Matrix saya sendiri yang memiliki semua fungsi yang biasa saya gunakan sebagai metode. Matrix.randn([5,5])
mdaoust
3
Juga, untuk matriks yang terdiri dari vektor, np.linalg.normsekarang memiliki axisargumen baru , dibahas di sini: stackoverflow.com/a/19794741/1959808
Ioannis Filippidis
95

Jika Anda khawatir tentang kecepatan, Anda sebaiknya menggunakan:

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

Inilah beberapa tolok ukur:

>>> 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: Peningkatan kecepatan nyata terjadi ketika Anda harus mengambil norma banyak vektor. Menggunakan fungsi numpy murni tidak memerlukan loop. Sebagai contoh:

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
pengguna545424
sumber
1
Saya benar-benar menggunakan metode yang sedikit kurang eksplisit ini setelah menemukan itu np.linalg.normadalah hambatan, tetapi kemudian saya melangkah lebih jauh dan hanya menggunakan math.sqrt(x[0]**2 + x[1]**2)yang merupakan peningkatan signifikan lainnya.
Nick T
@NickT, lihat edit saya untuk perbaikan nyata saat menggunakan fungsi numpy murni.
user545424
2
Aplikasi keren dari produk dot!
vktec
1
numpy.linalg.normberisi perlindungan terhadap overflow yang dilewati oleh implementasi ini. Misalnya, coba hitung norma [1e200, 1e200]. Ada alasan jika lebih lambat ...
Federico Poloni
@FedericoPoloni, setidaknya dengan versi numpy 1.13.3 saya dapatkan infsaat komputasi np.linalg.norm([1e200,1e200]).
user545424
16

Namun alternatif lain adalah menggunakan einsumfungsi numpy untuk kedua 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

atau vektor:

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

Namun, sepertinya ada beberapa overhead yang terkait dengan memanggilnya yang mungkin membuatnya lebih lambat dengan input kecil:

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
sumber
numpy.linalg.normberisi perlindungan terhadap overflow yang dilewati oleh implementasi ini. Misalnya, coba hitung norma [1e200, 1e200]. Ada alasan jika lebih lambat ...
Federico Poloni
7

Cara tercepat yang saya temukan adalah melalui inner1d. Berikut ini perbandingannya dengan metode numpy lainnya:

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 lebih cepat dari linalg.norm dan rambut lebih cepat dari einsum

Fnord
sumber
Sebenarnya dari apa yang Anda tulis di atas, linalg.normadalah yang tercepat karena 9 panggilan dalam 29ms jadi 1 panggilan dalam 3,222ms vs 1 panggilan dalam 4,5ms untuk inner1d.
patapouf_ai
@bisounours_tronconneuse gunakan waktu untuk total waktu eksekusi. Jika Anda menjalankan kode di atas Anda akan mendapatkan rincian waktu per panggilan fungsi. Jika Anda masih ragu, ubah jumlah vektor ke sesuatu yang sangat besar, seperti ((10**8,3,))dan kemudian jalankan secara manual np.linalg.norm(V,axis=1)diikuti oleh np.sqrt(inner1d(V,V)), Anda akan melihat linalg.normakan ketinggalan dibandingkan dengan inner1d
Fnord
Baik. Terimakasih atas klarifikasinya.
patapouf_ai
numpy.linalg.normberisi perlindungan terhadap overflow yang dilewati oleh implementasi ini. Misalnya, coba hitung norma [1e200, 1e200]. Ada alasan jika lebih lambat ...
Federico Poloni
3

gunakan norma fungsi di scipy.linalg (atau 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
sumber
1

Anda dapat melakukan ini secara ringkas menggunakan toolbelt vg . Ini adalah lapisan ringan di atas numpy dan mendukung nilai tunggal dan vektor ditumpuk.

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

Saya membuat perpustakaan di startup terakhir saya, yang dimotivasi oleh penggunaan seperti ini: ide-ide sederhana yang terlalu bertele-tele di NumPy.

paulmelnikow
sumber