numpyを使用した奇妙なインデックス付け


27

(2,2,50,100)という形の変数xがあります。

また、np.array([0,10,20])と等しい配列yもあります。x [0、:、:、y]にインデックスを付けると、奇妙なことが起こります。

x = np.full((2,2,50,100),np.nan)
y = np.array([0,10,20])
print(x.shape)
(2,2,50,100)
print(x[:,:,:,y].shape)
(2,2,50,3)
print(x[0,:,:,:].shape)
(2,50,100)
print(x[0,:,:,y].shape)
(3,2,50)

最後の1つが(2,50,3)ではなく(3,2,50)を出力するのはなぜですか?


私はnumpyが初めてなので、質問に対する答えはありません。これをさらに調査するために、2Dまたは3Dのみであり、任意の軸で最大10個の要素にのみ似ている小さな例を見つけることをお勧めします。
コード見習い

回答:


21

これは、numpyが高度なインデックスを使用して配列形状をブロードキャストする方法です。0最初のインデックスとy最後のインデックスにaを渡すと、numpyはと0同じ形になるようにをブロードキャストしますy。次の同等性が成立しますx[0,:,:,y] == x[(0, 0, 0),:,:,y]。ここに例があります

import numpy as np

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

np.equal(x[0,:,:,y], x[(0, 0, 0),:,:,y]).all()
# returns:
True

ここで、2つのセットのインデックスを効果的に渡すため、高度なインデックスAPIを使用して(この場合は)インデックスのペアを作成します。

x[(0, 0, 0),:,:,y])

# equivalent to
[
  x[0,:,:,y[0]], 
  x[0,:,:,y[1]], 
  x[0,:,:,y[2]]
]

# equivalent to
rows = np.array([0, 0, 0])
cols = y
x[rows,:,:,cols]

# equivalent to
[
  x[r,:,:,c] for r, c in zip(rows, columns)
]

これには、の長さと同じ最初の次元がありyます。これはあなたが見ているものです。

例として、次のチャンクで説明されている4次元の配列を見てください。

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

# x looks like:
array([[[[  0,   1,   2,   3,   4],    -+      =+
         [  5,   6,   7,   8,   9],     Sheet1  |
         [ 10,  11,  12,  13,  14],     |       |
         [ 15,  16,  17,  18,  19]],   -+       |
                                                Workbook1
        [[ 20,  21,  22,  23,  24],    -+       |
         [ 25,  26,  27,  28,  29],     Sheet2  |
         [ 30,  31,  32,  33,  34],     |       |
         [ 35,  36,  37,  38,  39]],   -+       |
                                                |
        [[ 40,  41,  42,  43,  44],    -+       |
         [ 45,  46,  47,  48,  49],     Sheet3  |
         [ 50,  51,  52,  53,  54],     |       |
         [ 55,  56,  57,  58,  59]]],  -+      =+


       [[[ 60,  61,  62,  63,  64],
         [ 65,  66,  67,  68,  69],
         [ 70,  71,  72,  73,  74],
         [ 75,  76,  77,  78,  79]],

        [[ 80,  81,  82,  83,  84],
         [ 85,  86,  87,  88,  89],
         [ 90,  91,  92,  93,  94],
         [ 95,  96,  97,  98,  99]],

        [[100, 101, 102, 103, 104],
         [105, 106, 107, 108, 109],
         [110, 111, 112, 113, 114],
         [115, 116, 117, 118, 119]]]])

x 非常にわかりやすいシーケンシャルフォームがあり、これを使用して何が起こっているかを示すことができます...

最初の次元は2つのExcelワークブックを持つようなもので、2番目の次元は各ワークブックに3つのシートを持つようなものです。3番目の次元はシートごとに4行を持つようなもので、最後の次元は各行(またはシートごとの列)に対して5つの値です。

このように見て、を求めるのはx[0,:,:,0]、「最初のワークブックでは、各シート、各行、最初の値/列を教えてください」ということです。

x[0,:,:,y[0]]
# returns:
array([[ 0,  5, 10, 15],
       [20, 25, 30, 35],
       [40, 45, 50, 55]])

# this is in the same as the first element in:
x[(0,0,0),:,:,y]

しかし、高度なインデックス作成を使用すると、x[(0,0,0),:,:,y]「最初のワークブックでは、各シート、各行、yth番目の値/列を取得できます。OKの各値に対して実行してくださいy

