浮点数作为字典键

32

我正在开发一个用于分析微孔板的类。样本在单独的文件中描述,并用于有序字典的条目中。其中一个键是pH,通常表示为浮点数,例如6.8。

我可以使用Decimal('6.8')将其作为十进制数导入,以避免浮点数作为字典键。另一个解决方案是将点替换为例如p,如6p8或在我的样本说明中写入6p8,并因此在开始时消除问题。但这将在以后造成麻烦,因为我无法绘制pH为6p8的图表。

您将如何解决这个问题?


也许您希望截断浮点数,以便在将其用作关键之前了解其质量? - Reblochon Masque
谢谢。我会考虑的。 - Moritz
6个回答

33

使用浮点数作为字典键没有问题。

只需将它们 round(n, 1) 规范化为您的键空间即可,例如:

>>> hash(round(6.84, 1))
3543446220
>>> hash(round(6.75, 1))
3543446220

1
你的解决方案和Reblochon的有什么区别?他建议我只使用round()而不需要额外的hash()。由于我的pH条件始终相差至少0.1,因此我不需要非常高的精度。 - Moritz
6
我并不是建议你显式地使用 hashhash值是字典判断键是否 可能 相等的依据(相等的值 必须 具有相同的哈希值)。这就是获得 O(1) 性能的方法。我只是想展示哈希值是相同的(当你理解 round 函数的结果时,这是很明显的)。 - John La Rooy
4
如果你相信这些浮点数都来自于同一来源并以相同的方式生成,那么可以采用这种方法。但是我不会完全相信不受我控制的数据。 - John La Rooy
1
@BobStein-VisiBone,如果字典仅仅依赖于哈希的唯一性,那么它们将会非常不稳定。 - John La Rooy
4
@BobStein-VisiBone,为了澄清 - 对于相等的值来说哈希必须是相等的,但反过来则不一定成立。这也就是说,哈希不需要唯一。你可以把按哈希查找看作是一个筛选器,这样你就不需要对每个键进行测试。在使用哈希筛选后,还需要测试键是否相等以排除虚假结果。 - John La Rooy
显示剩余10条评论

5
也许在将其用作键之前,您希望截断浮点数?
也许像这样:
a = 0.122334
round(a, 4)       #<-- use this as your key?

您的密钥现在是:

0.1223           # still a float, but you have control over its quality

你可以按照以下方式使用它:
dictionary[round(a, 4)]   

获取您的值


1
你为什么认为这比使用四舍五入的浮点数更安全? - John La Rooy
浮点数也是不可变的。在这种情况下,你的担忧是没有根据的,会给你带来性能上的惩罚。 - John La Rooy
一旦它们被舍入,二进制表示就是相同的,因此哈希值当然也是相同的。 - John La Rooy
不错的解决方案。而且很简单。 - Moritz
此外,原帖作者想要绘制数据,因此他必须将其转换回浮点数,这会增加更多不必要的开销。 - Padraic Cunningham

1
另一种方法是将键作为字符串输入,使用点(.)而不是字母p,然后将其重新转换为浮点数进行绘图。
个人观点,如果您不坚持使用字典格式,我会将数据存储为pandas数据框,并将pH作为列,因为这样更容易传递给绘图库。

0
如果您想在程序的多个位置使用浮点键字典,那么将如何使用它(即使用舍入键)的复杂性“隐藏”在新字典类(完整实现)中可能是值得的。
例如:
>>> d = FloatKeyDictionary(2, {math.pi: "foo"})
>>> d
{3.14: 'foo'}

>>> d[3.1415]
'foo'

>>> 3.1 in d
False

>>> d[math.e] = "My hovercraft is full of eels!"
>>> d
{3.14: 'foo', 2.72: 'My hovercraft is full of eels!'}

以下是简化版的概要:

import abc
import collections.abc


class KeyTransformDictionaryBase(dict, abc.ABC):

    @abc.abstractmethod
    def __key_transform__(self, key):
        raise NotImplementedError

    def __contains__(self, key):
        return super().__contains__(self.__key_transform__(key))

    def __getitem__(self, key):
        return super().__getitem__(self.__key_transform__(key))

    def __setitem__(self, key, value):
        return super().__setitem__(self.__key_transform__(key), value)

    def __delitem__(self, key):
        return super().__delitem__(self.__key_transform__(key))


class FloatKeyDictionary(KeyTransformDictionaryBase):

    def __init__(self, rounding_ndigits, data=None):
        super().__init__()
        self.rounding_ndigits = rounding_ndigits
        if data is not None:
            self.update(data)

    def __key_transform__(self, key):
        return round(key, self.rounding_ndigits)

0
另一个快速选项是使用浮点数的字符串。
a = 200.01234567890123456789
b = {str(a): 1}
for key in b:
    print(float(key), b[key])

会打印出来

(200.012345679, 1)

注意,a 在小数点后的第十位被截断


-1

另一个选择是使用元组:

dictionary = {(6.8,): 0.3985}
dictionary[(6.8,)]

然后稍后检索这些值以进行绘图就变得非常容易,可以使用类似以下的代码:

points = [(pH, value) for (pH,), value in dictionary.items()]
    ...

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