NumPy 2d配列のスライス、またはnxn配列(n> m)からmxmサブマトリックスを抽出するにはどうすればよいですか?


174

NumPy nxn配列をスライスしたいと思います。その配列のm行と列の任意の選択を抽出します(つまり、行/列の数にパターンがない)。これを新しいmxm配列にします。この例では、配列が4x4であり、そこから2x2配列を抽出するとします。

これが私たちの配列です:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

削除する行と列は同じです。最も簡単なケースは、最初または最後にある2x2サブマトリックスを抽出する場合です。

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

しかし、行/列の別の混合を削除する必要がある場合はどうなりますか?最初と3番目の行/行を削除して、部分行列を抽出する必要がある場合[[5,7],[13,15]]ますか?行/行の構成は任意です。行と列の両方のインデックスの配列/リストを使用して配列にインデックスを付ける必要があることをどこかで読みましたが、それはうまくいかないようです:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

私は一つの方法を見つけました、それは:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

これの最初の問題は、私はそれと一緒に暮らすことができますが、ほとんど読むことができないということです。誰かがより良い解決策を持っているなら、私は確かにそれを聞きたいです。

もう1つは配列で配列にインデックスを付けるとNumPyが目的の配列のコピーを作成するように強制されることをフォーラムで読んだことです。そのため、大きな配列を扱う場合、これが問題になる可能性があります。なぜそうなのか/このメカニズムはどのように機能するのですか?

回答:


62

Svenが述べたように、x[[[0],[2]],[1,3]]1と3の列と一致する0と2の行をx[[0,2],[1,3]]返し、配列で値x [0,1]とx [2,3]を返します。

最初に挙げた例を実行するのに役立つ関数がありますnumpy.ix_。最初の例と同じことをで行うことができますx[numpy.ix_([0,2],[1,3])]。これにより、これらの余分な括弧をすべて入力する必要がなくなります。


111

この質問に答えるには、Numpyで多次元配列にインデックスを付ける方法を調べる必要があります。まずx、質問の配列があるとしましょう。に割り当てられたバッファにxは、0から15までの16個の昇順の整数が含まれます。たとえばx[i,j]、1つの要素にアクセスする場合、NumPyは、バッファの先頭からのこの要素のメモリ位置を把握する必要があります。これはi*x.shape[1]+j、実際に計算して(実際のメモリオフセットを取得するためにintのサイズを乗算して)行われます。

のような基本的なスライスによってサブ配列を抽出する場合y = x[0:2,0:2]、結果のオブジェクトは基になるバッファをと共有しxます。しかし、アクセスするとどうなりますy[i,j]か?NumPy i*y.shape[1]+jは配列へのオフセットの計算に使用できません。yはメモリ内で連続していない。

NumPyはストライドを導入することでこの問題を解決します。にアクセスするためのメモリオフセットを計算する場合、x[i,j]実際に計算されるのはi*x.strides[0]+j*x.strides[1]次のとおりです(これには、intのサイズの係数がすでに含まれています)。

x.strides
(16, 4)

y上記のように抽出された場合、NumPyは新しいバッファーを作成しませんが、同じバッファーを参照する新しい配列オブジェクトを作成ます(それ以外の場合yは、単にと同じになりxます)。新しい配列オブジェクトは異なる形状になります。xが異なり、場合によっては開始が異なりますバッファへのオフセットですが、x(この場合は少なくとも)ストライドを共有します。

y.shape
(2,2)
y.strides
(16, 4)

この方法で、メモリオフセットを計算するy[i,j]と、正しい結果が得られます。

しかし、NumPyは何かのために何をすべきz=x[[1,3]]ですか?ストライドメカニズムでは、元のバッファがに使用されてzいる場合、正しいインデックス作成が許可されません。NumPyは理論的にはストライドよりも洗練されたメカニズムを追加できますが、これは要素のアクセスを比較的高価にし、配列の全体的な考えに逆らってしまいます。さらに、ビューはもはや本当に軽量なオブジェクトではなくなります。

これについては、インデックスに関するNumPyドキュメントで詳しく説明しています。ます。

