检查数字列表是否在特定范围内?

4

我尝试离散化一些数字,通过查看它们是否在给定范围内,然后根据范围分配一个数字,但是我得到的结果并不完全正确。

mapp 是定义范围和相应值的字典。

lst 是我想要与这些范围匹配并为其分配标识符的数字列表。

mapp = {(0,100): 1, (100,400): 2, (400,800): 3}


lst = [3.5, 5.4, 300.12, 500.78, 600.45, 900.546]


def discretize(mapping_dict, list_of_values):
    print "\n"
    location = []
    for x in sorted(list_of_values):
        for (lower_bound,upper_bound),value in mapping_dict.items():
            if round(x) in range(lower_bound,upper_bound):
                print round(x), "yes", value

                distance = mapping_dict[(lower_bound,upper_bound)]
                location.append((distance))


        else:
            print round(x), "no"

            distance = len(mapping_dict.items())+10
            location.append((distance))

    return location

我期望得到的结果是:[1, 1, 2, 3, 3, 13],但实际的结果与此不符。
这是我得到的实际结果,它是不正确的:
4.0 yes 1
4.0 no         #wrong!
5.0 yes 1
5.0 no         #wrong!
300.0 yes 2
300.0 no         #wrong!
501.0 yes 3
501.0 no         #wrong!
600.0 yes 3
600.0 no         #wrong!
901.0 no         #CORRECT

[1, 13, 1, 13, 2, 13, 3, 13, 3, 13, 13]

我在4.0处得到了no,这是不正确的,等等。

问题出在哪里?

谢谢。


1
一旦找到正确的组,你可能只需要从循环中 break 出来,否则 else 语句将会一直被执行。 - tobias_k
1
你绝对不想像你这样创建范围。你只需要使用 lower_bound <= val < upper_bound 或类似的东西。 - acushner
3个回答

3
mapp = {(0,100): 1, (100,400): 2, (400,800): 3}
lst = [3.5, 5.4, 300.12, 500.78, 600.45, 900.546]
result = []
for l in lst:
    for m in mapp:
        if m[0] < l < m[1]:
            result.append(mapp[m])

print result

输出:

[1, 1, 2, 3, 3]

编辑:

result = []
for l in lst:
    flag=True
    for m in mapp:
        if m[0] < l < m[1]:
            result.append(mapp[m])
            flag = False
            break
    if flag:
        result.append(-1)
print result

输出:

[1, 1, 2, 3, 3, -1]

谢谢,但是这个解决方案仍然没有考虑到900.546的情况。@user3 - Kristof Pal
你如何计算900.546的值? - venpa
1
@user3пјҢдҪҝз”ЁflagеҸҳйҮҸзҡ„иҝҷз§Қз»“жһ„еңЁPythonдёӯе·Із»Ҹе’Ңfor/break/elseиҜӯеҸҘе®Ңе…Ёзӯүд»·дәҶгҖӮ - tobias_k

1
在你的for循环后面放一个else是正确的!当你在一个循环后面放一个else,这个else块会在每次正常退出循环时执行,即没有使用例如break。因此,(假设你的组不重叠)你只需要在if块的末尾添加一个break语句,即在location.append((distance))之后。然后它就可以按预期工作了。
此外,你应该使用<=<来检查数字是否在range中(每次都创建并搜索列表!)。而且,你已经有了value,为什么不使用它呢?
for (lower_bound, upper_bound), value in mapping_dict.items():
    if lower_bound <= x < upper_bound:
        location.append(value)
        break
else:
    location.append(len(mapping_dict) + 10)

1

我想我曾经遇到过类似的问题,因为我找到了一个小的RangeDict类:

class RangeDict (dict):
    def __init__ (self, *args):
        super ().__init__ ()

    def __setitem__ (self, k, v):
        if not isinstance (k, slice): raise ValueError ('Indices must be slices.')
        super ().__setitem__ ( (k.start, k.stop), v)

    def __getitem__ (self, k):
        for (start, stop), v in self.items ():
            if start <= k < stop: return v
        raise IndexError ('{} out of bounds.'.format (k) )

我希望这个类可以包含您想要的功能。显然,查找是O(N)而不是O(1)。

示例用法:

r = RangeDict ()
r [0:100] = 1
r [100:400] = 2
r [400:800] = 3

for x in [3.5, 5.4, 300.12, 500.78, 600.45, 900.546]:
    print (r [x] )
#Last value raises IndexError

1
+1 酷!只是好奇:为什么你不使用 if start <= k < stop: return v?有特殊原因吗? - tobias_k
为了最小化比较。 - Hyperboreus
虽然如此,但 x < y < z 不就像 x < y and y < z 吗?也就是说,如果第一个条件不满足,它也会提前停止,对吧? - tobias_k
@tobias_k 哦,很好的发现。如果它被惰性地评估(为什么不应该呢),它将归结于相同的结果。感谢您的输入。 - Hyperboreus
2
@Hyperboreus 索性比较链接的懒惰特性已经被记录,中间表达式仅在第一次评估时评估。 - Bakuriu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接