web-dev-qa-db-ja.com

numpy配列を反復する最も速い方法は何ですか

Numpy配列を「直接」反復することと、tolistメソッドを介して反復することの間に有意義な違いがあることに気付きました。以下のタイミングを参照してください。

直接
[i for i in np.arange(10000000)]
via tolist
[i for i in np.arange(10000000).tolist()]

enter image description here


もっと速くする方法を見つけたので。他に何がそれを速くすることができるのかを尋ねたかったのですか?

numpy配列を反復する最も速い方法は何ですか?

10
piRSquared

これらは遅いマシンでの私のタイミングです

_In [1034]: timeit [i for i in np.arange(10000000)]
1 loop, best of 3: 2.16 s per loop
_

範囲を直接生成する場合(Py3なので、これはジェネレーターです)、時間ははるかに優れています。このサイズのリストを理解するためのベースラインとしてください。

_In [1035]: timeit [i for i in range(10000000)]
1 loop, best of 3: 1.26 s per loop
_

tolistは最初にarangeをリストに変換します。少し時間がかかりますが、繰り返しはまだリストにあります

_In [1036]: timeit [i for i in np.arange(10000000).tolist()]
1 loop, best of 3: 1.6 s per loop
_

list()の使用-配列の直接反復と同じ時間。これは、直接反復が最初にこれを行うことを示唆しています。

_In [1037]: timeit [i for i in list(np.arange(10000000))]
1 loop, best of 3: 2.18 s per loop

In [1038]: timeit np.arange(10000000).tolist()
1 loop, best of 3: 927 ms per loop
_

.tolistでの繰り返し

_In [1039]: timeit list(np.arange(10000000))
1 loop, best of 3: 1.55 s per loop
_

一般に、ループする必要がある場合は、リストでの作業の方が高速です。リストの要素へのアクセスはより簡単です。

インデックスによって返された要素を見てください。

_a[0]_は別のnumpyオブジェクトです。 aの値から構成されますが、単にフェッチされた値ではありません

list(a)[0]は同じタイプです。リストは単に_[a[0], a[1], a[2]]]_です

_In [1043]: a = np.arange(3)
In [1044]: type(a[0])
Out[1044]: numpy.int32
In [1045]: ll=list(a)
In [1046]: type(ll[0])
Out[1046]: numpy.int32
_

しかしtolistは配列を純粋なリストに変換します。この場合は、intのリストです。 list()よりも多くの機能を果たしますが、コンパイルされたコードでは機能します。

_In [1047]: ll=a.tolist()
In [1048]: type(ll[0])
Out[1048]: int
_

一般に、list(anarray)は使用しないでください。それはめったに役立つことはほとんどなく、tolist()ほど強力ではありません。

配列を反復する最も速い方法は何ですか-なし。少なくともPythonではありません。 Cコードでは高速な方法があります。

a.tolist()は、配列から整数のリストを作成する最も高速なベクトル化された方法です。繰り返しますが、コンパイルされたコードでは繰り返します。

しかし、あなたの本当の目標は何ですか?

6
hpaulj

これは実際には驚くべきことではありません。最も遅いものから始めて、メソッドを1つずつ調べてみましょう。

_[i for i in np.arange(10000000)]
_

このメソッドは、python numpy配列(Cメモリスコープに格納されている)に一度に1要素ずつ到達するように要求し、Pythonオブジェクトをメモリに割り当てます。リスト内のそのオブジェクトへのポインタを作成します。Cバックエンドに格納されているnumpy配列間をパイプして純粋なpythonにプルするたびに、オーバーヘッドコストが発生します。この方法では、そのコストが10,​​000,000回追加されます。

次:

_[i for i in np.arange(10000000).tolist()]
_

この場合、.tolist()を使用すると、numpy Cバックエンドが1回呼び出され、すべての要素が一度にリストに割り当てられます。次に、pythonを使用してそのリストを反復処理します。

最後に:

_list(np.arange(10000000))
_

これは基本的に上記と同じことを行いますが、numpyのネイティブタイプオブジェクトのリストを作成します(例:_np.int64_)。 list(np.arange(10000000))np.arange(10000000).tolist()の使用は、ほぼ同時に行う必要があります。


したがって、反復に関しては、numpyを使用する主な利点は、反復する必要がないことです。操作は、ベクトル化された方法でアレイに適用されます。反復は単にそれを遅くします。配列要素を繰り返し処理していることに気付いた場合は、試行錯誤しているアルゴリズムを再構築する方法を探す必要があります。これは、数の多い操作のみを使用する方法(組み込みが多すぎる)であるか、本当に必要な場合は、 _np.apply_along_axis_、_np.apply_over_axis_、または_np.vectorize_を使用します。

8
James

テストケースにnumpy array

[[  34  107]
 [ 963  144]
 [ 921 1187]
 [   0 1149]]

rangeenumerateを使用してこれを1回だけ実行します

使用範囲

loopTimer1 = default_timer()
for l1 in range(0,4):
    print(box[l1])
print("Time taken by range: ",default_timer()-loopTimer1)

結果

[ 34 107]
[963 144]
[ 921 1187]
[   0 1149]
Time taken by range:  0.0005405639985838206

列挙を使用

loopTimer2 = default_timer()
for l2,v2 in enumerate(box):
    print(box[l2])
print("Time taken by enumerate: ", default_timer() - loopTimer2)

結果

[ 34 107]
[963 144]
[ 921 1187]
[   0 1149]
Time taken by enumerate:  0.00025605700102460105

私が選んだこのテストケースenumerateはより速く動作します

0
Santhosh