查找其值最接近某个值的k个字典项

时间:2018-10-14 19:24:57

标签: python sorting dictionary nearest-neighbor

假设我们要查找其值最接近10的2个项目:

A = {'abc': 12.3, 'def': 17.3, 'dsfsf': 18, 'ppp': 3.2, "jlkljkjlk": 9.23}

它适用于:

def nearest(D, centre, k=10):
    return sorted([[d, D[d], abs(D[d] - centre)] for d in D], key=lambda e: e[2])[:k]

print(nearest(A, centre=10, k=2))
  

[['jlkljkjlk',9.23,0.7699999999999996],['abc',12.3,2.3000000000000007]

但是当dict的大小更大(成千上万项)时,是否有Python内置的方法来做到这一点和/或优化版本?

3 个答案:

答案 0 :(得分:7)

如果您不介意使用熊猫:

import pandas as pd
closest = (pd.Series(A) - 10).abs().sort_values()[:2]
#jlkljkjlk    0.77
#abc          2.30
closest.to_dict()
#{'jlkljkjlk': 0.7699999999999996, 'abc': 2.3000000000000007}

答案 1 :(得分:3)

您可以使用heapq.nsmallest()

from heapq import nsmallest
A = {'abc': 12.3, 'def': 17.3, 'dsfsf': 18, 'ppp': 3.2, 'jlkljkjlk': 9.23}
def nearest(D, centre, k=10):
    return [[x, D[x], abs(D[x] - centre)] for x in nsmallest(k, D, key=lambda x: abs(D[x] - centre))]

print(nearest(A, centre=10, k=2))
# [['jlkljkjlk', 9.23, 0.7699999999999996], ['abc', 12.3, 2.3000000000000007]]

就时间复杂度而言,它是在O(n log(k))时间内运行,而不是基于对字典进行排序的解决方案的O(n log(n))

答案 2 :(得分:2)

鉴于您需要经常执行查找,我们可以将其存储为 sorted 列表中的数据,从而将其作为 O(log n)算法: / p>

from operator import itemgetter

ks = sorted(A.items(), key=itemgetter(1))
vs = list(map(itemgetter(1), ks))

然后对于每个项目,我们可以使用bisect.bisect_left点来确定插入点。然后,我们可以检查两个周围的值,以检查最小的值,并返回相应的键。

from bisect import bisect_left
from operator import itemgetter

def closests(v):
    idx = bisect_left(vs, v)
    i, j = max(0, idx-1), min(idx+2, len(ks))
    part = ks[i:j]
    return sorted([[*pi, abs(pi[-1]-v)] for pi in part], key=itemgetter(-1))[:2]

上面的内容可能看起来并不算是一种改进,但是在这里,我们将始终在sorted(..)中最多评估 三个元素,而bisect_left将对对数进行评估元素数量。

例如:

>>> closests(1)
[['ppp', 3.2, 2.2], ['jlkljkjlk', 9.23, 8.23]]
>>> closests(3.2)
[['ppp', 3.2, 0.0], ['jlkljkjlk', 9.23, 6.03]]
>>> closests(5)
[['ppp', 3.2, 1.7999999999999998], ['jlkljkjlk', 9.23, 4.23]]
>>> closests(9.22)
[['jlkljkjlk', 9.23, 0.009999999999999787], ['abc', 12.3, 3.08]]
>>> closests(9.24)
[['jlkljkjlk', 9.23, 0.009999999999999787], ['abc', 12.3, 3.0600000000000005]]

“加载”阶段因此需要 O(n log n)(元素数量为 n )。然后,如果我们概括上述方法以获取 k 个元素(通过增加切片),则需要 O(log n + k log k)进行查找。 / p>