按维度上的范数对多维NumPy数组进行排序

vmjh9lq9  于 2023-05-17  发布在  其他
关注(0)|答案(2)|浏览(195)

我正在使用一个多维NumPy数组a,它是2x2矩阵的“向量”。我想对a排序,使得2x2矩阵按其行范数排序。

import numpy as np
a = np.array([[[3, 4],
               [1, 2]],

              [[5, 6],
               [7, 8]]])    
sortidxs = np.argsort(np.linalg.norm(a, axis=-1))
a = np.array([a[_][sortidxs[_]] for _ in range(a.shape[0])])

# And the final output should be:

print(a)
[[[1 2]
  [3 4]]

  [[5 6]
  [7 8]]]

上面的代码片段做了我正在寻找的(不完全,看看下面的编辑)。但我一直在寻找一种避免循环的方法

a = np.array([a[_][sortidxs[_]] for _ in range(a.shape[0])])

--编辑--

上面的例子忽略了问题的关键部分。a可以具有更多的“空”维度,即

a = np.array([[[3, 4],
               [1, 2]],

              [[5, 6],
               [7, 8]]])
a = a.reshape((2,1,2,2))

a现在看起来像:

In [257]: a
Out[257]: 
array([[[[3, 4],
         [1, 2]]],

       [[[5, 6],
         [7, 8]]]])

排序后应该是

In [259]: a
Out[259]: 
array([[[[1, 2],
         [3, 4]]],

       [[[5, 6],
         [7, 8]]]])

a也可以具有以下尺寸(1,2,2,2)或更多这样的“空”尺寸。我也希望排序在这些情况下起作用。

1cklez4t

1cklez4t1#

可以使用advanced-indexing-

a[np.arange(a.shape[0])[:,None], sortidxs]

样品运行-

In [144]: a = np.random.randint(0,9,(2,3,4))

In [145]: a
Out[145]: 
array([[[1, 1, 5, 5],
        [1, 1, 7, 5],
        [6, 1, 2, 8]],

       [[7, 2, 5, 4],
        [3, 7, 3, 7],
        [8, 4, 4, 6]]])

In [146]: sortidxs = np.argsort(np.linalg.norm(a, axis=-1))

In [147]: np.array([a[_][sortidxs[_]] for _ in range(a.shape[0])])
Out[147]: 
array([[[1, 1, 5, 5],
        [1, 1, 7, 5],
        [6, 1, 2, 8]],

       [[7, 2, 5, 4],
        [3, 7, 3, 7],
        [8, 4, 4, 6]]])

In [149]: a[np.arange(a.shape[0])[:,None], sortidxs]
Out[149]: 
array([[[1, 1, 5, 5],
        [1, 1, 7, 5],
        [6, 1, 2, 8]],

       [[7, 2, 5, 4],
        [3, 7, 3, 7],
        [8, 4, 4, 6]]])

性能进一步提升

我们可以用np.einsum优化sortidxs的计算-

sortidxs = np.einsum('ijk,ijk->ij',a,a).argsort()

我们来计时并验证这个想法-

In [94]: a = np.random.randint(0,9,(20,30,40))

In [95]: %timeit np.argsort(np.linalg.norm(a, axis=-1))
10000 loops, best of 3: 63.5 µs per loop

In [96]: %timeit np.einsum('ijk,ijk->ij',a,a).argsort()
10000 loops, best of 3: 19.7 µs per loop

In [97]: a = np.random.randint(0,9,(200,300,400))

In [98]: %timeit np.argsort(np.linalg.norm(a, axis=-1))
10 loops, best of 3: 88.6 ms per loop

In [99]: %timeit np.einsum('ijk,ijk->ij',a,a).argsort()
10 loops, best of 3: 22.6 ms per loop

更高维数组

对于a是一个4D数组的额外情况,我们需要使用更多的数组进行索引。
1]对于第一轴:使用np.arange(a.shape[0]),并在末尾添加两个新轴。
2]对于第二轴:使用np.arange(a.shape[0]),并在末尾添加一个新轴。
3]对于第三轴:使用sortidxs索引到这个。
因此,我们将有:

m,n,r,s = a.shape
out = a[np.arange(m)[:,None,None],np.arange(n)[:,None], sortidxs]

singleton dim(dim with length=1)数组

作为一种特殊情况,假设输入数组的第二个轴已经是单例的,我们可以简单地使用0作为该轴,从而简化事情,如下所示-

a[np.arange(m)[:,None,None],0, sortidxs]

样品运行-

In [58]: a = np.array([[[3, 4],
    ...:                [1, 2]],
    ...: 
    ...:               [[5, 6],
    ...:                [7, 8]]])
    ...: 
    ...: a = a.reshape((2,1,2,2))
    ...: 

In [59]: sortidxs = np.argsort(np.linalg.norm(a, axis=-1))

In [60]: a[np.arange(a.shape[0])[:,None,None],0, sortidxs]
Out[60]: 
array([[[[1, 2],
         [3, 4]]],

       [[[5, 6],
         [7, 8]]]])

另一个示例运行了一个通用形状为(2,3,4)的数组,以使事情变得非常清楚-

In [70]: a = np.random.randint(0,9,(2,1,3,4))

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

       [[[3, 6, 0, 4],
         [6, 2, 5, 2],
         [0, 8, 0, 8]]]])

In [72]: sortidxs = np.argsort(np.linalg.norm(a, axis=-1))

In [73]: sortidxs
Out[73]: 
array([[[1, 2, 0]],

       [[0, 1, 2]]])

In [74]: a[np.arange(a.shape[0])[:,None,None],0, sortidxs]
Out[74]: 
array([[[[4, 0, 1, 0],
         [5, 3, 2, 5],
         [6, 4, 8, 6]]],

       [[[3, 6, 0, 4],
         [6, 2, 5, 2],
         [0, 8, 0, 8]]]])
wi3ka0sx

wi3ka0sx2#

由于sortidxs包含每个轴(从开始到结束)的所需索引,因此可以通过np.arange(a.shape[0])生成第一个轴范围,并在索引时将其作为第一个轴传递:

In [31]: x,y, z = a.shape
In [32]: i, j = sortidxs.shape
In [33]: a[np.repeat(np.arange(x)[:, none], i, 1),sortidxs]

Out[33]: 
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

作为一种更简单的方式,在这种情况下(正如你在评论中提到的),你可以只传递np.arange(x)[:, None]而不使用repeat()函数,但是如果你想要沿着第二和第三个索引的项目的变量数量,或者其他维度数组repeat会给予你正确的答案。还要注意,在这些情况下,您也可以分别沿着每个轴传递相应的索引。

In [107]: a[np.arange(x)[:, None],sortidxs]
Out[107]: 
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

相关问题