どのようにしてNumpyでベクトルの大きさを取得しますか?


157

「それを行うには明らかな方法が1つしかない」に合わせて、Numpyでベクトル(1D配列)の大きさをどのように取得しますか?

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

上記は機能しますが、私はそのような些細なコア機能を自分で指定しなければならないとは信じられません。


1
通常はlinalg.norm以下のように使用しています。しかし、不要輸入して、ちょうどあなたのラムダのものよりも少し簡単ですsum(x*x)**0.5
WIM

7
ちなみに、ラムダ関数を名前に割り当てる正当な理由は決してありません。
WIM

@wimなぜですか?defそのような関数を宣言するときにのみ使用する必要がありますか?正当に一行だと読みやすくなると思います。
Nick T

6
lambdaは無名関数であることを意図しているので、それに名前を付けると、間違っていることになります。それは、defの不自由なバージョンです。また、主張する場合は、defを1行に置くこともできます。ラムダを使用することが正当化される可能性がある通常の場所は、呼び出し可能として引数リストを渡すためのものです。上に示したようにそれを誤用している人々が、それがguidoのpython後悔のリストに載せられた理由の1つです (スライド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)

必要なordn次のノルムのオプションでフィードすることもできます。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

編集:実際の速度の向上は、多くのベクトルの規範をとらなければならないときに起こります。純粋なnumpy関数を使用するために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)もう1つの重要な改善であるだけを使用しました。
ニックT

@NickT、純粋なnumpy関数を使用する場合の実際の改善については、私の編集を参照してください。
user545424 2013

2
ドット品のクールな使い方!
vktec 2018

1
numpy.linalg.normこの実装がスキップするオーバーフローに対する保護機能が含まれています。たとえば、のノルムを計算してみてください[1e200, 1e200]。遅くなるのには理由があります...
フェデリコポローニ

@FedericoPoloni、少なくともinf計算時に取得するnumpyバージョン1.13.3では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経由です。他の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は、linalg.normより3倍高速で、髪はeinsumより高速です


実際、上記の記述linalg.normからすると、29ミリ秒で9回の呼び出しを行うため、3.222ミリ秒で1回の呼び出しを行うのに対し、4.5msで1回の呼び出しを行うので、最速ですinner1d
patapouf_ai 2016年

@bisounours_tronconneuse合計実行時間のタイミング。上記のコードを実行すると、関数呼び出しごとのタイミングの内訳が表示されます。あなたはまだ疑問を持っている場合は、非常に非常に大きなものにベクトル数を変更する、などの((10**8,3,))、その後、手動で実行np.linalg.norm(V,axis=1)に続いてnp.sqrt(inner1d(V,V))、あなたはわかりますlinalg.norminner1dに比べて遅れる
Fnord

OK。説明していただきありがとうございます。
patapouf_ai 2016年

numpy.linalg.normこの実装がスキップするオーバーフローに対する保護機能が含まれています。たとえば、のノルムを計算してみてください[1e200, 1e200]。遅くなるのには理由があります...
フェデリコポローニ

3

scipy.linalg(またはnumpy.linalg)で関数normを使用します

>>> 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で非常に冗長すぎる単純なアイデア。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.