简体   繁体   中英

Sort N-D numpy array by column with a smaller N-D array

I understand that through Sort ND numpy array by another 1-D array the use of fancy indexing, I can do the following c = a[:, :, b] with b defining the order to which I want to sort by column

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

       [[ 5,  4],
        [ 7,  6]],

       [[ 9,  8],
        [11, 10]]])

Now I increase b with 2 more inputs to b2 corresponding to how I want to sort each set of 2x2 in a

>>> b2 = np.array([[1, 0], [0, 1], [1, 0]])
>>> c2 = ?
>>> c2
array([[[ 1,  0],
        [ 3,  2]],

       [[ 4,  5],
        [ 6,  7]],

       [[ 9,  8],
        [11, 10]]])

I have a larger set of inputs and I have a function that returns an array similar to 'b2' which provides me the info to which I should obtain. Hence may I know what should I filling into the c2 = ? in order to get the desired result?

Here's one approach with fancy-indexing -

(a[np.arange(a.shape[0])[:,None],:,b2]).transpose(0,2,1)

Sample run -

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

       [[8, 0, 5, 5, 7],
        [4, 3, 4, 0, 1],
        [8, 6, 3, 2, 4]],

       [[3, 2, 7, 3, 7],
        [4, 3, 0, 1, 5],
        [4, 3, 7, 8, 7]]])

In [192]: b2
Out[192]: 
array([[1, 2, 4, 3, 0],
       [4, 2, 0, 1, 3],
       [1, 3, 4, 0, 2]])

In [193]: (a[np.arange(a.shape[0])[:,None],:,b2]).transpose(0,2,1)
Out[193]: 
array([[[8, 5, 0, 2, 7],
        [7, 0, 1, 7, 6],
        [6, 5, 0, 4, 7]],

       [[7, 5, 8, 0, 5],
        [1, 4, 4, 3, 0],
        [4, 3, 8, 6, 2]],

       [[2, 3, 7, 3, 7],
        [3, 1, 5, 4, 0],
        [3, 8, 7, 4, 7]]])

In case nobody finds a pure fancy-indexing solution, this here is one that loops through the first axis:

np.asarray([a[n,:,p] for n,p in enumerate(b2)])

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

   [[ 4,  6],
    [ 5,  7]],

   [[ 9, 11],
    [ 8, 10]]])

Something similar to @Divakar's solution but without the transpose .

In [259]: I,J, K = np.ogrid[:3,:2,:2]

In [260]: a[I, J, b[:,None,:]]
Out[260]: 
array([[[ 1,  0],
        [ 3,  2]],

       [[ 4,  5],
        [ 6,  7]],

       [[ 9,  8],
        [11, 10]]])

I'm using ogrid (or np.ix_ ) as a compact way of generating 2 3d arrays that broadcast with b[:,None,:] to produce a set of (3,2,2) indices.

The equivalent with full Nones is:

a[np.arange(3)[:,None,None], np.arange(2)[None,:,None], b[:,None,:]]

(this makes it clearer that b is selecting items along the 1st and last axes)

To see the full broadcasted indexing arrays, print:

 np.broadcast_arrays(I,J,b[:,None,:])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM