Як ви отримуєте величину вектора в Numpy?


158

Відповідно до "Є лише один очевидний спосіб зробити це", як ви отримуєте величину вектора (1D масив) у Numpy?

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

Вищезазначене працює, але я не можу повірити, що я повинен сам вказати таку банальну і основну функцію.


1
Я зазвичай використовую, linalg.normяк було сказано нижче. Але трохи простіша річ, ніж ваша лямбда, без імпорту, простоsum(x*x)**0.5
відмовляйте

7
До речі, ніколи немає жодної вагомої причини привласнити лямбда-функцію імені.
Вім

@wim чому це? Я повинен використовувати лише defпри оголошенні такої функції? Я думаю, що якщо це законно один рядок, це полегшує читання.
Нік Т

6
лямбда призначена для анонімної функції, тому, даючи їй ім’я, ви робите неправильно. це просто калікова версія def тоді. і, якщо ви наполягаєте, ви також можете поставити def на один рядок. звичайне місце, де вам може бути виправдано використовувати лямбда, - це використання, яке передається в деякий список аргументів як позивний. люди неправильно використовують його, як показано вище, є однією з причин, чому він потрапив до списку шкодування python (див. слайд 4)
вім

6
Посилання мертва! Хай живе посилання!
daviewales

Відповіді:


209

Функція, яку ви шукаєте, є numpy.linalg.norm. (Я вважаю, що це повинно бути в базі numpy як властивість масиву - скажімо, x.norm()- але добре).

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

Ви також можете подавати в додатковому ordпорядку для норми n-го порядку, який ви хочете. Скажіть, що хочете 1-норми:

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

І так далі.


14
"Має бути властивістю масиву: x.norm ()" Я повністю згоден. Зазвичай під час роботи з numpy я використовую власні підкласи Array та Matrix, які мають усі функції, які я зазвичай використовую як методи. Matrix.randn([5,5])
mdaoust

3
Крім того , для матриць , що складаються з векторів, np.linalg.normтепер має новий axisаргумент, який обговорювався тут: stackoverflow.com/a/19794741/1959808
Іоанніс Filippidis

95

Якщо ви зовсім переживаєте за швидкість, вам слід скористатися:

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

Ось деякі орієнтири:

>>> 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: Справжнє підвищення швидкості настає, коли вам доведеться приймати норму багатьох векторів. Використання чистих функцій numpy не вимагає циклів. Наприклад:

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
Я фактично використовував цей трохи менш чіткий метод, виявивши, що np.linalg.normце вузьке місце, але потім я пішов на крок далі і просто використав, math.sqrt(x[0]**2 + x[1]**2)що було ще одним значним покращенням.
Нік Т

@NickT, дивіться мою редакцію щодо справжнього вдосконалення при використанні чистих функцій numpy.
користувач545424

2
Класне застосування крапкового продукту!
vktec

1
numpy.linalg.normмістить захисні засоби від переповнення, які ця програма пропускає. Наприклад, спробуйте обчислити норму [1e200, 1e200]. Є причина, якщо вона повільніше ...
Федеріко Полоні

@FedericoPoloni, принаймні, з нумерованою версією 1.13.3, яку я отримую infпід час обчислень np.linalg.norm([1e200,1e200]).
користувач545424

16

Ще одна альтернатива - використовувати einsumфункцію numpy для будь-якого масиву:

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

або вектори:

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

Однак, здається, є деякі накладні витрати, пов’язані з викликом його, що може зробити його повільнішим з невеликими введеннями:

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.normмістить захисні засоби від переповнення, які ця програма пропускає. Наприклад, спробуйте обчислити норму [1e200, 1e200]. Є причина, якщо вона повільніше ...
Федеріко Полоні

7

Найшвидший спосіб, який я знайшов, - через inner1d. Ось як це порівняти з іншими методами 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 на 3 рази швидше, ніж linalg.norm, а волосся швидше за einsum


Насправді, із linalg.normнаписаного вище, це найшвидше, оскільки він робить 9 дзвінків за 29 мс, тому 1 дзвінок у 3,222 мс проти 1 дзвінок у 4,5 мс для inner1d.
patapouf_ai

@bisounours_tronconneuse терміни загального часу виконання. Якщо запустити код вище, ви отримаєте зрив часу на виклик функції. Якщо у вас все ще виникають сумніви, змініть кількість векторів на щось дуже велике, наприклад, ((10**8,3,))а потім запустіть вручну, np.linalg.norm(V,axis=1)за ним np.sqrt(inner1d(V,V)), ви помітите, linalg.normбуде відставати порівняно з inner1d
Fnord,

Гаразд. Дякую за роз’яснення.
patapouf_ai

numpy.linalg.normмістить захисні засоби від переповнення, які ця програма пропускає. Наприклад, спробуйте обчислити норму [1e200, 1e200]. Є причина, якщо вона повільніше ...
Федеріко Полоні

3

використовувати норму функції в scipy.linalg (або 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

Це можна зробити стисло за допомогою інструментальної стрічки vg . Це легкий шар поверх нумерового, і він підтримує одиничні значення та складені вектори.

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

Я створив бібліотеку під час свого останнього запуску, де вона була мотивована такими способами використання: простими ідеями, які є занадто багатослівними в NumPy.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.