说我有一个列表[1,2,3,4,5,6,7]
。我想找到3个最接近的数字,比方说6.5。然后返回的值为[5,6,7]
。
在python中找到一个最接近的数字并不是那么棘手,可以使用
来完成min(myList, key=lambda x:abs(x-myNumber))
但我试图不围绕这个找到k个最接近的数字。是否有一种pythonic方式来实现上述任务?
答案 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,最接近colors,smallest diffs,最少的基因突变,欧几里得的单词将是有效的。距离等)。
nsmallest()和 sorted()都将返回按近似顺序排列的列表排名(关系是通过首先看到的值来确定的)。
对于那些感兴趣的人,对预期的比较次数here和here进行了一些分析。快速摘要:
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]]
执行以下操作:
(d, x)
,其中d
是指向目标的距离k
元素答案 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]