2013-08-25 10 views
6

スライディングウィンドウ操作をベクトル化しようとしています。Python - スライディングウィンドウをベクトル化する

x[1,:]=np.where((x[0,:]<2)&(x[0,:]>0),x[1,x[0,:]+1],x[1,:]) 
IndexError: index (10) out of range (0<=index<9) in dimension 1 

x= vstack((np.array([range(10)]),np.array([range(10)]))) 

x[1,:]=np.where((x[0,:]<5)&(x[0,:]>0),x[1,x[0,:]+1],x[1,:]) 

インデックス< 5用の各電流値についてのn + 1つの値しかし、私はこのエラーを取得する:1-Dの場合に役立つ例は、の線に沿って行くことができます不思議なことに、私は0よりも小さいインデックスを意味し、N-1値のため、このエラーを得ないだろうそれは気にしていないようです:

x[1,:]=np.where((x[0,:]<5)&(x[0,:]>0),x[1,x[0,:]-1],x[1,:]) 

print(x) 

[[0 1 2 3 4 5 6 7 8 9] 
[0 0 1 2 3 5 6 7 8 9]] 

は、この問題を回避とにかくありますか?私のアプローチは完全に間違っていますか?コメントがあれば幸いです。

EDIT:

matriz = np.array([[1,2,3,4,5], 
    [6,5,4,3,2], 
    [1,1,2,2,3], 
    [3,3,2,2,1], 
    [3,2,1,3,2], 
    [1,2,3,1,2]]) 

# matrix to vector 
vector2 = ndarray.flatten(matriz) 

ncols = int(shape(matriz)[1]) 
nrows = int(shape(matriz)[0]) 

vector = np.zeros(nrows*ncols,dtype='float64') 


# Interior pixels 
if ((i % ncols) != 0 and (i+1) % ncols != 0 and i>ncols and i<ncols*(nrows-1)): 

    vector[i] = np.mean(np.array([vector2[i-ncols-1],vector2[i-ncols],vector2[i-ncols+1],vector2[i-1],vector2[i+1],vector2[i+ncols-1],vector2[i+ncols],vector2[i+ncols+1]])) 

これは私が、私は、各セルの6×6近傍の平均値を計算したいnumpyの配列に行列を平らに達成したいものです

+0

明確にするために、 'vector2 [i]'を平均に含めたくないのですが、これはコードの間違いでしたか? – Daniel

+0

私はしません。ありがとうございました。 – JEquihua

+0

あなたのコードは6x6近傍ではなく、各セルの3x3近傍の平均を計算します。これは意図的でしたか? – nneonneo

答えて

8

私が問題を正しく理解している場合は、インデックスを無視して、インデックスの周りのすべての数字の平均をとってみたいと思います。

def original(matriz): 

    vector2 = np.ndarray.flatten(matriz) 

    nrows, ncols= matriz.shape 
    vector = np.zeros(nrows*ncols,dtype='float64') 

    # Interior pixels 
    for i in range(vector.shape[0]): 
     if ((i % ncols) != 0 and (i+1) % ncols != 0 and i>ncols and i<ncols*(nrows-1)): 

      vector[i] = np.mean(np.array([vector2[i-ncols-1],vector2[i-ncols],\ 
         vector2[i-ncols+1],vector2[i-1],vector2[i+1],\ 
         vector2[i+ncols-1],vector2[i+ncols],vector2[i+ncols+1]])) 

私はスライスとビューを使用して使用して、これを書き直し:

def mean_around(arr): 
    arr=arr.astype(np.float64) 

    out= np.copy(arr[:-2,:-2]) #Top left corner 
    out+= arr[:-2,2:]   #Top right corner 
    out+= arr[:-2,1:-1]   #Top center 
    out+= arr[2:,:-2]   #etc 
    out+= arr[2:,2:] 
    out+= arr[2:,1:-1] 
    out+= arr[1:-1,2:] 
    out+= arr[1:-1,:-2] 

    out/=8.0 #Divide by # of elements to obtain mean 

    cout=np.empty_like(arr) #Create output array 
    cout[1:-1,1:-1]=out  #Fill with out values 
    cout[0,:]=0;cout[-1,:]=0;cout[:,0]=0;cout[:,-1]=0 #Set edges equal to zero 

    return cout 

