微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

给定一个可以在字典元组键之间找到的整数,在 dict 中查找值

如何解决给定一个可以在字典元组键之间找到的整数,在 dict 中查找值

给定一个包含元组键和字符串值的 x 字典:

x = {(0,4): 'foo',(4,9): 'bar',(9,10): 'sheep'}

任务是编写函数,找到给定特定数字的值,例如如果用户输入 3,它应该返回 'foo'。我们可以假设密钥中没有重叠的数字。

一个例子,如果用户输入 9,它应该返回 'bar'


我尝试将 x dict 转换为列表并按如下方式编写函数,但如果键中的值范围非常大,则它不是最佳的:

from itertools import chain

mappings = None * max(chain(*x))

for k in x:
    for i in range(k[0],k[1]):
        mappings[i] = x[k] 

def myfunc(num):
    return mapping[num]
  • 还可以如何编写 myfunc 函数
  • 是否有更好的数据结构来保留 mapping

解决方法

您可以简单地遍历键并比较值(而不是创建映射)。这比先创建映射要高效一些,因为您可以使用 (0,100000) 这样的键,这会产生不必要的开销。

根据 OP 的评论编辑答案

x = {(0,4): 'foo',(4,9): 'bar',(9,10): 'sheep'}

def find_value(k):
    for t1,t2 in x:
        if k > t1 and k <= t2:   # edited based on comments
            return x[(t1,t2)]
    
    # if we end up here,we can't find a match
    # do whatever appropriate,e.g. return None or raise exception
    return None

注意: 不清楚您的元组键是否包含输入数字的范围。例如。如果用户输入 4,他们应该得到 'foo' 还是 'bar'?这将影响您在我的代码段中上述功能的比较。(请参阅上面的编辑,这应该满足您的要求)。

在上面的这个例子中,输入 4 将返回 'foo',因为它满足 k >= 0 and k <= 4 的条件,因此在继续循环之前返回。

编辑:措辞和错别字修正

,

您可以将键转换为 numpy 数组并使用 numpy.searchsorted 来搜索查询。由于键是 left open,因此我将数组中键的打开值增加了 1

每个查询的顺序为 O(log(n))

创建一个数组:

A = np.array([[k1+1,k2] for k1,k2 in x])
>>> A
array([[ 1,4],[ 5,9],[10,10]])

搜索查询的功能:

def myfunc(num):
    ind1 = np.searchsorted(A[:,0],num,'right')
    ind2 = np.searchsorted(A[:,1],'left')
    if ind1 == 0 or ind2 == A.shape[0] or ind1 <= ind2: return None
    return vals[ind2]

打印:

>>> myfunc(3)
'foo'
,

迭代字典与键的比较:

x = {(0,10): 'sheep'}

def find_tuple(dct,num):
    for tup,val in dct.items():
        if tup[0] <= num < tup[1]:
            return val
    return None

print(find_tuple(x,3))
# foo
print(find_tuple(x,9))
# sheep
print(find_tuple(x,11))
# None

一个更好的数据结构是一个字典,只有区间的左边界(作为键)和相应的值。然后您可以使用 bisect 作为其他答案提及。

import bisect
import math

x = {
    -math.inf: None,0: 'foo',4: 'bar',9: 'sheep',10: None,}

def find_tuple(dct,num):
    idx = bisect.bisect_right(list(dct.keys()),num)
    return list(dct.values())[idx-1]

print(find_tuple(x,11))
# None
,

这是使用 pandas.IntervalIndexpandas.cut 的一种解决方案。请注意,我将最后一个键“调整”为 (10,11),因为我在 IntervalIndex 中使用了 closed="left"。如果您希望间隔在不同侧(或两侧)闭合,您可以更改此设置:

import pandas as pd

x = {(0,4): "foo",9): "bar",(10,11): "sheep"}

bins = pd.IntervalIndex.from_tuples(x,closed="left")
result = pd.cut([3],bins)[0]

print(x[(result.left,result.right)])

打印:

foo

使用 bisect 模块的其他解决方案(假设范围是连续的 - 所以没有“间隙”):

from bisect import bisect_left

x = {(0,10): "sheep"}

bins,values = [],[]
for k in sorted(x):
    bins.append(k[1])  # intervals are closed "right",eg. (0,4]
    values.append(x[k])

idx = bisect_left(bins,4)
print(values[idx])

打印:

foo

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。