找出距离某一特定值最近的k个键值对项目

2024-09-27 23:20:37 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我们要找到两个值最接近10的项:

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内置的方法来实现这一点和/或更优化的版本?


Tags: lambdakeyinforreturndefabsppp
3条回答

您可以使用^{}

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))时间内运行。你知道吗

如果您不介意使用熊猫:

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}

如果您需要经常执行查找,我们可以将此算法设置为O(log n)算法,首先将数据存储在排序的列表中:

from operator import itemgetter

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

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

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 logn)(元素数为n)。如果我们将上述方法推广到提取k元素(通过增加切片),则需要O(logn+klogk)来执行查找。你知道吗

相关问题 更多 >

    热门问题