使用 NumPy 从矩阵中获取最小/最大 n 值和索引的有效方法

Posted

技术标签:

【中文标题】使用 NumPy 从矩阵中获取最小/最大 n 值和索引的有效方法【英文标题】:Efficient way to take the minimum/maximum n values and indices from a matrix using NumPy 【发布时间】:2011-08-14 00:05:48 【问题描述】:

在给定 NumPy 矩阵(二维数组)的情况下,返回数组中最小/最大 n 值(连同它们的索引)的有效方法是什么?

目前我有:

def n_max(arr, n):
    res = [(0,(0,0))]*n
    for y in xrange(len(arr)):
        for x in xrange(len(arr[y])):
            val = float(arr[y,x])
            el = (val,(y,x))
            i = bisect.bisect(res, el)
            if i > 0:
                res.insert(i, el)
                del res[0]
    return res

这比 pyopencv 生成我要运行它的数组所用的图像模板匹配算法要长三倍,我认为这很愚蠢。

【问题讨论】:

nlen(arr) 的典型比率是多少? @Paul: tiny.. 我正在查找模板与图像的匹配数,因此它是图像中像素数的匹配数,例如 20 到 150000 【参考方案1】:

由于 NumPy 中没有堆实现,您最好的猜测可能是对整个数组进行排序并取最后一个 n 元素:

def n_max(arr, n):
    indices = arr.ravel().argsort()[-n:]
    indices = (numpy.unravel_index(i, arr.shape) for i in indices)
    return [(arr[i], i) for i in indices]

(与您的实现相比,这可能会以相反的顺序返回列表 - 我没有检查。)

this answer 中提供了适用于 NumPy 较新版本的更高效的解决方案。

【讨论】:

如果n 很小,那么运行argmax 几次(每次删除最大值)可能会更快。 没有 NumPy 专家,但我们真的需要对在 O(n) 中简单完成的事情进行排序 (O(n log n)) 吗?我认为优点是排序是在 C 中完成的,而循环代码是由 python 解释器运行的? @Voo:OP 算法的复杂度为O(m log n),其中m 是数组中元素的数量,n 是要查找的最高元素的数量。我的答案中的算法是O(m log m)mn 的这两个复杂性之间的因子在 OP 的上述评论中是 4,​​通过摆脱 Python 循环可以弥补这一点。正如 Paul 上面提到的,如果 n 真的很小,可能会有更好的选择。 @Voo:是的,复杂性并不是一切。在这种情况下,在 C 中完成此操作比我的要快很多(快 3 倍) - 并且足够让我不再担心它,但如果我需要更快的东西,我会回来更多。但是 - 你会如何在 O(n) 上简单地做到这一点? NumPy 有 numpy.partitionnumpy.argpartition,如果你需要的话,这会让你在 O(arr.size) 或 O(arr.size+n*log(n)) n 项按顺序排列。【参考方案2】:

从另一个答案开始,NumPy 添加了numpy.partitionnumpy.argpartition 函数进行部分排序,允许您在O(arr.size) 时间执行此操作,如果您需要按排序顺序排列的元素,则可以在O(arr.size+n*log(n)) 执行此操作.

numpy.partition(arr, n) 返回一个大小为arr 的数组,其中nth 元素是对数组进行排序后的样子。所有较小的元素都在该元素之前,所有较大的元素都在之后。

numpy.argpartitionnumpy.partition 就像 numpy.argsortnumpy.sort

以下是如何使用这些函数来查找二维arr 的最小n 元素的索引:

flat_indices = numpy.argpartition(arr.ravel(), n-1)[:n]
row_indices, col_indices = numpy.unravel_index(flat_indices, arr.shape)

如果您需要按顺序排列索引,那么row_indices[0] 是最小元素的行,而不仅仅是n 最小元素之一:

min_elements = arr[row_indices, col_indices]
min_elements_order = numpy.argsort(min_elements)
row_indices, col_indices = row_indices[min_elements_order], col_indices[min_elements_order]

一维案例要简单得多:

# Unordered:
indices = numpy.argpartition(arr, n-1)[:n]

# Extra code if you need the indices in order:
min_elements = arr[indices]
min_elements_order = numpy.argsort(min_elements)
ordered_indices = indices[min_elements_order]

【讨论】:

这个确切的代码给了我 ValueError: not enough values to unpack (expected 2, got 1) @WayneFilkins:听起来你试图在一维数组而不是二维数组上使用它。 1D case 比较简单,但是不能将 2D case 代码扔到 1D 数组中。【参考方案3】:

我刚刚遇到了完全相同的问题并解决了。 这是我的解决方案,包装 np.argpartition:

应用于任意轴。 K 返回排序结果和原始矩阵中的相应索引。
def get_sorted_smallest_K(array, K, axis=-1):
    # Find the least K values of array along the given axis. 
    # Only efficient when K << array.shape[axis].
    # Return:
    #   top_sorted_scores: np.array. The least K values.
    #   top_sorted_indexs: np.array. The least K indexs of original input array.
    
    partition_index = np.take(np.argpartition(array, K, axis), range(0, K), axis)
    top_scores = np.take_along_axis(array, partition_index, axis)
    sorted_index = np.argsort(top_scores, axis=axis)
    top_sorted_scores = np.take_along_axis(top_scores, sorted_index, axis)
    top_sorted_indexs = np.take_along_axis(partition_index, sorted_index, axis)
    return top_sorted_scores, top_sorted_indexs

【讨论】:

以上是关于使用 NumPy 从矩阵中获取最小/最大 n 值和索引的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

NumPy:在矩阵中找到 N 个最大元素

从变量中获取最小值和最大值的正确方法

使用 Numpy 对矩阵进行最小二乘回归

从Awk中的多维数组中的子数组获取最小值和最大值

Pyspark - 从具有最小值和最大值范围的数组中获取值

在c ++中不同行或列旁边的矩阵中搜索最小值和最大值的最快方法是啥