找到最接近给定数字的k个数字

时间:2014-06-09 00:29:14

标签: python closest

说我有一个列表[1,2,3,4,5,6,7]。我想找到3个最接近的数字,比方说6.5。然后返回的值为[5,6,7]

在python中找到一个最接近的数字并不是那么棘手,可以使用

来完成
min(myList, key=lambda x:abs(x-myNumber))

但我试图不围绕这个找到k个最接近的数字。是否有一种pythonic方式来实现上述任务?

4 个答案:

答案 0 :(得分:45)

简短回答

heapq.nsmallest()功能可以整齐有效地完成这项工作:

>>> from heapq import nsmallest
>>> s = [1,2,3,4,5,6,7]
>>> nsmallest(3, s, key=lambda x: abs(x-6.5))
[6, 7, 5]

基本上这就是说,“给我三个输入值,其绝对差值与 6.5 ”的绝对差值最小。

算法及其运行时间

nsmallest 的算法对数据进行单次传递,在任何时候都保留在内存中的 n 最佳值(这意味着它适用于任何输入迭代器,缓存效率高,节省空间。

当找到新的“最佳”值时,算法仅向堆中添加新值。因此,它最小化了比较的次数。例如,如果您要查找1,000,000个随机输入中的100个最佳值,则通常会进行少于1,008,000次比较(比使用min()找到单个最佳值的情况多出约0.8%。)

min() nsmallest() sorted()key functions都保证关键功能是在输入可迭代中每个值只调用一次。这意味着这种技术对于n个最接近的值问题的更复杂和有趣的例子(即sound the most alike,最接近colorssmallest diffs,最少的基因突变,欧几里得的单词将是有效的。距离等)。

nsmallest() sorted()都将返回按近似顺序排列的列表排名(关系是通过首先看到的值来确定的)。

对于那些感兴趣的人,对预期的比较次数herehere进行了一些分析。快速摘要:

  • 随机输入的平均情况:n + k * (log(k, 2) * log(n/k) + log(k, 2) + log(n/k))
  • 提升输入的最佳案例:n + k * log(k, 2)
  • 输入下降的最坏情况:n * log(k, 2)

优化重复查找

在评论中,@ Phylliida询问如何针对具有不同起点的重复查找进行优化。关键是对数据进行预排序,然后使用bisect找到小型搜索网段的中心:

from bisect import bisect

def k_nearest(k, center, sorted_data):
    'Return *k* members of *sorted_data* nearest to *center*'
    i = bisect(sorted_data, center)
    segment = sorted_data[max(i-k, 0) : i+k]
    return nsmallest(k, segment, key=lambda x: abs(x - center))

例如:

>>> s.sort()
>>> k_nearest(3, 6.5, s)
[6, 7, 5]
>>> k_nearest(3, 0.5, s)
[1, 2, 3]
>>> k_nearest(3, 4.5, s)    
[4, 5, 3]
>>> k_nearest(3, 5.0, s)
[5, 4, 6]

bisect() nsmallest()都利用了排序数据。前者运行 O(log2 k)时间,后者运行 O(n)时间。

答案 1 :(得分:3)

你可以计算距离,然后排序:

[n for d, n in sorted((abs(x-myNumber), x) for x in myList)[:k]]

执行以下操作:

  1. 创建一系列元组(d, x),其中d是指向目标的距离
  2. 选择该列表的第一个k元素
  3. 从结果中提取数字值,丢弃距离

答案 2 :(得分:1)

两个答案都很好,Greg是对的,Raymond的答案更高级,更容易实现,但我建立在Greg的答案上,因为它更容易操作以满足我的需要。

如果有人正在搜索从词典列表中找到n个最接近的值的方法。

我的dict看起来像这样,其中npi只是我需要的标识符以及值:

mydict = {u'fnpi': u'1982650024',
 u'snpi': {u'npi': u'1932190360', u'value': 2672},
 u'snpis': [{u'npi': u'1831289255', u'value': 20},
  {u'npi': u'1831139799', u'value': 20},
  {u'npi': u'1386686137', u'value': 37},
  {u'npi': u'1457355257', u'value': 45},
  {u'npi': u'1427043645', u'value': 53},
  {u'npi': u'1477548675', u'value': 53},
  {u'npi': u'1851351514', u'value': 57},
  {u'npi': u'1366446171', u'value': 60},
  {u'npi': u'1568460640', u'value': 75},
  {u'npi': u'1326046673', u'value': 109},
  {u'npi': u'1548281124', u'value': 196},
  {u'npi': u'1912989989', u'value': 232},
  {u'npi': u'1336147685', u'value': 284},
  {u'npi': u'1801894142', u'value': 497},
  {u'npi': u'1538182779', u'value': 995},
  {u'npi': u'1932190360', u'value': 2672},
  {u'npi': u'1114020336', u'value': 3264}]}

value = mydict['snpi']['value'] #value i'm working with below
npi = mydict['snpi']['npi'] #npi (identifier) i'm working with below
snpis = mydict['snpis'] #dict i'm working with below

要获取[id, value]列表(不仅仅是值列表),我使用此:

[[id,val] for diff, val, id in sorted((abs(x['value']-value), x['value'], x['npi']) for x in snpis)[:6]]

产生这个:

[[u'1932190360', 2672],
 [u'1114020336', 3264],
 [u'1538182779', 995],
 [u'1801894142', 497],
 [u'1336147685', 284],
 [u'1912989989', 232]]

修改

如果你正在处理一个字典(或列表),我实际上发现操纵Raymond的答案也很容易。

from heapq import nsmallest
[[i['npi'], i['value']] for i in nsmallest(6, snpis, key=lambda x: abs(x['value']-value))]

这将产生与上述输出相同的效果。

这个

nsmallest(6, snpis, key=lambda x: abs(x['value']-value))会产生一个字典。

答案 3 :(得分:0)

对于那些想要索引的人:

def find_nearest_index(array, value, k):
    array = np.array(array)
    return np.argsort(abs(array - value))[:k]

示例:

find_nearest_index([-3,0,1,2,4,5], 0.2, 4)

# array([1, 2, 3, 0], dtype=int64)
# distance = [3.20 0.20 0.80 1.80 3.80 4.80]