如何查找最接近给定值的值并返回相应的键

时间:2018-01-17 05:23:16

标签: python dictionary

假设我的字典看起来像这样:

my_dict = {'bananas':'3', 'apples':'10', 'pears':'9', 'grapes':'2', 'oranges':'21', 'peaches':'12'}

我想创建一个函数,当用户输入水果x时,返回最接近水果x的2个水果。

例如,如果用户将苹果输入为x,则该函数将返回梨和桃子。

如果葡萄以x输入,香蕉和梨将被退回。

从算法上讲,这非常简单。我想知道是否有一种pythonic方式来实现这一目标。

5 个答案:

答案 0 :(得分:1)

我认为你的意思是“概念上”它非常简单。但你仍然需要提出一个算法来做到这一点。

如果它实际上总是2个最接近的元素,那么到目前为止你可以有2个变量保持最接近和第二个最接近的匹配。然后遍历字典,对于您找到的每个水果,执行以下逻辑:如果比第一个最接近,则首先复制到最接近第二个,然后将新水果复制到第一个最接近的水果。否则,如果小于第二个最近,则在最接近的第二个上复制新的。

如果有可能推广到n个最接近的匹配,那么你可以这样做:创建一个heapq来保存n个最接近的匹配。通过字典迭代。在每一步,将(距离,果实)元组推入heapq。如果heapq的长度大于n,则从队列中弹出最大元素(最长距离)并丢弃它。最后,heapq将保存n个最接近的项目。

或者您可以使用列表推导来创建(距离,水果)对的列表,然后对其进行排序,然后获取n个最小的条目(在您的情况下n = 2)。

根据@ RoadRunner的建议,另一个选项是创建和存储(count,fruit)元组的排序列表(例如search_list = sorted([(c, f) for (f, c) in my_dict.items()])。然后使用bisect模块中的bisect函数快速找到哪里(x_count,x)属于此列表。然后从那里向前或向后检查最多2个位置的距离,为这些候选者创建(x_count-fruit_count,fruit)对的(短)列表。然后对此列表进行排序并取前两项。

我没有提供完整的细节,因为这听起来有点像家庭作业,但希望这会给你一些想法。

答案 1 :(得分:1)

这是一种使用deques的简单方法。

<强>鉴于

import collections as ct


my_dict = {
    "bananas":"3", "apples":"10", "pears":"9",
    "grapes":"2", "oranges":"21", "peaches":"12"
}

<强>代码

def search_neighbors(query, sorted_iterable):
    """Return the nearest neighbors."""    
    # Build data structures
    lookup_idx = {v[0]: i for i, v in enumerate(sorted_iterable)}
    dq = ct.deque(sorted_iterable)

    # Pop searched items from a deque
    idx = lookup_idx[query]
    dq.rotate(-idx)      
    left = dq.pop()
    center = dq.popleft()
    right = dq.popleft()
    if left[-1] > right[-1]:
        # Correction for min/max values
        if center[-1] == min(d.values()):
            left = dq.popleft()
        else:
            right = dq.pop()
    return left, right   

<强>演示

d = {k: int(v) for k, v in my_dict.items()}
sorted_items = sorted(d.items(), key=lambda x: x[-1])

对于大多数情况:

search_neighbors("apples", sorted_items)
# (("pears", 9), ("peaches", 12))

search_neighbors("bananas", sorted_items)
# (('grapes', 2), ('pears', 9))

search_neighbors("peaches", sorted_items)
# (('apples', 10), ('oranges', 21))

查询最小/最大值:

search_neighbors("grapes", sorted_items)
# (("pears", 9), ("bananas", 3))

search_neighbors("oranges", sorted_items)
# (('peaches', 12), ('apples', 10))

<强>详情

字典将数字字符串转换为整数,以便对其进行排序。排序的键在反向字典中编制索引以便更快地查找。已排序的项目将添加到双端队列中。将双端队列旋转到查询项目的位置后,弹出最近的邻居(左右)。如果检测到最小/最大值,即弹出项目的值不按顺序,则在任一方向上弹出下一个连续项目。注意:在此更正过程中,变量会被覆盖,不再是严格的左右方向。

有关deque.popleft()的效果,请参阅此post

答案 2 :(得分:0)

可以使用这样的东西,

你的范围非常疯狂,但我相信调整key-3key+4会处理你的工作。

def near():
    key = int(my_dict[input("Enter Key:")])
    return [i for i in my_dict if int(my_dict[i]) in range(key-3,key+4)]

现在当你运行它时:

>>> near()
Enter Key:apples
['pears', 'peaches', 'apples']
>>> 

您可以轻松地从列表中删除苹果,但这些都是为您准备的!

答案 3 :(得分:0)

这样的事情:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
    intent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:+91100"));
    startActivity(intent);
} else {
    intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:+91100"));
    startActivity(intent);
}

