查找最接近列表中未完全排序的值的项目的索引

时间:2012-03-14 16:33:21

标签: python list search

作为一个例子,我的清单是:

[25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]

我正在寻找最接近11.5的值的索引。我尝试过其他方法,例如二进制搜索和bisect_left,但它们不起作用。

我无法对此数组进行排序,因为该值的索引将用于类似的数组以获取该索引处的值。

7 个答案:

答案 0 :(得分:118)

尝试以下方法:

min(range(len(a)), key=lambda i: abs(a[i]-11.5))

例如:

>>> a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
>>> min(range(len(a)), key=lambda i: abs(a[i]-11.5))
16

或者获取索引和值:

>>> min(enumerate(a), key=lambda x: abs(x[1]-11.5))
(16, 11.33447)

答案 1 :(得分:2)

怎么样:你压缩两个列表,然后对结果进行排序?

答案 2 :(得分:2)

如果你无法对数组进行排序,那么就没有快速找到最近项的方法 - 你必须遍历所有条目。

有一个解决方法,但它有相当多的工作:编写一个排序算法,对数组进行排序,并(同时)更新第二个数组,告诉你此条目之前的位置数组已经排序。

这样,您可以使用二进制搜索查找最近条目的索引,然后使用此索引使用“索引数组”查找原始索引。

[编辑] 使用zip(),这很容易实现:

 array_to_sort = zip( original_array, range(len(original_array)) )
 array_to_sort.sort( key=i:i[0] )

现在您可以二进制搜索该值(使用item[0])。 item[1]将为您提供原始索引。

答案 3 :(得分:2)

通过所有项目只是线性的。如果你要对阵列进行排序会更糟。

我没有看到有关保持额外deltax(目前为止的最小差异)和idx(该元素的索引)的问题,只是通过列表循环一次。

答案 4 :(得分:2)

import numpy as np

a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]

index = np.argmin(np.abs(np.array(a)-11.5))
a[index] # here is your result

如果a已经是数组,则可以省略相应的转换。

答案 5 :(得分:1)

请记住,如果空间不重要,您可以通过创建排序索引的辅助列表来排序任何列表而不移动内容。

另外请记住,如果你这样做只查一次,那么你只需要遍历列表O(n)中的每个元素。 (如果多次,那么您可能希望在以后进行排序以提高效率)

答案 6 :(得分:0)

如果你经常搜索一个很长的列表,那么如果你将一些搜索结果添加到搜索列表中,那么min会非常糟糕(O(n)甚至是O(n ^ 2) )。 Bisect是你的朋友。这是我的解决方案。它会缩放O(log(n))最坏的情况:

class Closest:
    """Assumes *no* redundant entries - all inputs must be unique"""
    def __init__(self, numlist=[], firstdistance=0):
        self.numindexes = dict((val, n) for n, val in enumerate(numlist))
        self.nums = sorted(self.numindexes)
        self.firstdistance = firstdistance

    def append(self, num):
        if num in self.numindexes:
            raise ValueError("Cannot append '%i' it is already used" % num)
        self.numindexes[num] = len(self.nums)
        bisect.insort(self.nums, num)

    def rank(self, target):
        rank = bisect.bisect(self.nums, target)
        if rank == 0:
            pass
        elif len(self.nums) == rank:
            rank -= 1
        else:
            dist1 = target - self.nums[rank - 1]
            dist2 = self.nums[rank] - target
            if dist1 < dist2:
                rank -= 1
        return rank

    def closest(self, target):
        try:
            return self.numindexes[self.nums[self.rank(target)]]
        except IndexError:
            return 0

    def distance(self, target):
        rank = self.rank(target)
        try:
            dist = abs(self.nums[rank] - target)
        except IndexError:
            dist = self.firstdistance
        return dist

像这样使用:

a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
cl = Closest(a)
for x in targets:
    rank = cl.rank(x)
    print("Closest number:", cl.nums[rank])
    print("Closest index:",  cl.numindexes[cl.nums[rank]])