#1.【概要】
O'Reilly Japanのゼロから作るDeep Learningを読んでいて、numpyのtransposeメソッドの動き(特に3次元配列以上の時の動き)が難しく理解に手間取ったので、自分用のメモも兼ねて記載します。
####【対象レベル】
・numpy初心者(transposeメソッドを利用したことが無い、転置・軸の概念がわからない)
#2. 【transposeメソッドとは】
Pythonライブラリのnumpyに含まれている、配列加工用のメソッドです。
※numpy.transposeとnumpy.ndarray.transposeがありますが、今回は後者が対象です。
リファレンスの概要から、重要そうな部分だけ和訳すると下記のようになります。(Google翻訳&意訳)
軸を転置した配列のビューを返します。
・1次元配列に使用した場合、転置されたベクトルは単純に同じベクトルであるため、このメソッドは効果がありません。
・2次元配列に使用した場合、これは標準の行列転置です。
・n次元配列に使用した場合、軸が指定されていれば、その順序が軸の並べ替え方法を示します。
numpy.ndarray.transpose 公式リファレンス(英語)
##2.1 2次元配列にtransposeを使用した場合
リファレンスにて、1次元配列はtransposeを使っても意味が無いと記載されているので、
まずは2次元配列の場合の動き(行列転置)から見ていきます。
####【そもそも転置とは??】
m 行 n 列の行列 A に対して A の (i, j) 要素と (j, i) 要素を入れ替えた n 行 m 列の行列、つまり対角線で成分を折り返した行列のことである。
Wikipediaより
要は下記のように配列の縦と横を入れ替える行為です。
※Jupyter NotebookやGoogle colaboratoryにそのまま貼り付けて動かせるようになっています。
#Numpyのインポート
import numpy as np
#randintメソッドで0~9までのランダムな値で、size引数に記載の形状の配列を生成
Array=np.random.randint(10,size=(2,3))
print("2×3の配列")
print(Array)
#2次元配列に対しtransposeを実行し、転置する(3×2になる)
print(Array.transpose())
#---------------コードここまで----------------
#【実行結果】
#・2×3の配列
[[2 5 2]
[6 1 1]]
#・上記配列を転置(3×2になる)
[[2 6]
[5 1]
[2 1]]
ここまでは特に問題ないかと思います。
##2.2 n次元配列にtransposeを使用した場合
では本題のn次元配列(3次元以上の配列)の場合の動き(軸の並び変え)を見ていきます。
先ほどのリファレンスを見返すと、n次元配列は下記のように記載されています。
・n次元配列に使用した場合、軸が指定されていれば、その順序が軸の並べ替え方法を示します。
####【軸とは??】
指摘を恐れず個人的な解釈を述べると、
軸=配列要素を表すときのインデックス(Array[1,2]の「1,2」の部分)のことだと言えます。
そしてこの軸の順番を並べ変える=「配列の各要素の位置を配列のインデックス基準で変更する」ということになります。
実際にコードを実行してみましょう。
#Numpyのインポート
import numpy as np
#randintメソッドで0~9までのランダムな値で、size引数に記載の形状の配列を作成
Array=np.random.randint(10,size=(2,3,4))
print("【2×3×4の3次元配列】\n")
print(Array)
#transposeメソッドで並び替え
print(Array.transpose(1,0,2))
#---------------コードここまで----------------
#【実行結果】
[[[4 8 1 6]
[2 3 8 5]
[9 1 0 6]]
[[0 1 7 0]
[9 6 4 9]
[4 6 9 5]]]
#【transpose(1,0,2)を実行し、上記配列を並び替え(インデックスの0番目、1番目を入れ替え)】
#・配列の形状:2×3×4 → 3×2×4
# ※インデックス0番目(第0軸)の「2」と1番目(第1軸)の「3」が入れ替わっている
#・要素の位置:全要素が形状同様に入れ替わり(例 [2,1]の値[0,1,7,0]が[1,2]に移動)
# ※入れ替わっていないものは、元々インデックスの0番目と1番目が同じ値で、入れ替えても変わらないもの。(インデックスが[1,1,2]の値など)
[[[4 8 1 6]
[0 1 7 0]]
[[2 3 8 5]
[9 6 4 9]]
[[9 1 0 6]
[4 6 9 5]]]
####【reshapeメソッドと何が違う??】
同じようなメソッドにreshapeもありますが、
reshapeの場合は、あくまで配列の形状が変わるだけで、transposeのように配列の要素の位置は変わりません。
つまり、最初に生成した配列を1次元配列一行で表現したものと比較すると下記のようになります。
・reshapeメソッド:最初に生成した配列と同じ
・transposeメソッド:最初に生成した配列と要素の順番が違う
試しにreshapeした場合と、transposeした場合とで比べてみましょう。
#Numpyのインポート
import numpy as np
#randintメソッドで0~9までのランダムな値で、size引数に記載の形状の配列を作成
Array=np.random.randint(10,size=(2,3,4))
#reshapeメソッドで形状変形(2×3×4 → 3×2×4)
Array_reshape=(Array.reshape(3,2,4))
#transposeメソッドで形状変形と共に並び替え(2×3×4 → 3×2×4)
Array_transpose=(Array.transpose(1,0,2))
#flattenメソッドで配列を1次元に直して比較
print("\n\n【検証:flattenで配列を1次元に直して比較】")
print("・最初に生成した配列")
print(Array.flatten())
print("\n・reshapeメソッドで形状だけを変えた配列 (最初に生成した配列と「同じ」順に要素が並んでいる)")
print(Array_reshape.flatten())
print("\n・transposeメソッドで形状と要素の位置を変えた配列 (最初に生成した配列と「違う」順に要素が並んでいる)")
print(Array_transpose.flatten())
#---------------コードここまで----------------
#【実行結果】
#【検証:flattenで配列を1次元に直して比較】
#・最初に生成した配列
[4 4 1 6 5 5 3 8 5 1 2 9 2 5 5 7 7 4 2 8 7 9 6 3]
#・reshapeメソッドで形状だけを変えた配列 (最初に生成した配列と「同じ」順に要素が並んでいる)
[4 4 1 6 5 5 3 8 5 1 2 9 2 5 5 7 7 4 2 8 7 9 6 3]
#・transposeメソッドで形状と要素の位置を変えた配列 (最初に生成した配列と「違う」順に要素が並んでいる)
[4 4 1 6 2 5 5 7 5 5 3 8 7 4 2 8 5 1 2 9 7 9 6 3]
##3.【結論】
reshapeは配列の**「形状」を変えますが、「要素の並び順」は変わりません。**
しかし、transposeはreshapeのように配列の**「形状」を変えると共に「要素の並び順」も入れ替えます。**
(注意:reshapeのように任意の形状の配列は作れません。あくまで各次元の要素数を並び替えるだけです。)