【问题标题】:Efficiently sorting a numpy array in descending order?有效地按降序对numpy数组进行排序?
【发布时间】:2015-01-15 01:21:59
【问题描述】:

我很惊讶这个特定的问题以前没有被问过,但我真的没有在 SO 和 np.sort 的文档中找到它。

假设我有一个包含整数的随机 numpy 数组,例如:

> temp = np.random.randint(1,10, 10)    
> temp
array([2, 4, 7, 4, 2, 2, 7, 6, 4, 4])

如果我对它进行排序,默认情况下我会得到升序:

> np.sort(temp)
array([2, 2, 2, 4, 4, 4, 4, 6, 7, 7])

但我希望解决方案按降序顺序排序。

现在,我知道我总能做到:

reverse_order = np.sort(temp)[::-1]

但是这最后的语句有效吗?它不是按升序创建一个副本,然后反转这个副本以获得相反顺序的结果吗?如果确实如此,是否有有效的替代方案?看起来np.sort 不接受参数来更改排序操作中比较的符号,从而以相反的顺序获取内容。

【问题讨论】:

    标签: python arrays sorting numpy


    【解决方案1】:

    temp[::-1].sort() 对数组进行就地排序,而np.sort(temp)[::-1] 创建一个新数组。

    In [25]: temp = np.random.randint(1,10, 10)
    
    In [26]: temp
    Out[26]: array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])
    
    In [27]: id(temp)
    Out[27]: 139962713524944
    
    In [28]: temp[::-1].sort()
    
    In [29]: temp
    Out[29]: array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])
    
    In [30]: id(temp)
    Out[30]: 139962713524944
    

    【讨论】:

    • 谢谢,但是temp[::-1].sort() 怎么知道它必须以相反的顺序排序?我读它的方式是:反转原始数组,然后对其进行排序(按升序排列)。为什么要反转原始数组(以随机顺序出现)然后按升序排序返回数组以反转顺序?
    • 这种行为是否记录在案,因为它非常不直观。
    • 这看起来很有效,因为 [::-1] 只是告诉 numpy 向后迭代数组,而不是实际重新排序数组。因此,当发生就地排序时,它实际上是按升序排序并移动位,但保持向后迭代部分不变。
    • 对于a=np.array((...)),成语a[::-1] 不会反转任何东西,它只是对相同数据的新视图,更具体地说是镜像视图。 a[::-1].sort() 方法对镜像进行操作,这意味着当sort 在其镜像中向 移动一个较小的项目时,实际上是将其移动到right 在a 数组的实际内存块中。镜像视图按升序排序,真实数据按降序排序。自己在家里尝试一下,用一些不同的硬币和一面镜子!
    • 这真的应该作为一个可读参数添加,比如np.sort(temp,order='descending'),而不是需要这些类型的hacks
    【解决方案2】:

    对于短数组,我建议使用np.argsort(),通过查找已排序的负数数组的索引,这比反转已排序的数组要快一些:

    In [37]: temp = np.random.randint(1,10, 10)
    
    In [38]: %timeit np.sort(temp)[::-1]
    100000 loops, best of 3: 4.65 µs per loop
    
    In [39]: %timeit temp[np.argsort(-temp)]
    100000 loops, best of 3: 3.91 µs per loop
    

    【讨论】:

    • a[np.argsort(-a)] 可能是此页面上任何其他人的最佳方法。无需考虑 -1 步反转和少一个负号。
    【解决方案3】:

    不幸的是,当您有一个复杂的数组时,只有 np.sort(temp)[::-1] 可以正常工作。这里提到的另外两种方法都无效。

    【讨论】:

    • @anishtain4:“复数数组”是指复数数组吗?还是您的意思是具有其他某种复杂性的数组(如果是,请指定哪种复杂性)。无论哪种情况,我觉得您可以通过了解其他方法可能会如何失败来详细说明您的答案。谢谢。
    • @fountainhead 我的意思是复数数组。由于这是一个老问题,我不记得我的测试用例从那时起详细说明。
    • @anishtain4 你如何对复数进行排序?您无法定义复数之间的排序关系......您只能分别比较它们的实部和虚部,或它们的大小。
    【解决方案4】:
    >>> a=np.array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])
    
    >>> np.sort(a)
    array([2, 2, 4, 4, 4, 4, 5, 6, 7, 8])
    
    >>> -np.sort(-a)
    array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])
    

    【讨论】:

    • 最佳答案 - 简短而甜蜜,无需了解应用了np.sortaxis
    • 这与np.sort(temp)[::-1] 的不同之处在于它将nans 放在数组的后面而不是前面。这是好是坏还有待商榷。
    • 我想nans 的哪个位置最好取决于具体的应用程序。所以最好有两种方法,一个人可以选择最适合他们特定应用的方法。
    • 此方法不适用于结构化数组,出现错误numpy.core._exceptions.UFuncTypeError: ufunc 'negative' did not contain a loop with signature matching types dtype([dtype list])
    • 简短而甜蜜的回答!应该是这个问题的默认答案!
    【解决方案5】:

    您好,我正在寻找一种对二维 numpy 数组进行反向排序的解决方案,但我找不到任何可行的方法,但我想我偶然发现了一个解决方案,我正在上传以防万一有人遇到相同的情况船。

    x=np.sort(array)
    y=np.fliplr(x)
    

    np.sort 按升序排序,这不是您想要的,但命令 fliplr 会从左到右翻转行!似乎有效!

    希望对你有所帮助!

    我想这类似于上面关于-np.sort(-a) 的建议,但我被评论推迟了它并不总是有效。也许我的解决方案也不总是有效,但是我已经用几个数组对其进行了测试,似乎没问题。

    【讨论】:

      【解决方案6】:

      我建议使用这个...

      np.arange(start_index, end_index, intervals)[::-1]
      

      例如:

      np.arange(10, 20, 0.5)
      np.arange(10, 20, 0.5)[::-1]
      

      那么你的结果:

      [ 19.5,  19. ,  18.5,  18. ,  17.5,  17. ,  16.5,  16. ,  15.5,
          15. ,  14.5,  14. ,  13.5,  13. ,  12.5,  12. ,  11.5,  11. ,
          10.5,  10. ]
      

      【讨论】:

      • 这如何解决问题?您只是在创建一个完全不相关的新(降序)数组 - 顺便说一下 - 可以以更有效的方式完成:np.arange(20-0.5, 10-0.5, -0.5)。但这是一个不同的故事,并且由于可读性较差,可能值得商榷。输入数组根本没有排序
      【解决方案7】:

      注意尺寸。

      x  # initial numpy array
      I = np.argsort(x) or I = x.argsort() 
      y = np.sort(x)    or y = x.sort()
      z  # reverse sorted array
      

      全反

      z = x[I[::-1]]
      z = -np.sort(-x)
      z = np.flip(y)
      
      • flip 更改为 1.15,以前的版本 1.14 必需 axis。解决方案:pip install --upgrade numpy

      第一维反转

      z = y[::-1]
      z = np.flipud(y)
      z = np.flip(y, axis=0)
      

      第二维度反转

      z = y[::-1, :]
      z = np.fliplr(y)
      z = np.flip(y, axis=1)
      

      测试

      在 100×10×10 阵列上测试 1000 次。

      Method       | Time (ms)
      -------------+----------
      y[::-1]      | 0.126659  # only in first dimension
      -np.sort(-x) | 0.133152
      np.flip(y)   | 0.121711
      x[I[::-1]]   | 4.611778
      
      x.sort()     | 0.024961
      x.argsort()  | 0.041830
      np.flip(x)   | 0.002026
      

      这主要是由于重新索引而不是argsort

      # Timing code
      import time
      import numpy as np
      
      
      def timeit(fun, xs):
          t = time.time()
          for i in range(len(xs)):  # inline and map gave much worse results for x[-I], 5*t
              fun(xs[i])
          t = time.time() - t
          print(np.round(t,6))
      
      I, N = 1000, (100, 10, 10)
      xs = np.random.rand(I,*N)
      timeit(lambda x: np.sort(x)[::-1], xs)
      timeit(lambda x: -np.sort(-x), xs)
      timeit(lambda x: np.flip(x.sort()), xs)
      timeit(lambda x: x[x.argsort()[::-1]], xs)
      timeit(lambda x: x.sort(), xs)
      timeit(lambda x: x.argsort(), xs)
      timeit(lambda x: np.flip(x), xs)
      

      【讨论】:

      • np.flip() - 超级
      • I = np.argsort(x) 然后 z = x[-I] 在 x 中有零时不起作用。通过自然排序,它应该排在最后,但它会在 z 中排在第一位,因为 -0 == 0
      【解决方案8】:

      您可以先对数组进行排序(默认为升序)然后应用 np.flip() (https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html)

      仅供参考,它也适用于日期时间对象。

      例子:

          x = np.array([2,3,1,0]) 
          x_sort_asc=np.sort(x) 
          print(x_sort_asc)
      
          >>> array([0, 1, 2, 3])
      
          x_sort_desc=np.flip(x_sort_asc) 
          print(x_sort_desc)
      
          >>> array([3,2,1,0])
      

      【讨论】:

      • 对于那些在他们的数组中有 NaN 的人要小心,各种建议的方法会产生不同的结果。例如,如果 x = np.array([2,3,np.nan,1,0]) np.flip(np.sort(x)) 方法产生 [nan 3. 2. 1. 0.],而 -np.sort(-x) 方法产生 [ 3. 2. 1. 0. nan]。
      【解决方案9】:

      这里有一个快速技巧

      In[3]: import numpy as np
      In[4]: temp = np.random.randint(1,10, 10)
      In[5]: temp
      Out[5]: array([5, 4, 2, 9, 2, 3, 4, 7, 5, 8])
      
      In[6]: sorted = np.sort(temp)
      In[7]: rsorted = list(reversed(sorted))
      In[8]: sorted
      Out[8]: array([2, 2, 3, 4, 4, 5, 5, 7, 8, 9])
      
      In[9]: rsorted
      Out[9]: [9, 8, 7, 5, 5, 4, 4, 3, 2, 2]
      

      【讨论】:

        【解决方案10】:

        np.flip() 和反向索引基本相同。以下是使用三种不同方法的基准测试。似乎np.flip() 稍微快一些。使用否定比较慢,因为它被使用了两次,所以反转数组比那要快。

        ** 请注意,根据我的测试,np.flip()np.fliplr() 快​​。

        def sort_reverse(x):
            return np.sort(x)[::-1]
        
        def sort_negative(x):
            return -np.sort(-x)
        
        def sort_flip(x):
            return np.flip(np.sort(x)) 
        
        arr=np.random.randint(1,10000,size=(1,100000))
        
        %timeit sort_reverse(arr)
        %timeit sort_negative(arr)
        %timeit sort_flip(arr)
        

        结果是:

        6.61 ms ± 67.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        6.69 ms ± 64.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        6.57 ms ± 58.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-16
          • 2013-09-20
          • 1970-01-01
          • 1970-01-01
          • 2016-12-05
          • 2011-02-09
          • 2021-12-28
          • 1970-01-01
          相关资源
          最近更新 更多