web-dev-qa-db-ja.com

NumPyのtranspose()メソッドは、配列の軸をどのように並べ替えますか?

_In [28]: arr = np.arange(16).reshape((2, 2, 4))

In [29]: arr
Out[29]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])


In [32]: arr.transpose((1, 0, 2))
Out[32]: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])
_

整数のタプルをtranspose()関数に渡すと、どうなりますか?

具体的には、これは3D配列です。軸のタプル_(1, 0 ,2)_を渡すと、NumPyはどのように配列を変換しますか?これらの整数が参照する行または列を説明できますか? NumPyのコンテキストでの軸番号とは何ですか?

52
Frank Hu

配列を転置するために、NumPyは各軸の形状と歩幅の情報を入れ替えるだけです。ここに歩みがあります:

_>>> arr.strides
(64, 32, 8)

>>> arr.transpose(1, 0, 2).strides
(32, 64, 8)
_

転置操作が軸0と軸1のストライドを交換したことに注意してください。これらの軸の長さも交換されました(この例では両方の長さが_2_です)。

これを行うためにデータをコピーする必要はありません。 NumPyは、基になるメモリを見る方法を変更するだけで、新しい配列を作成できます。


歩幅の視覚化

ストライド値は、配列の軸の次の値に到達するためにメモリ内を移動する必要があるバイト数を表します。

これで、3D配列arrは次のようになります(ラベル付き軸付き):

enter image description here

この配列は メモリの連続ブロック ;に保存されます。本質的には一次元です。 NumPyを3Dオブジェクトとして解釈するには、3つの軸のいずれかに沿って移動するために、一定のバイト数を飛び越える必要があります。

enter image description here

各整数は8バイトのメモリを使用するため(int64 dtypeを使用しているため)、各次元のストライド値はジャンプする必要がある値の数の8倍です。たとえば、軸1に沿って移動するには4つの値(32バイト)をジャンプし、軸0に沿って移動するには8つの値(64バイト)をジャンプする必要があります。

arr.transpose(1, 0, 2)と書くとき、軸0と1を交換します。転置された配列は次のようになります。

enter image description here

NumPyが行う必要があるのは、軸0と軸1のストライド情報を交換することだけです(軸2は変更されません)。ここで、軸0よりも軸1に沿って移動するには、さらにジャンプする必要があります。

enter image description here

この基本概念は、配列の軸の順列に対して機能します。転置を処理する実際のコードはCで記述されており、 here にあります。

113
Alex Riley

説明通り ドキュメント内

デフォルトでは、寸法を逆にします。指定しない場合は、指定された値に従って軸を並べ替えます。

したがって、ディメンションの新しい順序を定義するオプションのパラメーターaxesを渡すことができます。

例えば。 RGB VGAピクセル配列の最初の2つの次元を転置する:

 >>> x = np.ones((480, 640, 3))
 >>> np.transpose(x, (1, 0, 2)).shape
 (640, 480, 3)
6
Falko

C表記では、配列は次のようになります。

_int arr[2][2][4]
_

2つの2D配列を持つ3D配列です。これらの各2D配列には2つの1D配列があり、各1D配列には4つの要素があります。

したがって、3つの次元があります。軸は0、1、2、サイズは2、2、4です。これは、numpyがN次元配列の軸を正確に処理する方法です。

したがって、arr.transpose((1, 0, 2))は軸1を取り、位置0に配置し、軸0を配置し、位置1に配置し、軸2を配置し、位置2に配置します。

_0 -\/-> 0
1 -/\-> 1
2 ----> 2
_

つまり、_1 -> 0, 0 -> 1, 2 -> 2_。コピー先の軸は常に順番に並んでいるので、必要なのはソースの軸を指定することだけです。タプルを次の順序で読み取ります:_(1, 0, 2)_。

この場合、軸0と1のサイズが同じ(2)であるため、新しい配列の次元は再び_[2][2][4]_です。

さらに興味深いのは、_(2, 1, 0)_の配列を提供する_[4][2][2]_による転置です。

_0 -\ /--> 0
1 --X---> 1
2 -/ \--> 2
_

つまり、_2 -> 0, 1 -> 1, 0 -> 2_。タプルを次の順序で読み取ります:_(2, 1, 0)_。

_>>> arr.transpose((2,1,0))
array([[[ 0,  8],
        [ 4, 12]],

       [[ 1,  9],
        [ 5, 13]],

       [[ 2, 10],
        [ 6, 14]],

       [[ 3, 11],
        [ 7, 15]]])
_

最終的に_int[4][2][2]_になりました。

すべてのディメンションのサイズが異なる場合、おそらく各軸がどこに行くのかを理解できるでしょう。

最初の内部要素が_[0, 8]_なのはなぜですか? 3D配列を2枚の紙として視覚化すると、_0_と_8_が1枚ずつ、もう1枚が左上に並んでいるからです。 _(2, 1, 0)_を転置することにより、紙から紙への方向を左から右へ紙に沿って行進させ、左から右への方向を紙から紙へ移動させたいと言っています。左から右に4つの要素があったので、代わりに4つの紙片ができました。そして、あなたは2つの論文を持っていたので、今、あなたは左から右に行く2つの要素を持っています。

ひどいASCII art。¯\_(ツ)_/¯

2
Robert B