np.empty_likeを使用して、充填、私は私はあなたがこのような何かのために行っていたと信じて、動作するようにあなたの機能をパッチを適用している

エッジはわずかに速いように見えたnp.zeros_like。まず、matriz配列を使って同じことを二重チェックします。

print np.allclose(mean_around(matriz),original(matriz)) 
True 

print mean_around(matriz) 
[[ 0.  0.  0.  0.  0. ] 
[ 0.  2.5 2.75 3.125 0. ] 
[ 0.  3.25 2.75 2.375 0. ] 
[ 0.  1.875 2.  2.  0. ] 
[ 0.  2.25 2.25 1.75 0. ] 
[ 0.  0.  0.  0.  0. ]] 

いくつかのタイミング:

a=np.random.rand(500,500) 

print np.allclose(original(a),mean_around(a)) 
True 

%timeit mean_around(a) 
100 loops, best of 3: 4.4 ms per loop 

%timeit original(a) 
1 loops, best of 3: 6.6 s per loop 

大雑把〜1500倍高速化。

def mean_numba(arr): 
    out=np.zeros_like(arr) 
    col,rows=arr.shape 

    for x in xrange(1,col-1): 
     for y in xrange(1,rows-1): 
      out[x,y]=(arr[x-1,y+1]+arr[x-1,y]+arr[x-1,y-1]+arr[x,y+1]+\ 
         arr[x,y-1]+arr[x+1,y+1]+arr[x+1,y]+arr[x+1,y-1])/8. 
    return out 

nmean= autojit(mean_numba) 

今、すべての提示方法と比較することができます:numbaを使用するには良い場所は次のように

が見えます。

a=np.random.rand(5000,5000) 

%timeit mean_around(a) 
1 loops, best of 3: 729 ms per loop 

%timeit nmean(a) 
10 loops, best of 3: 169 ms per loop 

#CT Zhu's answer 
%timeit it_mean(a) 
1 loops, best of 3: 36.7 s per loop 

#Ali_m's answer 
%timeit fast_local_mean(a,(3,3)) 
1 loops, best of 3: 4.7 s per loop 

#lmjohns3's answer 
%timeit scipy_conv(a) 
1 loops, best of 3: 3.72 s per loop 

numba upの4倍の速度は、numpyのコードが得ようとしているほど良好であることを示しています。私は提示された他のコードを引っ張りましたが、私は@ CTZhuの答えを変えて配列のサイズを変えなければなりませんでした。

+1

ニース。私のバージョンではn = 3の方が2倍の速さですが、その特定のケースではかなり調整されています。 – nneonneo

+0

私はこれが大好きです。私は今休暇中ですが、私は私の特定の問題でこれを試し、あなたにお返しします。私はこれを5000 * 5000の行列に使用して、それがどのようになるかを見たいと思います。 – JEquihua

+1

@nneonneo uniform_filterは実際にこの記事の最初の繰り返しで使用した答えでした。 – Daniel

2

問題がx[1,x[0,:]+1]にあり、第2軸のインデックス:x[0,:]+110がxのディメンションより大きい[1 2 3 4 5 6 7 8 9 10]です。 x[1,x[0,:]-1]の場合

、第二軸のインデックスは9が最後の要素であると-1のインデックスを持っているとして、あなたが、[9 0 1 2 3 4 5 6 7 8]を取得してしまう、[-1 0 1 2 3 4 5 6 7 8 9]です。最後から2番目の要素のインデックスは-2です。基本的に何が起こっているかnp.where((x[0,:]<5)&(x[0,:]>0),x[1,x[0,:]-1],x[1,:])x[0,:]=[0 1 2 3 4 5 6 7 8 9]

