在二维数组中获取每列的第二个最小值

17

如何获取每列的第二小值?我有以下数组:

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]

每列的第二小值是什么? - Nicolas Gervais
6个回答

12

试试这个,在一行中完成:

[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

如果您需要跳过两个 35,可以使用 set()

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

8

numpy数组的操作应该使用numpy函数完成,因此请查看下面这个函数:

np.sort(A, axis=0)[1, :]

Out[61]: array([54, 53, 59, 36, 40, 44])

据我所知,这应该是最佳解决方案,它将所有内容都保留在 numpy 中,我认为 lambda 会减慢 heapq.nsmallest 的解决方案。似乎最好将所有东西都保持快速的 numpy - jamylak
除非您有重复的最小值... - matanster

5
你可以使用 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()

zip函数与sorted函数结合在小型二维列表中是最快的解决方案,而使用heapq.nsmallestzip在大型二维列表中表现最佳。


1
只是一个疯狂的想法:这些结果是否会受到您生成的不是numpy数据类型的数字影响?此外,内置的randint不会返回一个数组而不是一个列表吗? - Nicolas Gervais
在 np.matrix 上迭代行的方式只有这一种吗?是否有更快的替代方法? - Pablo

1

我希望我理解了你的问题,但无论如何这是我的解决方案,我相信有更优雅的方法来完成这个任务,但它能够工作。

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)

enter image description here


0
假设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 那样进行完整排序。

0
>>> 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]

原文:每行第二小的项目

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

这不是获取每行的第二个项目而不是列吗? - paxdiablo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接