【问题标题】:Get second minimum values per column in 2D array获取二维数组中每列的第二个最小值
【发布时间】:2020-06-23 10:21:11
【问题描述】:

如何从每列中获取第二个最小值?我有这个数组:

A = [[72 76 44 62 81 31]
     [54 36 82 71 40 45]
     [63 59 84 36 34 51]
     [58 53 59 22 77 64]
     [35 77 60 76 57 44]]

我希望有这样的输出:

A = [54 53 59 36 40 44]

【问题讨论】:

  • 第二个最小值每列

标签: python arrays python-3.x list numpy


【解决方案1】:

试试这个,只需一行:

[sorted(i)[1] for i in zip(*A)]

在行动:

In [12]: A = [[72, 76, 44, 62, 81, 31], 
    ...:      [54 ,36 ,82 ,71 ,40, 45], 
    ...:      [63 ,59, 84, 36, 34 ,51], 
    ...:      [58, 53, 59, 22, 77 ,64], 
    ...:      [35 ,77, 60, 76, 57, 44]] 

In [18]: [sorted(i)[1] for i in zip(*A)]                                                                                                                                                                           
Out[18]: [54, 53, 59, 36, 40, 44]

zip(*A) 将转置您的列表列表,使列变为行。

如果你有重复的值,例如:

In [19]: A = [[72, 76, 44, 62, 81, 31], 
    ...:  [54 ,36 ,82 ,71 ,40, 45], 
    ...:  [63 ,59, 84, 36, 34 ,51], 
    ...:  [35, 53, 59, 22, 77 ,64],   # 35
    ...:  [35 ,77, 50, 76, 57, 44],]  # 35

如果需要同时跳过35s,可以使用set()

In [29]: [sorted(list(set(i)))[1] for i in zip(*A)]                                                                                                                                                                
Out[29]: [54, 53, 50, 36, 40, 44]

【讨论】:

    【解决方案2】:

    numpy 数组的操作应该用numpy 函数完成,所以看看这个:

    np.sort(A, axis=0)[1, :]
    
    Out[61]: array([54, 53, 59, 36, 40, 44])
    

    【讨论】:

    • 据我所知,这必须是最好的解决方案,它将所有内容保存在numpy 中,我认为lambda 必须放慢heapq.nsmallest 解决方案。似乎最好在 numpy 中保持一切快速
    【解决方案3】:

    你可以使用heapq.nsmallest

    from heapq import nsmallest
    
    [nsmallest(2, e)[-1] for e in zip(*A)]
    

    输出:

    [54, 53, 50, 36, 40, 44]
    

    我添加了一个简单的基准来比较已发布的不同解决方案的性能:

    from simple_benchmark import BenchmarkBuilder
    from heapq import nsmallest
    
    
    b = BenchmarkBuilder()
    
    @b.add_function()
    def MehrdadPedramfar(A):
        return [sorted(i)[1] for i in zip(*A)]
    
    @b.add_function()
    def NicolasGervais(A):
        return np.sort(A, axis=0)[1, :]
    
    @b.add_function()
    def imcrazeegamerr(A):
        rotated = zip(*A[::-1])
    
        result = []
        for arr in rotated:
            # sort each 1d array from min to max
            arr = sorted(list(arr))
            # add the second minimum value to result array
            result.append(arr[1])
    
        return result
    
    @b.add_function()
    def Daweo(A):
        return np.apply_along_axis(lambda x:heapq.nsmallest(2,x)[-1], 0, A)
    
    @b.add_function()       
    def kederrac(A):
        return [nsmallest(2, e)[-1] for e in zip(*A)]
    
    
    @b.add_arguments('Number of row/cols (A is  square matrix)')
    def argument_provider():
        for exp in range(2, 18):
            size = 2**exp
            yield size, [[randint(0, 1000) for _ in range(size)] for _ in range(size)]
    
    r = b.run()
    r.plot()
    

    zipsorted 函数结合使用是小型二维列表的最快解决方案,而将zipheapq.nsmallest 结合使用则在大型二维列表中表现最佳

    【讨论】:

    • 只是一个疯狂的想法:这些结果会受到您生成的数字不是 numpy dtypes 的影响吗?另外,内置的 randint 不会返回列表而不是数组吗?
    • 这是迭代 np.matrix 上的行的唯一方法吗?有更快的替代方案吗?
    【解决方案4】:

    我希望我正确理解了您的问题,但无论哪种方式,这都是我的解决方案,我确信有一种更优雅的方式可以做到这一点,但它确实有效

    A = [[72,76,44,62,81,31]
     ,[54,36,82,71,40,45]
     ,[63,59,84,36,34,51]
     ,[58,53,59,22,77,64]
     ,[35,77,50,76,57,44]]
    
    #rotate the array 90deg
    rotated = zip(*A[::-1])
    
    result = []
    for arr in rotated:
        # sort each 1d array from min to max
        arr = sorted(list(arr))
        # add the second minimum value to result array
        result.append(arr[1])
    print(result)
    

    【讨论】:

      【解决方案5】:

      假设Anumpy.array(如果这成立,请考虑在您的问题中添加numpy 标签)然后您可以使用apply_along_axis 进行以下方式:

      import heap
      import numpy as np
      A = np.array([[72, 76, 44, 62, 81, 31],
                    [54, 36, 82, 71, 40, 45],
                    [63, 59, 84, 36, 34, 51],
                    [58, 53, 59, 22, 77, 64],
                    [35, 77, 60, 76, 57, 44]])
      second_mins = np.apply_along_axis(lambda x:heapq.nsmallest(2,x)[-1], 0, A)
      print(second_mins)  # [54 53 59 36 40 44]
      

      请注意,我使用了 heapq.nsmallest,因为它会根据需要进行尽可能多的排序以获得 2 个最小元素,而 sorted 会完成排序。

      【讨论】:

        【解决方案6】:
        >>> A = np.arange(30).reshape(5,6).tolist()
        >>> A
        [[0, 1, 2, 3, 4, 5], 
         [6, 7, 8, 9, 10, 11], 
         [12, 13, 14, 15, 16, 17], 
         [18, 19, 20, 21, 22, 23],
         [24, 25, 26, 27, 28, 29]]
        

        更新: 使用set 防止使用zip(*A) 重复和转置列表

        >>> [sorted(set(items))[1] for items in zip(*A)]
        [6, 7, 8, 9, 10, 11]
        

        old:每行中的第二个最小项目

        >>> [sorted(set(items))[1] for items in A]
        [1, 7, 13, 19, 25]
        

        【讨论】:

        • 这不是在每一行而不是列中获取第二项吗?
        猜你喜欢
        • 1970-01-01
        • 2022-01-22
        • 2012-10-29
        • 1970-01-01
        • 2022-11-25
        • 1970-01-01
        • 1970-01-01
        • 2020-06-20
        • 2015-07-22
        相关资源
        最近更新 更多