は、x[0,0]が0であるとx[0,:]<5)&(x[0,:]>0Falseであるため、最初のセルは、フォームx[1,:]を取っていることです。次の4つの要素はx[1,x[0,:]-1]から取られます。残りはx[1,:]です。最後に結果が[0 0 1 2 3 4 5 6 7 8]

あるちょうど1セルのウィンドウをスライドさせるためのOKのように見えるかもしれないが、それはをごつもり驚きです:あなたは2つのセルの窓で、それを移動しようとすると

>>> np.where((x[0,:]<5)&(x[0,:]>0),x[1,x[0,:]-2],x[1,:]) 
array([0, 9, 0, 1, 2, 5, 6, 7, 8, 9]) 

。この特定の問題については

、私たちは、1行でこれ、やるだろうすべてのものを維持したい場合:

>>> for i in [1, 2, 3, 4, 5, 6]: 
    print hstack((np.where(x[1,x[0,:]-i]<x[0, -i], x[1,x[0,:]-i], 0)[:5], x[0,5:])) 

[0 0 1 2 3 5 6 7 8 9] 
[0 0 0 1 2 5 6 7 8 9] 
[0 0 0 0 1 5 6 7 8 9] 
[0 0 0 0 0 5 6 7 8 9] 
[0 0 0 0 0 5 6 7 8 9] 
[0 0 0 0 0 5 6 7 8 9] 

編集: を今、私は基本的にあなたが2Dをしたい、もっと自分の元の質問を理解します各セルの周りのN * Nセル平均を計算する。それはかなり一般的です。まず、Nを奇数に制限したいと思うかもしれません。そうしないと、セルの周りの2 * 2の平均が定義するのが難しくなります。私たちは3 * 3の平均をしたいとします:

#In this example, the shape is (10,10) 
>>> a1=\ 
array([[3, 7, 0, 9, 0, 8, 1, 4, 3, 3], 
    [5, 6, 5, 2, 9, 2, 3, 5, 2, 9], 
    [0, 9, 8, 5, 3, 1, 8, 1, 9, 4], 
    [7, 4, 0, 0, 9, 3, 3, 3, 5, 4], 
    [3, 1, 2, 4, 8, 8, 2, 1, 9, 6], 
    [0, 0, 3, 9, 3, 0, 9, 1, 3, 3], 
    [1, 2, 7, 4, 6, 6, 2, 6, 2, 1], 
    [3, 9, 8, 5, 0, 3, 1, 4, 0, 5], 
    [0, 3, 1, 4, 9, 9, 7, 5, 4, 5], 
    [4, 3, 8, 7, 8, 6, 8, 1, 1, 8]]) 
#move your original array 'a1' around, use range(-2,2) for 5*5 average and so on 
>>> movea1=[a1[np.clip(np.arange(10)+i, 0, 9)][:,np.clip(np.arange(10)+j, 0, 9)] for i, j in itertools.product(*[range(-1,2),]*2)] 
#then just take the average 
>>> averagea1=np.mean(np.array(movea1), axis=0) 
#trim the result array, because the cells among the edges do not have 3*3 average 
>>> averagea1[1:10-1, 1:10-1] 
array([[ 4.77777778, 5.66666667, 4.55555556, 4.33333333, 3.88888889, 
    3.66666667, 4.  , 4.44444444], 
    [ 4.88888889, 4.33333333, 4.55555556, 3.77777778, 4.55555556, 
    3.22222222, 4.33333333, 4.66666667], 
    [ 3.77777778, 3.66666667, 4.33333333, 4.55555556, 5.  , 
    3.33333333, 4.55555556, 4.66666667], 
    [ 2.22222222, 2.55555556, 4.22222222, 4.88888889, 5.  , 
    3.33333333, 4.  , 3.88888889], 
    [ 2.11111111, 3.55555556, 5.11111111, 5.33333333, 4.88888889, 
    3.88888889, 3.88888889, 3.55555556], 
    [ 3.66666667, 5.22222222, 5.  , 4.  , 3.33333333, 
    3.55555556, 3.11111111, 2.77777778], 
    [ 3.77777778, 4.77777778, 4.88888889, 5.11111111, 4.77777778, 
    4.77777778, 3.44444444, 3.55555556], 
    [ 4.33333333, 5.33333333, 5.55555556, 5.66666667, 5.66666667, 
    4.88888889, 3.44444444, 3.66666667]]) 

あなたは2D配列を平らにする必要はないと思うので混乱の原因になります。また、エッジ要素をトリム以外の方法で扱う場合は、「元の配列を移動する」ステップでnp.maを使用してマスクされた配列を作成することを検討してください。

+0

他の方法で動作しないのはなぜですか?10が再び最初の要素ですか?それでは、私が望むことをどうすればできますか? – JEquihua

+0

ああMatlabとは異なり、Pythonのインデックスは0から始まります。したがって、正の 'int'を使うと、長さ10のベクトルの最大インデックスは9になり、x [10]を試してみると' indexError'が得られます。 'x = [0 1 2 3 4 5 6 7 8 9]'の場合、9を得るために 'x [-1]'か 'x [9]'のどちらかが実行されますが、 'x [ない。 –

+0

私は実際に達成したいことを示すために質問を編集します。私はちょうど長い質問がほしいと思わなかったが、ここに行く。私はあなたが私を少し誤解していると思います。 – JEquihua

4

2Dコンボルーションを計算しようとしているようです。あなたがscipyを使用することができますならば、私はscipy.signal.convolve2dを試みることをお勧めします:あなたはそれを構成するループにconvolve2dを「アンロール」場合

matriz = np.random.randn(10, 10) 

# to average a 3x3 neighborhood 
kernel = np.ones((3, 3), float) 

# to compute the mean, divide by size of neighborhood 
kernel /= kernel.sum() 

average = scipy.signal.convolve2d(matriz, kernel) 

これは、すべての3x3の地域の平均値を計算した理由を見ることができます。効果的に(とソースとカーネルアレイの端に何が起こるかを無視して)、それはコンピューティング:

カーネル内のすべての値があるので、場合
X, Y = kernel.shape 
for i in range(matriz.shape[0]): 
    for j in range(matriz.shape[1]): 
     for ii in range(X): 
      for jj in range(Y): 
       average[i, j] += kernel[ii, jj] * matriz[i+ii, j+jj] 

1 /(1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1)== 1/9、あなたは、上記のコードを書き換えることができる:

for i in range(matriz.shape[0]): 
    for j in range(matriz.shape[1]): 
     average[i, j] = 1./9 * matriz[i:i+X, j:j+Y].sum() 

3x3の領域にわたって、マトリズの値の平均を計算するとまったく同じである、で始まりますi, j

このようにすることの利点の1つは、カーネル内の値を適切に設定することによって、近隣に関連する重みを簡単に変更できることです。だから、あなたはそれぞれの周辺に他の人の2倍の重量を中心値を与えたいと思った場合、たとえば、あなたがこのようなカーネルを構築することができ:

kernel = np.ones((3, 3), float) 
kernel[1, 1] = 2. 
kernel /= kernel.sum() 

と畳み込み符号は同じままだろうが計算は異なるタイプの平均(「中心重み付け」)をもたらす。ここには多くの可能性があります。うまくいけば、これはあなたがやっている仕事の素敵な抽象化を提供します。

3

Scipyの標準ライブラリでは、スライディングウインドウの平均を非常に高速に計算する関数が存在します。これはuniform_filterと呼ばれています。あなたは次のようにあなたの平均の近傍機能を実装するためにそれを使用することができます:

from scipy.ndimage.filters import uniform_filter 
def neighbourhood_average(arr, win=3): 
    sums = uniform_filter(arr, win, mode='constant') * (win*win) 
    return ((sums - arr)/(win*win - 1)) 

これはX[i,j]i,j自体を除くarri,jのすべてのネイバーの平均である配列Xを返します。最初と最後の列と最初と最後の行は境界条件の対象であるため、アプリケーションには無効である可能性があります(必要に応じてmode=を使用して境界ルールを制御できます)。

は、ストレートC(直線のサイズがarr)で実装された非常に効率的な線形時間アルゴリズムを使用しているため、特にwinが大きい場合は他のソリューションよりも優れています。

+0

非常に興味深い。境界条件にはどのような条件がありますか?私はいつもの条件を欲しいと思うが、私はそれを私の質問に投稿しなかった。これは(i、j)自体をどのように除外していますか?コードを少し説明してもらえますか? – JEquihua

+0

'uniform_filter'はデフォルトでウィンドウを各'(i、j) 'の中央に配置します。 3×3ウィンドウ「(i-1:i + 2、j-1:j + 2)」を生成する。元の配列の外側にある値の場合、 'uniform_filter'は' mode'で決まるフィル値を使います。不完全なウィンドウを気にしない場合は、最初と最後の行、最初と最後の列を削除または0にするだけです。 – nneonneo

+1

'(i、j)'は ' - arr'ビットのために除外されます。これは、ウィンドウの合計から元の値を削除します。 – nneonneo

関連する問題