【问题标题】:Find the index of the k smallest values of a numpy array查找numpy数组的k个最小值的索引
【发布时间】:2016-03-17 12:50:48
【问题描述】:

为了找到最小值的索引,我可以使用argmin

import numpy as np
A = np.array([1, 7, 9, 2, 0.1, 17, 17, 1.5])
print A.argmin()     # 4 because A[4] = 0.1

但是我怎样才能找到 k 最小值 的索引?

我正在寻找类似的东西:

print A.argmin(numberofvalues=3)   
# [4, 0, 7]  because A[4] <= A[0] <= A[7] <= all other A[i]

注意:在我的用例中,A 的值介于 ~ 10 000 和 100 000 之间,我只对 k=10 个最小值的索引感兴趣。 k 永远不会 > 10。

【问题讨论】:

标签: python numpy


【解决方案1】:

使用np.argpartition。它不对整个数组进行排序。它只保证kth 元素处于排序位置,并且所有较小的元素都将移到它之前。因此,第一个 k 元素将是 k 最小的元素。

import numpy as np

A = np.array([1, 7, 9, 2, 0.1, 17, 17, 1.5])
k = 3

idx = np.argpartition(A, k)
print(idx)
# [4 0 7 3 1 2 6 5]

这将返回 k 最小值。请注意,这些可能不是按排序顺序排列的。

print(A[idx[:k]])
# [ 0.1  1.   1.5]

要获得 k 最大值,请使用

idx = np.argpartition(A, -k)
# [4 0 7 3 1 2 6 5]

A[idx[-k:]]
# [  9.  17.  17.]

警告:不要(重新)使用idx = np.argpartition(A, k); A[idx[-k:]] 来获得k-largest。 这并不总是有效的。例如,这些不是x 中的 3 个最大值:

x = np.array([100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0])
idx = np.argpartition(x, 3)
x[idx[-3:]]
array([ 70,  80, 100])

这是与np.argsort 的比较,它也可以,但只是对整个数组进行排序以获得结果。

In [2]: x = np.random.randn(100000)

In [3]: %timeit idx0 = np.argsort(x)[:100]
100 loops, best of 3: 8.26 ms per loop

In [4]: %timeit idx1 = np.argpartition(x, 100)[:100]
1000 loops, best of 3: 721 µs per loop

In [5]: np.alltrue(np.sort(np.argsort(x)[:100]) == np.sort(np.argpartition(x, 100)[:100]))
Out[5]: True

【讨论】:

  • 知道这是如何处理关系的吗?看来,如果您想要随机平局,唯一可能的方法是使用 lexsort 对整个数组进行排序。 stackoverflow.com/a/20199459/1993389 分区文档说 introselect 不稳定,但我不确定这是否意味着关系被随机破坏。
  • @user27182:根据the docs,如果a 是一个带有字段的数组(即structured array),那么您可以指定order 或让未指定的字段用于断绝关系。因此,如果您将A 倒入结构化数组的第一个字段,然后将随机(打破平局)数字倒入第二个字段,则np.argpartition 可用于选择具有随机平局的k 最小(或最大)休息。
  • 请记住,第一个k-1 元素不能保证按从小到大的顺序排列。如果这是您需要的,您可以使用np.argpartition,使用第一个k 索引对数组进行切片,然后在结果数组上使用np.argsort
【解决方案2】:

您可以使用numpy.argsort 进行切片

>>> import numpy as np
>>> A = np.array([1, 7, 9, 2, 0.1, 17, 17, 1.5])
>>> np.argsort(A)[:3]
array([4, 0, 7], dtype=int32)

【讨论】:

  • 谢谢!但是,必须计算 所有 argsort 然后只保留 k (
  • 不知道argsort的实现很难说。具体来说,如果它被实现为生成器,并且根据实际的排序算法,它可能是惰性的,或者它可能首先对整个集合进行排序,我不确定。
  • 从其他 cmets 看来,argsort 似乎对整个集合进行了排序,所以我更喜欢使用 argpartition 的其他建议解决方案之一
  • 这个解决方案的好处(与argpartition相比)是我们可以保证我们正在寻找的k个索引是按升序排列的。
【解决方案3】:

对于 n 维数组,此函数运行良好。这些缺陷以可调用的形式返回。如果要返回索引列表,则需要在创建列表之前转置数组。

要检索最大的k,只需传入-k

def get_indices_of_k_smallest(arr, k):
    idx = np.argpartition(arr.ravel(), k)
    return tuple(np.array(np.unravel_index(idx, arr.shape))[:, range(min(k, 0), max(k, 0))])
    # if you want it in a list of indices . . . 
    # return np.array(np.unravel_index(idx, arr.shape))[:, range(k)].transpose().tolist()

例子:

r = np.random.RandomState(1234)
arr = r.randint(1, 1000, 2 * 4 * 6).reshape(2, 4, 6)

indices = get_indices_of_k_smallest(arr, 4)
indices
# (array([1, 0, 0, 1], dtype=int64),
#  array([3, 2, 0, 1], dtype=int64),
#  array([3, 0, 3, 3], dtype=int64))

arr[indices]
# array([ 4, 31, 54, 77])

%%timeit
get_indices_of_k_smallest(arr, 4)
# 17.1 µs ± 651 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

【讨论】:

  • 我正在寻找 n 维案例的答案,您的工作按预期工作!此外,这是一个快速的解决方案。
【解决方案4】:

numpy.partition(your_array, k) 是另一种选择。不需要切片,因为它给出了排序到 kth 元素之前的值。

【讨论】:

  • 这会将索引k(可能未排序)处的元素置于排序位置。由于排序后的位置不需要索引kk-1,我们不能保证your_array[:k] 包含knumpy.partition 之后的最小元素。
  • 这是 2019 年 10 月数组(不是索引)值的最佳答案。@protagonist 我不明白你的评论。如果有错误,请纠正我,但证明此分区函数正常工作的证据是在循环中运行以下命令: y = np.arange(10) ; np.random.shuffle(y) ; y.partition(3) ; assert y[:3+1].max() k 是零索引的。
猜你喜欢
  • 2012-01-29
  • 1970-01-01
  • 2018-04-22
  • 2018-07-27
  • 2017-11-05
  • 1970-01-01
  • 2016-12-09
  • 2018-07-31
  • 2017-08-13
相关资源
最近更新 更多