调用最近的字典键小于某个给定值

时间:2019-07-10 16:30:00

标签: python lambda

我有一个以时间值作为键的字典,我需要检索与那些键关联的值。程序的性质意味着我几乎永远无法使用精确的现有键来调用字典,因此,我要做的是以下操作:我想将随机输入值传递给字典和字典找到最接近但小于输入值的键,然后给我绑定该键的值。

到目前为止,我尝试过的是一个lambda表达式,该表达式应该找到比输入低的最接近键,但是当我执行它时,我发现它没有给我期望的值。由于我的实际代码相当长且复杂,因此我构建了一个小型测试程序以更清楚地观察我的结果:

import random as r

my_dict = {0.0: 0, 1.0: 1, 2.0: 2, 3.0: 3, 4.0: 4, 5.0: 5}

value = round(r.uniform(0, 5), 1)
print('Random Value is: ' + str(value))

dict_value = my_dict[min(my_dict, key=lambda x:abs(x-value))]
print('Corresponding Dictionary Value is: ' + str(dict_value))

此程序成功生成一个四舍五入到十进制小数的随机浮点值。当它传递给lambda函数时,它不会给出准确的响应值。例如,如果随机值是7.7,则相应的字典值将返回3(这是错误的),随机值3.1将返回3(这是正确的),4.7则返回5(错误的),等等。我可以理解为什么这是发生的,因为我要从x减去我的值,这将比较两个数字与每个键的差。也没有什么要求最接近的键必须低于输入,只是为了找到通常最接近输入的键,但是我不确定如何实现这一点。我希望我已经清楚了,但如果不是,请提出问题。谢谢!

3 个答案:

答案 0 :(得分:0)

如果您的上下文需要您提供的数据结构,则可以尝试类似的操作(排除不了太大值的dict理解):

import random as r

my_dict = {0.0: 0, 1.0: 1, 2.0: 2, 3.0: 3, 4.0: 4, 5.0: 5}

value = round(r.uniform(0, 5), 1)
print('Random Value is: ' + str(value))

new_dict = {k:v for (k,v) in my_dict.items() if v<value}
dict_value = my_dict[min(new_dict, key=lambda x:abs(x-value))]
print('Corresponding Dictionary Value is: ' + str(dict_value))

答案 1 :(得分:0)

df <- as.data.frame(
  read.table(text = 
"Id         Date1       Date2     Value
 1       01/01/18    01/03/18     1.000
 2       01/05/18    01/07/18     500
 3       01/03/18    01/06/18     17.000
 4       01/12/18    01/01/19     670
 5       01/10/18    01/12/18     9.600",header=TRUE)
) %>%mutate_at(2:3, function(x) as.Date(as.character(x), "%m/%d/%y"))

RefDate <- as.Date("01/04/18", "%m/%d/%y")
ref <- c(0, 60, 90, 180, 270, 360)
dat <- NULL

for (i in 1:length(ref)) {
  for (j in 1:nrow(df)) {
    if ((RefDate - df$Date1[j] > ref[i]) & (RefDate - df$Date1[j] > RefDate - df$Date2[j])) {
      dat[j] <- df$Value[j]
    } else
      dat[j] <- 0
  }
  df[,paste0("X", i)] <- dat
}

df
  Id      Date1      Date2 Value X1 X2 X3 X4 X5 X6
1  1 2018-01-01 2018-01-03   1.0  1  0  0  0  0  0
2  2 2018-01-05 2018-01-07 500.0  0  0  0  0  0  0
3  3 2018-01-03 2018-01-06  17.0 17  0  0  0  0  0
4  4 2018-01-12 2019-01-01 670.0  0  0  0  0  0  0
5  5 2018-01-10 2018-01-12   9.6  0  0  0  0  0  0

如果您确实选择使用其他数据结构,则此技术仍适用于列表:

import random

my_dict = {0.0: 0, 1.0: 1, 2.0: 2, 3.0: 3, 4.0: 4, 5.0: 5}
rand_num = round(random.uniform(0, 5), 1)

# This will find the dict key that corresponds to the value you are seeking
index = min(my_dict, key=lambda n: abs(n-value))

# Then just grab the value from the dict
print(my_dict[index])

或者,您可以过滤出字典中大于给定值的所有键:

keys = [1.0, 2.0, 3.0]
value = 1.2
target = min(keys, key=lambda n: abs(n-value))
print(target)

您还可以使用过滤器功能:

value = 2.2
valid_keys = [key for key in my_dict if key <= value]
target_key = max(valid_keys)
answer = my_dict[target_key]

更新:此处的过滤示例将仅找到最接近给定值的键,这与您要求的值不完全相同。我将把这些示例留在这里,供以后搜索此问题可能对他们有用的任何人使用。对于您的特定用例,请使用第一个示例。

答案 2 :(得分:0)

您可以利用bisect模块并在collections.abc的帮助下创建自定义容器:

from collections.abc import Mapping
import bisect

class closest_dict(Mapping):
    def __init__(self, items):
        s = [*sorted(items)]
        self._keys = [i[0] for i in s]
        self._items = [i[1] for i in s]

    def __getitem__(self, key):
        idx = bisect.bisect_left(self._keys, key)

        if idx > len(self._keys) - 1:
            return self._items[-1]

        if abs(self._keys[idx-1] - key) < abs(self._keys[idx] - key):
            return self._items[idx-1]

        return self._items[idx]

    def __iter__(self):
        yield from self._keys

    def __len__(self):
        return len(self._keys)

my_dict = {0.0: 0, 1.0: 1, 2.0: 2, 3.0: 3, 4.0: 4, 5.0: 5}

d = closest_dict( (k, v) for k, v in my_dict.items() )

for val in [-1.0, 0.0, 0.1, 2.1, 6.0]:
    print('Closest value to key {} is {}'.format(val, d[val]))

打印:

Closest value to key -1.0 is 0
Closest value to key 0.0 is 0
Closest value to key 0.1 is 0
Closest value to key 2.1 is 2
Closest value to key 6.0 is 5