派手な配列を逆にする最も効率的な方法


276

信じられないかもしれませんが、私の現在のコードをプロファイリングした後、numpy配列のリバートの繰り返し操作は、実行時間の巨大なチャンクを食べました。私が今持っているのは、一般的なビューベースの方法です:

reversed_arr = arr[::-1]

それをより効率的に行う他の方法はありますか、それとも非現実的な派手なパフォーマンスへの執着からの幻想ですか?


27
Er ... arr[::-1]は、逆のビューを返します。それはあなたが得ることができるのと同じくらい速く、それはストライドを変更するだけなので、配列内のアイテムの数に依存しません。あなたが実際に派手な配列を反転させているのは何ですか?
Joe Kington、

はい、確かにarr、派手な配列です。
nye17

12
うーん...まあ、私のラップトップでは、アレイの長さに関係なく、約670ナノ秒かかります。それがボトルネックである場合、言語を切り替える必要があるかもしれません... numpy配列を逆にするより速い方法を見つけられないと私はかなり確信しています。とにかく頑張ってね!
Joe Kington、2011

6
ええと、必ずしもループ内で実行する必要がありますか?場合によっては、数百万のアイテムを含む派手な配列を作成し、配列全体を操作する方がよい場合もあります。結果が前の結果に依存する有限差分法または類似のものを実行している場合でも、時々これを行うことができます。(時に強調...)とにかく、速度が主な目標である場合、Fortranは依然として王様です。f2pyあなたの友だちです!アルゴリズムのパフォーマンスが重要な部分(特に科学計算)を別の言語で記述し、それをpythonから呼び出すことはしばしば価値があります。幸運を!
Joe Kington、

1
@berto。それはラッパーだので、それは遅いですarr[::-1]github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py。を検索しdef flipudます。関数は文字通り4行です。
Mad Physicist

回答:


240

作成するreversed_arrと、元の配列にビューが作成されます。その後、元の配列を変更すると、ビューが更新され、変更が反映されます。

必要以上にビューを再作成していますか?次のようなことができるはずです。

arr = np.array(some_sequence)
reversed_arr = arr[::-1]

do_something(arr)
look_at(reversed_arr)
do_something_else(arr)
look_at(reversed_arr)

私は派手な専門家ではありませんが、これは派手に物事を行うための最速の方法のようです。これがあなたがすでにやっていることなら、あなたはそれを改善することができないと思います。

PSここで派手なビューの素晴らしい議論:

派手な配列を見ますか?


スライスオブジェクトを作成し、それを多くの配列で再利用することは役立ちますか?
エンドリス

1
実際、私はそれをテストしただけで、ループの外で作成されたスライスオブジェクトとの違いはありません。(まあ、それは非常にわずかに高速です。1000000ループでは繰り返し43.4ミリ秒vs 44.3ミリ秒)
エンドリス

look_at関数は何をすることを想定していますか?
mrgloom

1
@mrgloomデータを見るタスクを表すことになっています。この例の要点は、reversed_arr基礎となるデータが変更された後もビューがまだ使用可能であることを示すことでした。配列に新しい値を書き込んでも、ビューは無効になりません。実際には、ビューを使用して配列に新しい値を書き込むこともできます。 reversed_arr[0] = 99配列の最後の要素を99に設定しarr[-1] = 99ます。
steveha

60

上で述べたように、a[::-1]実際にはビューを作成するだけなので、それは一定時間の操作です(したがって、配列が大きくなるのでそれほど時間がかかりません)。配列を連続させる必要がある場合(たとえば、それを使用して多くのベクトル演算を実行しているため)、/とascontiguousarray同じくらい高速です。flipupfliplr

ここに画像の説明を入力してください


プロットを生成するコード:

import numpy
import perfplot


perfplot.show(
    setup=lambda n: numpy.random.randint(0, 1000, n),
    kernels=[
        lambda a: a[::-1],
        lambda a: numpy.ascontiguousarray(a[::-1]),
        lambda a: numpy.fliplr([a])[0],
    ],
    labels=["a[::-1]", "ascontiguousarray(a[::-1])", "fliplr"],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)

それはF-ストリング(文字列リテラル補間)を使用するためperfplotは、少なくともPythonの3.6必要
fivef

42

これはまだ回答済みとしてマークされていないようです... Thomas Arildsenの回答は適切なものでなければなりません。

np.flipud(your_array) 

1次元配列(列配列)の場合。

matrizeで

fliplr(matrix)

flipud(matrix)を反転したい場合、列を反転したい場合。1d列の配列を2次元の行の配列(Noneレイヤーが1つある行列)にしてから反転する必要はありません。


38

np.fliplr() 配列を左から右に反転します。

1次元配列の場合は、少しトリックする必要があることに注意してください。

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]

34
reversed_arr = np.flipud(arr1d)直接動作するようです。
Thomas Arildsen、2015

3

についての以前の回答について詳しく説明しnp.fliplr()ます。以下は、1次元配列の作成、2次元配列への変換、反転、1次元配列への変換を示すコードです。time.clock()秒単位で表される時間を維持するために使用されます。

import time
import numpy as np

start = time.clock()
x = np.array(range(3))
#transform to 2d
x = np.atleast_2d(x)
#flip array
x = np.fliplr(x)
#take first (and only) element
x = x[0]
#print x
end = time.clock()
print end-start

コメントなしの印刷ステートメント:

[2 1 0]
0.00203907123594

プリントステートメントをコメントアウトすると:

5.59799927506e-05

ですから、効率の点では、それはまともです。一行でそれを行うのが大好きなあなたのために、ここにその形があります。

np.fliplr(np.atleast_2d(np.array(range(3))))[0]

3
そのような小さな配列で何かのタイミングをとるのは、ほとんど役に立ちません。物事を比較したい場合は、3000やそれ以上の要素など、しばらく時間がかかるものを使用する方が良いでしょう。
15:09のバラバス

0

他の人が言ったことを拡張して、短い例を挙げましょう。

1D配列がある場合...

>>> import numpy as np
>>> x = np.arange(4) # array([0, 1, 2, 3])
>>> x[::-1] # returns a view
Out[1]: 
array([3, 2, 1, 0])

しかし、2D配列で作業している場合...

>>> x = np.arange(10).reshape(2, 5)
>>> x
Out[2]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> x[::-1] # returns a view:
Out[3]: array([[5, 6, 7, 8, 9],
               [0, 1, 2, 3, 4]])

これは実際にはマトリックスを反転しません。

np.flipを使用して実際に要素を反転する必要があります

>>> np.flip(x)
Out[4]: array([[9, 8, 7, 6, 5],
               [4, 3, 2, 1, 0]])

マトリックスの要素を1つずつ印刷する場合は、フリップと一緒にフラットを使用します

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0

-1

負の数と長いリストで機能させるには、次のようにします。

b = numpy.flipud(numpy.array(a.split(),float))

反転が1d arraの場合

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