Как вы получаете величину вектора в 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
wim

7
Кстати, нет никаких веских причин назначать лямбда-функцию имени.
Вим

@ Wim, почему это? Я должен использовать только defпри объявлении такой функции? Я думаю, что если это законно одна строка, это облегчает чтение.
Ник Т

6
Лямбда предназначена для анонимной функции, поэтому, дав ей имя, вы делаете это неправильно. тогда это просто искаженная версия def. и, если вы настаиваете, вы также можете поставить def на одну строку. обычное место, где вам может быть оправдано использование лямбды, - это использование для передачи в некотором списке аргументов в качестве вызываемого. люди, неправильно использующие его, как показано выше, являются одной из причин, по которым он попал в список сожалений Гвидо о питоне (см. слайд 4)
wim

Ответы:


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
Иоаннис Филиппидис

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

РЕДАКТИРОВАТЬ: реальное улучшение скорости происходит, когда вы должны принять норму многих векторов. Использование чисто кодовых функций не требует циклов for. Например:

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 функций.
user545424

2
Классное приложение точечного продукта!
vktec

1
numpy.linalg.normсодержит гарантии от переполнения, которые пропускает эта реализация. Например, попробуйте вычислить норму [1e200, 1e200]. Есть причина, если она медленнее ...
Федерико Полони,

@FedericoPoloni, по крайней мере с клочковатой версией 1.13.3, которую я получаю infпри вычислениях np.linalg.norm([1e200,1e200]).
user545424

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. Вот как это можно сравнить с другими методами:

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

Вы можете сделать это кратко, используя toolbelt vg . Это легкий слой поверх numpy и он поддерживает отдельные значения и сложенные векторы.

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.