ああ、そしてあなたの実際の質問をほとんど忘れていました:複数のリストを使ったインデックス作成を期待どおりに機能させる方法は次のとおりです:

x[[[1],[3]],[1,3]]

これは、インデックス配列が共通の形でブロードキャストされるためです。もちろん、この特定の例では、基本的なスライスを行うこともできます。

x[1::2, 1::2]

配列をサブクラス化して、インデックスを元の配列に再マップする「slcie-view」オブジェクトを作成できるようにする必要があります。それはおそらくOPのニーズを満たす可能性があります
jsbueno 2010年

@jsbueno:Pythonコードでは機能しますが、Scipy / NumpyがラップするC / Fortranルーチンでは機能しません。これらのラップされたルーチンは、Numpyの威力があります。
Dat Chu

Soo .. x [[[1]、[3]]、[1,3]]とx [[1,3]、:] [:、[1,3]]の違いは何ですか?つまり、他よりも使用する方が良いバリアントがあるのですか?
レベスク2010年

1
@JC:2 x[[[1],[3]],[1,3]]つのx[[1,3],:][:,[1,3]]コピー中に1つの新しい配列のみを作成するため、最初の配列を使用します。
Sven Marnach、2010年

@JC:または、ジャスティンの答えからの方法を使用します。
Sven Marnach、2010年

13

x[[1,3]][:,[1,3]]読みにくいとは思いません。あなたの意図をより明確にしたい場合は、次のことができます:

a[[1,3],:][:,[1,3]]

私はスライスの専門家ではありませんが、通常、配列にスライスしようとして値が連続している場合、ストライド値が変更されたビューに戻ります。

たとえば、入力33と34では、2x2配列を取得しますが、ストライドは4です。したがって、次の行にインデックスを付けると、ポインターはメモリ内の正しい位置に移動します。

明らかに、このメカニズムはインデックスの配列の場合にはうまく機能しません。したがって、numpyはコピーを作成する必要があります。結局のところ、他の多くの行列数学関数は、サイズ、ストライド、連続メモリ割り当てに依存しています。


10

他のすべての行と他のすべての列をスキップする場合は、基本的なスライスでそれを行うことができます。

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

これは、配列のコピーではなくビューを返します。

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

while z=x[(1,3),:][:,(1,3)]は高度なインデックスを使用しているため、コピーを返します。

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

x変更されていないことに注意してください。

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

任意の行と列を選択する場合、基本的なスライスは使用できません。あなたは、のようなものを使用して、高度なインデックスを使用する必要がありますx[rows,:][:,columns]場所、rowsおよびcolumns配列です。もちろんこれにより、元の配列のビューではなくコピーが得られます。派手な配列は連続したメモリ(一定のストライドを使用)を使用し、任意の行と列を含むビューを生成する方法がないため(これは一定でないストライドを必要とするため)、これは予想どおりです。


5

numpyを使用すると、インデックスの各コンポーネントのスライスを渡すことができます-したがって、x[0:2,0:2]上記の例は機能します。

列または行を均等にスキップする場合は、3つのコンポーネント(開始、停止、ステップ)を含むスライスを渡すことができます。

ここでも、上記の例の場合:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

つまり、基本的には、最初の次元のスライスをインデックス1から開始し、インデックスが4以上のときに停止し、各パスでインデックスに2を追加します。2番目の次元についても同じです。繰り返しますが、これは一定のステップでのみ機能します。

内部的にかなり異なる構文を実行する必要がある構文- x[[1,3]][:,[1,3]]実際に行うことは、元の配列から行1と3のみを含む新しい配列を作成し(x[[1,3]]パーツで実行)、それを再スライスして-3 番目の配列を作成する-のみを含む前の配列の列1および3。


1
この解決策は、抽出しようとした行/列に固有であるため機能しません。同じことを50x50マトリックスで想像してください。行/列5、11、12、32、39、45を抽出する場合、単純なスライスでそれを行う方法はありません。質問がよくわからなかったらすみません。
レベスク


0

これがどれほど効率的かはわかりませんが、range()を使用して両方の軸でスライスできます

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.