x[(0,0,0),:,:,y]
# returns:
array([[[ 0,  5, 10, 15],
        [20, 25, 30, 35],
        [40, 45, 50, 55]],

       [[ 2,  7, 12, 17],
        [22, 27, 32, 37],
        [42, 47, 52, 57]],

       [[ 4,  9, 14, 19],
        [24, 29, 34, 39],
        [44, 49, 54, 59]]])

頭がおかしいのは、numpyがブロードキャストして、インデックス配列の外側の次元に一致することです。したがって、上記と同じ操作を実行する必要がある場合は、「Excelワークブック」の両方について、ループして連結する必要はありません。最初の次元に配列を渡すことができますが、互換性のある形状でなければなりません

整数を渡すと、にブロードキャストされy.shape == (3,)ます。配列を最初のインデックスとして渡す場合、配列の最後の次元のみがと互換性があればよいy.shape。つまり、最初のインデックスの最後の次元は3または1でなければなりません。

ix = np.array([[0], [1]])
x[ix,:,:,y].shape
# each row of ix is broadcast to length 3:
(2, 3, 3, 4)

ix = np.array([[0,0,0], [1,1,1]])
x[ix,:,:,y].shape
# this is identical to above:
(2, 3, 3, 4)

ix = np.array([[0], [1], [0], [1], [0]])
x[ix,:,:,y].shape
# ix is broadcast so each row of ix has 3 columns, the length of y
(5, 3, 3, 4)

ドキュメントで短い説明が見つかりました:https : //docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing


編集:

元の質問から、希望するサブスライスのワンライナーを取得するには、以下を使用できますx[0][:,:,y]

x[0][:,:,y].shape
# returns
(2, 50, 3)

ただし、割り当てようとしている場合これらのサブスライスている場合は、元の配列の共有メモリビューを確認するように十分注意する必要があります。そうでない場合、割り当ては元の配列ではなくコピーになります。

共有メモリは、あなたがあなたの配列をサブセットする整数またはスライスを使用しているとき、すなわち、発生しますx[:,0:3,:,:]x[0,:,:,1:-1]

np.shares_memory(x, x[0])
# returns:
True

np.shares_memory(x, x[:,:,:,y])
# returns:
False

元の質問と私の例の両方で y両方で、intでもスライスでもないため、常にのコピーに割り当てられます。

だが!以下のためのあなたの配列があるためy、スライスのように表すことができ、あなたはCAN実際経由して、あなたの配列の割り当て可能なビューを取得:

x[0,:,:,0:21:10].shape
# returns:
(2, 50, 3)

np.shares_memory(x, x[0,:,:,0:21:10])
# returns:
True

# actually assigns to the original array
x[0,:,:,0:21:10] = 100

ここでは、スライスを使用して、にある0:21:10すべてのインデックスを取得しrange(0,21,10)ます。私たちは、使用する必要が21ない20だけのように、ストップポイントがスライスから除外されているため、range関数の。

したがって、基本的には、サブスライス基準に適合するスライスを作成できれば、割り当てを行うことができます。


4

と呼ばれていcombining advanced and basic indexingます。でcombining advanced and basic indexing、numpyは最初に高度なインデックス付けでインデックス付けを行い、結果をサブスペース/基本的なインデックス付けの次元に連結します。

ドキュメントの例:

x.shapeを(10,20,30,40,50)とし、ind_1とind_2を形状(2,3,4)にブロードキャストできるとします。次に、X [:、ind_1、ind_2]の形状は(10,2,3,4,40,50)になります。これは、Xの(20,30)形状の部分空間が、(2,3,4)の部分空間に置き換えられたためです。インデックス。ただし、x [:、ind_1、:、ind_2]の形状は(2,3,4,10,30,50)です。これは、インデックスサブスペースにドロップするための明確な場所がないため、最初に追加されるためです。.transpose()を使用してサブスペースを必要な場所に移動することは常に可能です。この例は、takeを使用して複製できないことに注意してください。

そう、上x[0,:,:,y]0およびy事前のインデックスです。それらは一緒に放送されて次元を生み出します(3,)

In [239]: np.broadcast(0,y).shape
Out[239]: (3,)

この (3,)は、2次元と3次元の始めにタックして、(3, 2, 50)

最初と最後のディメンションが実際に一緒にブロードキャストされている0こと[0,1]を確認するには、に変更して、ブロードキャストのエラーを確認します。

print(x[[0,1],:,:,y])

Output:
IndexError                                Traceback (most recent call last)
<ipython-input-232-5d10156346f5> in <module>
----> 1 x[[0,1],:,:,y]

IndexError: shape mismatch: indexing arrays could not be broadcast together with
 shapes (2,) (3,)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.