nye17
nye17

Reputation: 13347

puzzled on how to slice a numpy array

m is a ndarray with shape (12, 21, 21), now I want to take only a sparse slice of it to form a new 2D array, with

sliceid = 0
indx    = np.array([0, 2, 4, 6, 8, 10])

so that sparse_slice is, intuitively,

sparse_slice = m[sliceid, indx, indx]

but apparently the above operation does not work, currently what I am using is

sparse_slice = m[sliceid,indx,:][:, indx]

why is the first "intuitive" way not working? and is there a more compact way than my current solution? all my previous ndarray slicing trials were based on nothing but intuition, maybe I shall switch to read some serious manual now...

Upvotes: 4

Views: 3381

Answers (2)

Bi Rico
Bi Rico

Reputation: 25813

The more compact way is to do new = m[0, :12:2, :12:2]. This is what the numpy docs call "basic indexing" meaning that you slice with an integer or a slice object (ie 0:12:2). When you use basic indexing numpy returns a view of the original array. For example:

In [3]: a = np.zeros((2, 3, 4))

In [4]: b = a[0, 1, ::2]

In [5]: b
Out[5]: array([ 0.,  0.])

In [6]: b[:] = 7

In [7]: a
Out[7]: 
array([[[ 0.,  0.,  0.,  0.],
        [ 7.,  0.,  7.,  0.],
        [ 0.,  0.,  0.,  0.]],

       [[ 0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  0.]]])

In you "intuitive" approach what you're doing is indexing an array with another array. When you index an numpy array with another array the arrays need to be the same size (or they need to broadcast against each other, more on this in a sec). In the docs this is called fancy indexing or advanced indexing. For example:

In [10]: a = np.arange(9).reshape(3,3)

In [11]: a
Out[11]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [12]: index = np.array([0,1,2])

In [13]: b = a[index, index]

In [14]: b
Out[14]: array([0, 4, 8])

You see that I get a[0,0], a[1,1], and a[2,2] not a[0,0], a[0,1] ... If you want the "outer product" of index with index you can do the following.

In [22]: index1 = np.array([[0,0],[1,1]])

In [23]: index2 = np.array([[0,1],[0,1]])

In [24]: b = a[index1, index2]

In [25]: b
Out[25]: 
array([[0, 1],
       [3, 4]])

There is a shorthand for doing the above, like this:

In [28]: index = np.array([0,1])

In [29]: index1, index2 = np.ix_(index, index)

In [31]: index1
Out[31]: 
array([[0],
       [1]])

In [32]: index2
Out[32]: array([[0, 1]])

In [33]: a[index1, index2]
Out[33]: 
array([[0, 1],
       [3, 4]])

In [34]: a[np.ix_(index, index)]
Out[34]: 
array([[0, 1],
       [3, 4]])

You'll notice that index1 is (2, 1) and index2 is (1, 2), not (2, 2). That's because the two arrays get broadcast against one another, you can read more about broadcasting here. Keep in mind that when you're using fancy indexing you get a copy of the original data not a view. Sometimes this is better (if you want to leave the original data unchanged) and sometimes it just takes more memory. More about indexing here.

Upvotes: 5

wim
wim

Reputation: 362707

If I'm not mistaken, for input m = np.array(range(5292)).reshape(12,21,21) you are expecting output sparse_slice = m[sliceid,indx,:][:, indx] of

array([[  0,   2,   4,   6,   8,  10],
       [ 42,  44,  46,  48,  50,  52],
       [ 84,  86,  88,  90,  92,  94],
       [126, 128, 130, 132, 134, 136],
       [168, 170, 172, 174, 176, 178],
       [210, 212, 214, 216, 218, 220]])

In that case, you can get it using the step part of a slice:

m[0, :12:2, :12:2]

Upvotes: 2

Related Questions