【问题标题】:Quickly Find the Index in an Array Closest to Some Value快速找到最接近某个值的数组中的索引
【发布时间】:2011-08-29 06:31:03
【问题描述】:

我有一个值数组 t,它总是按递增顺序排列(但并不总是均匀间隔)。我有另一个值,x。我需要找到 t 中的索引,使得 t[index] 最接近 x。对于 x t.max(),函数必须返回最大索引(或 -1)。

我已经编写了两个函数来做到这一点。第一个,f1,在这个简单的计时测试中要快得多。但我喜欢第二个只是一条线。此计算将在一个大型数组上完成,可能每秒多次。

谁能想出一些其他功能与第一个功能相当的时间但看起来更干净的代码?比第一个更快的东西怎么样(速度是最重要的)?

谢谢!

代码:

import numpy as np
import timeit

t = np.arange(10,100000)         # Not always uniform, but in increasing order
x = np.random.uniform(10,100000) # Some value to find within t

def f1(t, x):
   ind = np.searchsorted(t, x)   # Get index to preserve order
   ind = min(len(t)-1, ind)      # In case x > max(t)
   ind = max(1, ind)             # In case x < min(t)
   if x < (t[ind-1] + t[ind]) / 2.0:   # Closer to the smaller number
      ind = ind-1
   return ind

def f2(t, x):
   return np.abs(t-x).argmin()

print t,           '\n', x,           '\n'
print f1(t, x),    '\n', f2(t, x),    '\n'
print t[f1(t, x)], '\n', t[f2(t, x)], '\n'

runs = 1000
time = timeit.Timer('f1(t, x)', 'from __main__ import f1, t, x')
print round(time.timeit(runs), 6)

time = timeit.Timer('f2(t, x)', 'from __main__ import f2, t, x')
print round(time.timeit(runs), 6)

【问题讨论】:

  • 由于您的数组已排序,请尝试二进制搜索。查看这个问题的答案:stackoverflow.com/questions/212358/binary-search-in-python
  • 我刚下班,但想稍后再看。我认为一旦你测试了 x max(t),你可以通过短路来改进你的第一个函数,但我还没有机会测试它。跨度>

标签: python performance indexing numpy


【解决方案1】:

使用搜索排序:

t = np.arange(10,100000)         # Not always uniform, but in increasing order
x = np.random.uniform(10,100000)

print t.searchsorted(x)

编辑:

啊,是的,我知道这就是你在 f1 中所做的。也许下面的 f3 比 f1 更容易阅读。

def f3(t, x):
    ind = t.searchsorted(x)
    if ind == len(t):
        return ind - 1 # x > max(t)
    elif ind == 0:
        return 0
    before = ind-1
    if x-t[before] < t[ind]-x:
        ind -= 1
    return ind

【讨论】:

  • 这只是给出了数字插入位置的索引,不一定是最接近的索引。这就是为什么他的 f1 函数需要额外的步骤。
  • 啊,是的,我没有看到 f1 中的搜索排序。对不起。
【解决方案2】:

这似乎要快得多(对我来说,Python 3.2-win32,numpy 1.6.0):

from bisect import bisect_left
def f3(t, x):
    i = bisect_left(t, x)
    if t[i] - x > 0.5:
        i-=1
    return i

输出:

[   10    11    12 ..., 99997 99998 99999]
37854.22200356027
37844
37844
37844
37854
37854
37854
f1 0.332725
f2 1.387974
f3 0.085864

【讨论】:

  • 是的,快得多。但是,它确实需要稍微调整以考虑 x t.max 的特殊情况
  • 如果间隙较大,它也将无法正常工作:f3([100, 200], 199) 表示0 尽管200 更接近199 而不是100。见my answer here
【解决方案3】:

np.searchsorted 是二分查找(每次将数组分成两半)。因此,您必须以返回小于 x 的最后一个值而不是返回零的方式来实现它。

看看这个算法(来自here):

def binary_search(a, x):
    lo=0
    hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        midval = a[mid]
        if midval < x:
            lo = mid+1
        elif midval > x: 
            hi = mid
        else:
            return mid
    return lo-1 if lo > 0 else 0

刚刚替换了最后一行(原为return -1)。也改变了论点。

由于循环是用 Python 编写的,它可能比第一个慢...(未进行基准测试)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-31
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2017-03-15
    • 2016-07-19
    • 2013-03-01
    相关资源
    最近更新 更多