LoginSignup
11
9

More than 3 years have passed since last update.

NumPyのstride_tricks.as_stridedの使用例

Last updated at Posted at 2019-05-19

はじめに

音声処理でよく使うフレームシフト処理(スライディングウィンドウとか,Pandasのrolling的な処理(?)とも呼ぶ)をしたい.
numpy.lib.stride_tricks.as_strided を使えばよいという情報にたどり着くも,思いのほかはまってしまったので,備忘録的にいくつかの例を書いてみた.

1次元のデータ

いろいろ調べてみたものの,2次元の例が多く自分にはイメージできなかったので,
最初は1次元から考えていく.1次元ならワタシチョトダケワカルので...

import numpy as np
from numpy.lib.stride_tricks import as_strided

まずは,適当に長さ10のndarrayを作る.そして,いくつかの情報を確認して,データ構造のイメージを持っておく.
特に,itemsizeは,後でstrideのbyte数を考えるときに必要になる.

x = np.arange(10, dtype='float64')
# -> array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
x.size
# -> 10
x.shape
# -> (10,)
x.strides
# -> (8,)
x.itemsize
# -> 8

早速as_strideを試してみる.まずは,1つとばしにしてみる.
要素1つで8バイトなので,1つスキップするなら,こうだろうか?

as_strided(x, strides=(16,))
# -> array([0.00000000e+000, 2.00000000e+000, 4.00000000e+000, 6.00000000e+000,
#       8.00000000e+000, 0.00000000e+000, 4.80906177e-073, 4.63229019e-073,
#       3.74803829e-073, 4.16239459e+201])

末尾が怪しいが,,,C言語で配列外にアクセスしてしまった時が思い出される.
きちんとshapeも与えておくなら,こんな感じか.

as_strided(x, shape=(5,), strides=(16,))
# -> array([0., 2., 4., 6., 8.])

つぎは,スライディングウィンドウ処理の基本として,2つずつのデータとして取り出してみたい.こんな感じか.

as_strided(x, shape=(5,2))
# -> array([[0., 1.],
#        [2., 3.],
#        [4., 5.],
#        [6., 7.],
#        [8., 9.]])

できたけど,この時のstrideはどうなっているのだろうか?

as_strided(x, shape=(5,2)).strides
# -> (16, 8)

最初に調べたx.stirdesから自動で変わっている?ようだ.
1つ目の16は下側に見ていく時に次のデータが存在する場所までのバイト数で,
2つ目の8は右側に見ていく時に次のデータが存在する場所までのバイト数である.
つまり,右には1つずつ見ていけばよく,下には1つとばして2つ目をみればよい,ということだ.
C言語の画像処理でよくやるアレに似てるな,と思ったし,これがイメージできれば怖くないのではないか.

int W = 16;
int H = 32;
unsigned char Image[W*H];

for(w=0; w<W; w++) for(h=0; h<H; h++) {
    Image[h*W + w] = ...;
}

さて,次はオーバーラップを考慮しよう.下に見るときに2つ隣ではなく少し手前を見に行けばよい.
ということは,

as_strided(x, shape=(9,2), strides=(8,8))
# -> array([[0., 1.],
#           [1., 2.],
#           [2., 3.],
#           [3., 4.],
#           [4., 5.],
#           [5., 6.],
#           [6., 7.],
#           [7., 8.],
#           [8., 9.]])

はみ出したらどうなるだろうか?

as_strided(x, shape=(10,2), strides=(8,8))
# -> array([[0., 1.],
#           [1., 2.],
#           [2., 3.],
#           [3., 4.],
#           [4., 5.],
#           [5., 6.],
#           [6., 7.],
#           [7., 8.],
#           [8., 9.],
#           [9., 0.]])

ゼロ詰めかな・・・?もう少し違う形にしてみよう.3つずつの塊で,2つずらして読む形だとどうなるか.

as_strided(x, shape=(5,3), strides=(16,8))
# -> array([[0., 1., 2.],
#           [2., 3., 4.],
#           [4., 5., 6.],
#           [6., 7., 8.],
#           [8., 9., 0.]])

2次元のデータ

次に,2次元の特徴量の配列から,スライディングウィンドウ処理をしたい時を考えておきたい.

X = np.arange(10, dtype='float64').reshape(5,2)
# X -> array([[0., 1.],
#             [2., 3.],
#             [4., 5.],
#             [6., 7.],
#             [8., 9.]])

3次元のデータにすることを考えるとして,元の構造(2次元の特徴量)をキープするなら,stridesの最後の2つの数字はそのままにすればいい.つまり,元のfloat64で8 byteを指定,それを2つ組にしているので16 byteを指定.
あとは,その2つ組を1つずつずらしたいなら 1 x 16 =16 byte, 2つずつずらしていきたいなら 2 x 16 = 32 bytes となる.

as_strided(X, shape=(4,2,2), strides=(16,16,8))
# -> array([[[0., 1.],
#            [2., 3.]],
#    
#           [[2., 3.],
#            [4., 5.]],
#    
#           [[4., 5.],
#            [6., 7.]],
#    
#           [[6., 7.],
#            [8., 9.]]])

as_strided(X, shape=(3,2,2), strides=(32,16,8))
# -> array([[[ 0.00000000e+000,  1.00000000e+000],
#            [ 2.00000000e+000,  3.00000000e+000]],
#    
#           [[ 4.00000000e+000,  5.00000000e+000],
#            [ 6.00000000e+000,  7.00000000e+000]],
#    
#           [[ 8.00000000e+000,  9.00000000e+000],
#            [ 0.00000000e+000, -1.29986726e-231]]])

shapeを変えれば,2次元特徴量を3つ分見る,みたいなことも可能.

as_strided(X, shape=(4,3,2), strides=(16,16,8))
# -> array([[[ 0.00000000e+000,  1.00000000e+000],
#            [ 2.00000000e+000,  3.00000000e+000],
#            [ 4.00000000e+000,  5.00000000e+000]],
#    
#           [[ 2.00000000e+000,  3.00000000e+000],
#            [ 4.00000000e+000,  5.00000000e+000],
#            [ 6.00000000e+000,  7.00000000e+000]],
#    
#           [[ 4.00000000e+000,  5.00000000e+000],
#            [ 6.00000000e+000,  7.00000000e+000],
#            [ 8.00000000e+000,  9.00000000e+000]],
#    
#           [[ 6.00000000e+000,  7.00000000e+000],
#            [ 8.00000000e+000,  9.00000000e+000],
#            [ 0.00000000e+000, -1.29986726e-231]]])

なんとなく使い方がわかった.だが,shapeの計算&ゼロ詰めは自分でしたほうがよいかもしれない.

また,繰り返しになるがstridesはバイト数である.自分が扱ってるデータのバイト数がわからないならitemsizeなどで確認すればよい.(C言語の sizeof 的な感じ.)

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9