其工作原理如下:

from operator import itemgetter

def search(x, fruits):
    # get the count of the fruit
    count = fruits[x]

    # get the rest of the elements
    rest = {k: _ for k, _ in fruits.items() if k != x}

    # dictionary that stores fruits count differences
    diff = {_: abs(int(count) - int(v)) for _, v in rest.items()}

    # get the top two closest fruits and store them in a set
    top_two = {x for x, _ in sorted(diff.items(), key = itemgetter(1)) [:2]}

    # return the top two closest
    return [(x, fruits[x]) for x in fruits if x in top_two]

答案 4 :(得分:0)

以下是使用迭代器和第三方工具more_itertools.adajecent的单独方法。此代码产生距离查询项目1或更远的邻居。

<强>代码

def search_neighbors(query, sorted_items, distance=1):
    """Yield neighboring items."""
    if query in sorted_items[0] or query in sorted_items[-1]:
        if query in sorted_items[-1]:
            sorted_items = reversed(sorted_items)

        sorted_iter = iter(sorted_items)
        next(sorted_iter)
        for _ in range(distance*2):
            yield next(sorted_iter)
    else:
        pred = lambda x: x[0] == query
        items = (item for bool_, item in mit.adjacent(pred, sorted_items, distance) if bool_)
        for _ in range(distance):
            yield next(items)
        next(items)
        for _ in range(distance):
            yield next(items)

# Pre-build dictionaries
d = {k: int(v) for k, v in my_dict.items()}
sorted_items = sorted(d.items(), key=lambda x: x[-1])

<强>演示

项目根据其值进行排序。对于列表中的项目(远离两端),可以产生左右邻居。

>>> # Non-Terminal Items
>>> list(search_neighbors("apples", sorted_items))
[("pears", 9), ("peaches", 12)]
>>> list(search_neighbors("bananas", sorted_items))
[('grapes', 2), ('pears', 9)]
>>> list(search_neighbors("peaches", sorted_items))
[('apples', 10), ('oranges', 21)]

在列表末尾找到的项目会有不同的处理方式 - 连续项目可以正向或反向生成。

>>> # Terminal Items
>>> list(search_neighbors("grapes", sorted_items))
[("pears", 9), ("bananas", 3)]
>>> list(search_neighbors("oranges", sorted_items))
[('peaches', 12), ('apples', 10)]
对于终端查询,

2n个邻居可以获得最远距离为n的非终端查询,而2n可用于终端查询。

>>> # Distance
>>> n = 2
>>> list(search_neighbors("apples", sorted_items, distance=n))
[('bananas', 3), ('pears', 9), ('peaches', 12), ('oranges', 21)]
>>> list(search_neighbors("grapes", sorted_items, distance=n))
[('bananas', 3), ('pears', 9), ('apples', 10), ('peaches', 12)]

如果距离大于已排序的可迭代,则迭代器会短路。

>>> # Early Termination
>>> list(search_neighbors("grapes", sorted_items, distance=10))
[('bananas', 3),
 ('pears', 9),
 ('apples', 10),
 ('peaches', 12),
 ('oranges', 21)]