覚え書きブログ

ブロック単位のreshape

以下の、6行4列の行列を2行2列を一塊とするブロック単位でreshapeして12行2列の行列に変換するとする。

z=np.arange(24).reshape(6,4)
>z
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])

その場合、普通にreshapeを使うと以下のように、2行2列のブロックはおかまないなしで変換される。
つまり、最初は[[0,1],[4,5]]というブロックだったのに対し、変換後は[[0,1],[2,3]]となってしまっている。

>np.reshape(z,[12,2])
array([[ 0,  1],
       [ 2,  3],
       [ 4,  5],
       [ 6,  7],
       [ 8,  9],
       [10, 11],
       [12, 13],
       [14, 15],
       [16, 17],
       [18, 19],
       [20, 21],
       [22, 23]])

6行4列の行列は、2行2列のブロックが3行2列あるわけなので、expand_dimsを2回作り(1, 1, 6, 4)にした後、
(3, 2, 2, 2)にreshapeする。この最初の(3,2)がブロックの配列で、(2,2)がブロックの大きさに対応している。

> np.expand_dims(np.expand_dims(z,0),0).shape 
(1, 1, 6, 4)
>np.reshape(np.expand_dims(np.expand_dims(z,0),0),[3,2,2,2]).shape
(3, 2, 2, 2)
>zz = np.reshape(np.expand_dims(np.expand_dims(z,0),0),[3,2,2,2])
>zz
array([[[[ 0,  1],
         [ 2,  3]],

        [[ 4,  5],
         [ 6,  7]]],


       [[[ 8,  9],
         [10, 11]],

        [[12, 13],
         [14, 15]]],


       [[[16, 17],
         [18, 19]],

        [[20, 21],
         [22, 23]]]])

しかしながら、これも単にreshapeしただけなので、最初のブロックは[[0,1], [4,5]]となってほしいところが[[0,1],[2,3]]になっている。
ここで、[0,1]は4次元配列で言うと、その番地は(0,0,0)に対応し、[2,3]は(0,0,1)、[4,5]は(0,1,0)、[6,7]は(0,1,1)に対応している。
つまり、現在ブロックとしてくっついてしまっている[[0,1],[2,3]]は番地で言うと、(0,0,0)と(0,0,1)に、ブロックでくっつけてほしい[[0,1],[2,3]]は番地で言うと(0,0,0)と(0,0,1)に対応している。つまり、1軸目と2軸目の番地を入れ替えると、[0,1]は(0,0,0)のままであるのに対し、[2,3]は(0,1,0), [4,5]は(0,0,1)となるので、[4,5]は[0,1]とくっつけることができる。

他はどうだろうか?次のブロックは[[2,3],[6,7]]だが、1軸目と2軸目の番地を入れ替えると、[2,3]は(0,1,0)、[6,7]は(0,1,1)となる。したがって、同じブロックとしてくっつけることができている。

したがって、以下のようにtransposeを用いて入れ替えると以下のようになる。

> np.transpose(zz,[0,2,1,3])
array([[[[ 0,  1],
         [ 4,  5]],

        [[ 2,  3],
         [ 6,  7]]],


       [[[ 8,  9],
         [12, 13]],

        [[10, 11],
         [14, 15]]],


       [[[16, 17],
         [20, 21]],

        [[18, 19],
         [22, 23]]]])

>np.transpose(zz,[0,2,1,3]).reshape(12,2)
array([[ 0,  1],
       [ 4,  5],
       [ 2,  3],
       [ 6,  7],
       [ 8,  9],
       [12, 13],
       [10, 11],
       [14, 15],
       [16, 17],
       [20, 21],
       [18, 19],
       [22, 23]])

しかし、この方法では、ブロックが2行2列の場合、元々の行列の列数が4の場合しかうまくいかない。
例えば、以下のように4行6列の場合はうまくいかない。

>z=np.arange(24).reshape(4,6)
>z
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
> zz = np.reshape(np.expand_dims(np.expand_dims(z,0),0),[2,3,2,2])
> zz
array([[[[ 0,  1],
         [ 2,  3]],

        [[ 4,  5],
         [ 6,  7]],

        [[ 8,  9],
         [10, 11]]],


       [[[12, 13],
         [14, 15]],

        [[16, 17],
         [18, 19]],

        [[20, 21],
         [22, 23]]]])
np.transpose(zz,[0,2,1,3]).reshape(12,2)
array([[ 0,  1],
       [ 4,  5],
       [ 8,  9],
       [ 2,  3],
       [ 6,  7],
       [10, 11],
       [12, 13],
       [16, 17],
       [20, 21],
       [14, 15],
       [18, 19],
       [22, 23]])

本当は、[[0,1],[6,7]]のブロックを作ってほしいのだが、[[0,1],[4,5]]のブロックになっている。残念。
他によい方法がないだろうか。。。