numpy.reshapeについてメモ
多次元配列を使ってテンソル計算したいことありますよね。その時に頻出するnumpy.reshapeについて、忘備録を記しておきます。
公式リファレンス:
numpy.reshape — NumPy v1.19 Manual
numpy.reshape ですが、n次元配列の形と次元を変えることが出来ます。
使い方
numpy.reshape(a,newshape,order)
引数には、変形させたい元の配列(a)、変形後の形状(newshape)が必須で入ります。また、変形後に"どう要素を配置するか"という並べ替えの規則(order)を指定します。デフォルトは"C"という辞書式とでも言える方法ですが、この規則については以降で詳しく説明します。
並べ替えの規則①("C"の場合)
例えば、
a = np.array([[1,2,3,4], [5,6,7,8]])
みたいな、2×4の2次元配列aを考えましょう。これには8個の要素があるので、reshapeを使えば4×2の2次元配列とか、2×2×2の3次元配列とかに変形可能です。
変形前後で要素の数が合っていれば、どんな形にもできます。ですので、意味があるかは置いておいて、2×2×2×1×1みたいな5次元配列とかにも変形可能です。
さて、aを4×2の2次元配列bに変形してみます。
b = np.reshape(a,(4,2),"C")
すると、次の配列が得られます。
array([[1, 2], [3, 4], [5, 6], [7, 8]])
次に、2×2×2の3次元配列cに変形してみます。
c = np.reshape(a,(2,2,2),"C")
すると、次の配列が得られます。
array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
2つの変換例を見ましたが、どういう規則で並び替えられているのかが問題です。(3次元以上だと表示された配列を見ても良く分からない・・・。)
これを決めるのが引数orderで、今は”C”にしていました。以下で詳しく見ていきます。
配列aの要素をindex(下付き文字)を使って表現することにします。こうすることで、多次元配列でも頭が混乱せずに済みます。
これをindex (i, j)の辞書順で並べると、以下の様になります。
さて、同様に、変換で得られた配列bを要素で表し、辞書順に並べてみます。
すると、配列aを辞書順に並べたときと一致していることが分かります。次に、配列cでもやってみると、
となり、やはりaを辞書順に並べたときと一致しています。
以上の例から分かる様に、order = "C"で変換を行うときの規則は、要素をindexの辞書順で並べたときに一致させるなのです。
ちなみに、indexと要素の対応は、
c[i-1][j-1][k-1] #-1はゼロから数えるための補正
に具体的な値を代入すれば、ひとつずつ確かめることができます。
並べ替えの規則②(”F”の場合)
またaを2x2x2の配列に変形しますが、次はorderを"F"にしてみます。こうして得られる3次元配列をdとします。
d = np.reshape(a,(2,2,2),"F")
dは以下の様になります。
array([[[1, 3], [2, 4]], [[5, 7], [6, 8]]])
dの要素をindexの辞書順で並べてみます。
aの要素と比べると、次の関係が成り立っていることが分かります。
左辺は辞書順に並んでいますが、右辺は辞書順になっていません。では、aからdへの変換は、どういう規則で行われているのでしょうか。
実は、”左読みの辞書順”で並ぶようになっています。ここで左読みと言っているのは、普通に横書きの文章を読むのとは逆方向、例えば”123”を百二十三と読むのではなく、三百二十一と読むという意味です。
dの要素をindexの”左読みの辞書順”で並び替えましょう。
さらに、右辺をaの要素を使って書き換えると、
となります。
ちゃんと両辺共に”左読みの辞書順”に並んでいることが分かりますね。
”C"と”F”の違いと、”A”について
実は、これまでに見てきた並べ替えの規則は、配列をメモリにどう保存するかという規則と関係しています。
配列は1次元(ただの数値の並び)に変換されてメモリに保存されるのですが、そのときindexの辞書順(C流)に並べるか、逆読み辞書順(Fortran流)に並べるかという2通りがあります。メモリにはこの並べ方の情報も同時に記録されます。ちなみに、numpy.arrayはデフォルトではC流になります。
reshapeのorderに"C"や”F”を指定した時は、変形前の配列がC流で保存されているかFortran流で保存されているかに関わらず、規則に沿って並び替えを行います。例えば”C”を入れると、配列はC流で保存されているものとみなし、変換もC流に従って行うことになります。同様に、”F”を指定すれば、元の保存形式が何であれFortran流で読み書きされます。一方、orderには”A”というオプションも存在し、ここでは保存されている規則を判別し、それに従って変換を行います。
ちなみに、配列aがどの規則で保存されているかは、例えば、
a.flags
で確認可能です。
以上です。