2

I have an array a of dimension n, and an array b of dimension n-1. The values of the last axis of b correspond to the indexes of the values I want to extract from the array a, in an array res of dimension n-1.

For exemple with n=2 :

a = np.array([[1,  2,  3,  4],
              [5,  6,  7,  8],
              [9, 10, 11, 12])
b = np.array([1,3,0])

I would like

res = [a[1], a[3], a[0]]

# i.e. res = [2, 8, 9]

Is there a function that does it in an efficient way, with a higher number of dimensions ? I know I could use for loops, but I hope there is something more efficient.

EDIT:

With n=3, let a have a shape of (2,2,3).

Then b and res have shapes of (2,2):

a = np.array([[[ 1, 2, 3],
               [ 4, 5, 6]],
              [[ 7, 8, 9],
               [10,11,12]]])

b = np.array([[0,2],
              [1,2]]

# res = np.array([[1,6],
#                 [8,12]])
3
  • Related: The 2d case is answered here. Commented Oct 5, 2018 at 11:27
  • 1
    Would the new np.take_along_axis apply? Commented Oct 5, 2018 at 11:32
  • Tkanks it works perfectly ! I need it at the right time ;) Commented Oct 5, 2018 at 13:14

1 Answer 1

1

The latest numpy (1.15) has added a take_along_axis function:

In [36]: np.take_along_axis(a, b[:,None], 1)
Out[36]: 
array([[2],
       [8],
       [9]])

It uses a helper function to construct an indexing tuple:

In [37]: np.lib.shape_base._make_along_axis_idx((3,4), b[:,None], 1)
Out[37]: 
(array([[0],
        [1],
        [2]]), 
 array([[1],
        [3],
        [0]]))

Prior to this, I (and others) would have recommended:

In [38]: a[np.arange(3), b]
Out[38]: array([2, 8, 9])

which is essentially the same thing (except for an added dimension). As take_along_axis docs show this was designed to take things like the results of argsort along an axis.

for the higher dimension case:

In [39]: a1 = np.array([[[ 1, 2, 3],
    ...:                [ 4, 5, 6]],
    ...:               [[ 7, 8, 9],
    ...:                [10,11,12]]])
    ...: b1 = np.array([[0,2],
    ...:               [1,2]])               
In [40]: a1.shape
Out[40]: (2, 2, 3)
In [41]: b1.shape
Out[41]: (2, 2)

In [42]: np.take_along_axis(a1, b1[...,None], -1)
Out[42]: 
array([[[ 1],
        [ 6]],

       [[ 8],
        [12]]])

In [45]: np.lib.shape_base._make_along_axis_idx(a1.shape, b1[...,None], 2)
Out[45]: 
(array([[[0]],

        [[1]]]), 
 array([[[0],
         [1]]]), 
 array([[[0],
         [2]],

        [[1],
         [2]]]))
In [46]: [i.shape for i in _]
Out[46]: [(2, 1, 1), (1, 2, 1), (2, 2, 1)]

Again, the equivalent do-it-yourself indexing:

In [48]: a1[np.arange(2)[:,None], np.arange(2)[None,:], b1]
Out[48]: 
array([[ 1,  6],
       [ 8, 12]])

Once you understand array broadcasting and how it applies to indexing, the concepts here are not difficult. But the take_along_axis may make applying them easier. It is in a sense an extension of np.ix_.

In [50]: np.ix_(np.arange(2), np.arange(2), np.arange(3))
Out[50]: 
(array([[[0]],

        [[1]]]), array([[[0],
         [1]]]), array([[[0, 1, 2]]]))
In [51]: [i.shape for i in _]
Out[51]: [(2, 1, 1), (1, 2, 1), (1, 1, 3)]
In [55]: a1[(*np.ix_(np.arange(2), np.arange(2)),b1)]
Out[55]: 
array([[ 1,  6],
       [ 8, 12]])
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.