できること
numpyがサポートするndarrayオブジェクト(配列)をバイト単位で変形できる。reshapeメソッドでは形状の変更のみだが、as_stridedメソッドを使うとより手の込んだ変形ができる。
a = np.arange(10, dtype=np.float64)
b = as_strided(a, (5, 2), (8, 8))
print(a)
->
\begin{pmatrix}
0 & 1 \\
1 & 2 \\
2 & 3 \\
... & ... \\
8 & 9 \\
\end{pmatrix}
ライブラリの使い方
①インポート
import numpy as np
from numpy.lib.stride_tricks import as_strided
②メソッド呼び出し
as_strided(ndarray, shape, strides)
戻り値:ndarrayオブジェクト(※注意)
引数について:
ndarray→変形元のndarrayオブジェクト。例えばnp.arange(100)とか。
shape→変形後の配列の形状。ndarray.reshape()と同様に指定すればよい。例えばshape=(3,3)ならば3行3列のndarrayが返される。
strides→バイト単位でndarrayを見た時に値を参照する間隔。例えばstrides=(8,8)ならば行、列が増えるごとに8バイトずつ変形前の配列の値を読んでいく。
仕組み
例として以下のようなndarray配列を考える。各要素はfloat型とする。
\begin{pmatrix}
0.0 & 1.0 \\
2.0 & 3.0
\end{pmatrix}
この時メモリ上でこの配列は次のように保存される。
0.0,1.0,2.0,3.0
これをバイトに直すと
\x00\x00\x00\x00\x00\x00\x00\x00,\x01\x00\x00\x00\x00\x00\x00\x00,\x02\x00\x00\x00\x00\x00\x00\x00,\x03\x00\x00\x00\x00\x00\x00\x00
float型は8bytesなので8*4=32bytes長となっている。
ではメモリ上のndarrayの要素にアクセスするにはどのようにしているか?
例えば、上の(2,1)要素の2.0にアクセスするならば
2x8 bytes+1x8 bytes=24 bytes目から8bytesを読み込めば良い。
この行と列の要素ごとの間隔をstrides, 1要素のバイト数をitemsizeという。上の例ならばstrides=(8,8)でitemsize=8となる。
この2要素はndarray生成時に設定される情報で、ndarray.itemsize, ndarray.stridesで確認できる。
これを踏まえた上で
as_strided(ndarray, shape, strides)
のstirdes引数が意味するのは
変形後の配列の各要素が参照する変形前の配列のバイト間隔。
例. x = (0.0, 1.0, 2.0, ... , 9.0)とする。
as_strided(x, (3,2), (16,8))とした場合、
変形後の配列の(3,2)要素にはitemsize=8とした時、
16x3 + 2x8 =64バイト目の値、つまり7.0が入る。
(1,2)要素には
16x1 + 2x8 =32バイト目の値、つまり3.0が入る
注意
as_stridedが返すオブジェクトは実際にはndarrayではない。元のndarrayの要素へのポインタ(参照)値をもつだけ。なので、as_stridedが返した配列の要素に代入するような操作を行うと、参照元の変形前配列の要素も同様に変更されてしまう。