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]])
np.take_along_axisapply?