【问题标题】:Sorting arrays in NumPy by column按列对 NumPy 中的数组进行排序
【发布时间】:2011-02-19 03:51:49
【问题描述】:

如何在 NumPy 中按第 n 列对数组进行排序?

例如,

a = array([[9, 2, 3],
           [4, 5, 6],
           [7, 0, 5]])

我想按第二列对行进行排序,以便返回:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])

【问题讨论】:

  • 这是一个非常糟糕的例子,因为np.sort(a, axis=0) 将是给定矩阵的令人满意的解决方案。我建议用一个更好的例子进行编辑,但被拒绝了,尽管实际上这个问题会更清楚。该示例应类似于 a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]]) 与所需的输出 array([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
  • 大卫,你没有明白问题的重点。他希望保持每行中的顺序相同。
  • @marcorossi 我确实明白了这一点,但这个例子的表述非常糟糕,因为正如我所说,有多个可能的答案(然而,这不会满足 OP 的要求)。后来根据我的评论进行的编辑确实已获得批准(但有趣的是,我的评论被拒绝了)。所以现在一切都很好。
  • 如果答案可以按兴趣降序排列...
  • 我认为使用结构化数组可以使代码更具可读性。我在这里附上了一个可能的答案:stackoverflow.com/a/67788660/13890678

标签: python arrays sorting numpy scipy


【解决方案1】:

a的第二列排序:

a[a[:, 1].argsort()]

【讨论】:

  • 这个不清楚,这里的1是什么?要排序的索引?
  • [:,1]表示a的第二列。
  • 如果要反向排序,修改为a[a[:,1].argsort()[::-1]]
  • 我觉得这更容易阅读:ind = np.argsort( a[:,1] ); a = a[ind]
  • a[a[:,k].argsort()] 与 a[a[:,k].argsort(),:] 相同。这推广到另一个维度(使用一行对列进行排序):a[:,a[j,:].argsort()](希望我输入正确。)
【解决方案2】:

@steveanswer 实际上是最优雅的方式。

对于“正确”的方式,请参见numpy.ndarray.sort 的 order 关键字参数

但是,您需要将数组视为具有字段的数组(结构化数组)。

如果你最初没有用字段定义你的数组,那么“正确”的方式是相当丑陋的......

作为一个简单的示例,对其进行排序并返回一个副本:

In [1]: import numpy as np

In [2]: a = np.array([[1,2,3],[4,5,6],[0,0,1]])

In [3]: np.sort(a.view('i8,i8,i8'), order=['f1'], axis=0).view(np.int)
Out[3]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

就地排序:

In [6]: a.view('i8,i8,i8').sort(order=['f1'], axis=0) #<-- returns None

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

据我所知,@Steve 确实是最优雅的方式......

此方法的唯一优点是“order”参数是用于对搜索进行排序的字段列表。例如,您可以通过提供 order=['f1','f2','f0'] 按第二列、第三列、第一列排序。

【讨论】:

  • 在我的 numpy 1.6.1rc1 中,它引发了ValueError: new type not compatible with array.
  • 提交功能请求以减少“正确”方式的丑陋是否有意义?
  • 如果数组中的值为float怎么办?我应该改变什么吗?
  • 与史蒂夫的方法相比,这种方法的一个主要优点是它允许对非常大的数组进行就地排序。对于足够大的数组,np.argsort 返回的索引本身可能会占用大量内存,除此之外,使用数组进行索引还会生成正在排序的数组的副本。
  • 有人能解释一下'i8,i8,i8'吗?这是针对每一列还是每一行?如果对不同的 dtype 进行排序,应该改变什么?如何找出正在使用的位数?谢谢
【解决方案3】:

您可以按照 Steve Tjoa 的方法对多个列进行排序,方法是使用像合并排序这样的稳定​​排序,并将索引从最不重要的列排序到最重要的列:

a = a[a[:,2].argsort()] # First sort doesn't need to be stable.
a = a[a[:,1].argsort(kind='mergesort')]
a = a[a[:,0].argsort(kind='mergesort')]

这按第 0 列排序,然后是 1,然后是 2。

【讨论】:

  • 为什么First Sort不需要稳定?
  • 好问题 - 稳定意味着当出现平局时,您保持原始顺序,而未排序文件的原始顺序无关紧要。
  • 这似乎是一个非常重要的观点。有一个默默不排序的列表会很糟糕。
【解决方案4】:

如果有人想在他们程序的关键部分使用排序,这里是不同提案的性能比较:

import numpy as np
table = np.random.rand(5000, 10)

%timeit table.view('f8,f8,f8,f8,f8,f8,f8,f8,f8,f8').sort(order=['f9'], axis=0)
1000 loops, best of 3: 1.88 ms per loop

%timeit table[table[:,9].argsort()]
10000 loops, best of 3: 180 µs per loop

import pandas as pd
df = pd.DataFrame(table)
%timeit df.sort_values(9, ascending=True)
1000 loops, best of 3: 400 µs per loop

所以,看起来使用argsort 进行索引是迄今为止最快的方法...

【讨论】:

    【解决方案5】:

    来自the Python documentation wiki,我认为你可以做到:

    a = ([[1, 2, 3], [4, 5, 6], [0, 0, 1]]); 
    a = sorted(a, key=lambda a_entry: a_entry[1]) 
    print a
    

    输出是:

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

    【讨论】:

    • 使用这个解决方案,人们得到一个列表而不是 NumPy 数组,所以这可能并不总是方便(占用更多内存,可能更慢等)。
    • 这个“解决方案”的速度比最受好评的答案慢了...嗯,实际上接近无穷大
    • @Jivan 实际上,这个解决方案比最受欢迎的答案快 5 倍 imgur.com/a/IbqtPBL
    【解决方案6】:

    来自the NumPy mailing list,这是另一个解决方案:

    >>> a
    array([[1, 2],
           [0, 0],
           [1, 0],
           [0, 2],
           [2, 1],
           [1, 0],
           [1, 0],
           [0, 0],
           [1, 0],
          [2, 2]])
    >>> a[np.lexsort(np.fliplr(a).T)]
    array([[0, 0],
           [0, 0],
           [0, 2],
           [1, 0],
           [1, 0],
           [1, 0],
           [1, 0],
           [1, 2],
           [2, 1],
           [2, 2]])
    

    【讨论】:

    • 正确的概括是a[np.lexsort(a.T[cols])]。其中cols=[1] 在原始问题中。
    【解决方案7】:

    我也遇到过类似的问题。

    我的问题:

    我想计算一个 SVD,需要按降序对我的eigenvalues 进行排序。但我想保留特征值和特征向量之间的映射。 我的特征值在第一行,其下方的相应特征向量在同一列中。

    所以我想按第一行的降序对二维数组按列进行排序。

    我的解决方案

    a = a[::, a[0,].argsort()[::-1]]
    

    那么这是如何工作的呢?

    a[0,] 只是我要排序的第一行。

    现在我使用 argsort 来获取索引的顺序。

    我使用[::-1],因为我需要降序。

    最后,我使用a[::, ...] 来获得按正确顺序排列的视图。

    【讨论】:

      【解决方案8】:
      import numpy as np
      a=np.array([[21,20,19,18,17],[16,15,14,13,12],[11,10,9,8,7],[6,5,4,3,2]])
      y=np.argsort(a[:,2],kind='mergesort')# a[:,2]=[19,14,9,4]
      a=a[y]
      print(a)
      

      期望的输出是[[6,5,4,3,2],[11,10,9,8,7],[16,15,14,13,12],[21,20,19,18,17]]

      请注意,argsort(numArray) 返回 numArray 的索引,因为它应该以排序方式排列。

      例子

      x=np.array([8,1,5]) 
      z=np.argsort(x) #[1,3,0] are the **indices of the predicted sorted array**
      print(x[z]) #boolean indexing which sorts the array on basis of indices saved in z
      

      答案是[1,5,8]

      【讨论】:

      • 你确定不是 [1,2,0]?
      【解决方案9】:

      稍微复杂一点的lexsort 示例 - 在第 1 列降序,在第 2 列升序。 lexsort 的技巧是它按行排序(因此是 .T),并优先考虑最后一个。

      In [120]: b=np.array([[1,2,1],[3,1,2],[1,1,3],[2,3,4],[3,2,5],[2,1,6]])
      In [121]: b
      Out[121]: 
      array([[1, 2, 1],
             [3, 1, 2],
             [1, 1, 3],
             [2, 3, 4],
             [3, 2, 5],
             [2, 1, 6]])
      In [122]: b[np.lexsort(([1,-1]*b[:,[1,0]]).T)]
      Out[122]: 
      array([[3, 1, 2],
             [3, 2, 5],
             [2, 1, 6],
             [2, 3, 4],
             [1, 1, 3],
             [1, 2, 1]])
      

      【讨论】:

        【解决方案10】:

        这是考虑 all 列的另一种解决方案(J.J 的答案更紧凑的方式);

        ar=np.array([[0, 0, 0, 1],
                     [1, 0, 1, 0],
                     [0, 1, 0, 0],
                     [1, 0, 0, 1],
                     [0, 0, 1, 0],
                     [1, 1, 0, 0]])
        

        用 lexsort 排序,

        ar[np.lexsort(([ar[:, i] for i in range(ar.shape[1]-1, -1, -1)]))]
        

        输出:

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

        【讨论】:

          【解决方案11】:

          只需使用排序,使用要排序的列号。

          a = np.array([1,1], [1,-1], [-1,1], [-1,-1]])
          print (a)
          a=a.tolist() 
          a = np.array(sorted(a, key=lambda a_entry: a_entry[0]))
          print (a)
          

          【讨论】:

            【解决方案12】:

            这是一个老问题,但如果您需要将其推广到高于 2 维的数组,这里有一个可以轻松推广的解决方案:

            np.einsum('ij->ij', a[a[:,1].argsort(),:])
            

            这对于二维来说太过分了,a[a[:,1].argsort()] 对于@steve 的回答来说就足够了,但是这个答案不能推广到更高的维度。你可以找到an example of 3D array in this question.

            输出:

            [[7 0 5]
             [9 2 3]
             [4 5 6]]
            

            【讨论】:

              【解决方案13】:

              #用于沿第 1 列排序

              indexofsort=np.argsort(dataset[:,0],axis=-1,kind='stable') 
              dataset   = dataset[indexofsort,:]
              

              【讨论】:

                【解决方案14】:
                def sort_np_array(x, column=None, flip=False):
                    x = x[np.argsort(x[:, column])]
                    if flip:
                        x = np.flip(x, axis=0)
                    return x
                

                原题中的数组:

                a = np.array([[9, 2, 3],
                              [4, 5, 6],
                              [7, 0, 5]])
                

                sort_np_array 函数的结果符合问题作者的预期:

                sort_np_array(a, column=1, flip=False)
                
                [2]: array([[7, 0, 5],
                            [9, 2, 3],
                            [4, 5, 6]])
                

                【讨论】:

                  【解决方案15】:

                  感谢这个帖子:https://stackoverflow.com/a/5204280/13890678

                  我使用structured array 找到了一个更“通用”的答案。 我认为这种方法的一个优点是代码更容易阅读。

                  import numpy as np
                  a = np.array([[9, 2, 3],
                             [4, 5, 6],
                             [7, 0, 5]])
                  
                  struct_a = np.core.records.fromarrays(
                      a.transpose(), names="col1, col2, col3", formats="i8, i8, i8"
                  )
                  struct_a.sort(order="col2")
                  
                  print(struct_a)
                  
                  [(7, 0, 5) (9, 2, 3) (4, 5, 6)]
                  

                  【讨论】:

                    猜你喜欢
                    • 2021-05-18
                    • 2021-09-21
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-03-13
                    • 2017-01-02
                    • 2017-04-12
                    相关资源
                    最近更新 更多