2012-12-06 68 views
6

辞書を比較し、浮動小数点比較を「ファジー」にしたい場合は、numpy.allclose()を使用します。しかし、デフォルトの==または!=をdictsのPythonで使用しても、これは実行されません。浮動小数点値と浮動小数点値の比較

おそらく、安全なクリーンアップのためにコンテキストマネージャを使用して浮動小数点比較操作を変更する方法があるのだろうかと思っていました。

ここでは例が役立つと思います。私は、あらゆる種類の値を含む深く入れ子にされた辞書を持っています。これらの値の一部は浮動小数点値です。私はこの二つのdictsを比較する!=を使用し、唯一の違いは、内部に浮動小数点数をしている場合、それはTrueを返す持ちたいなど

d1 = {'a': {'b': 1.123456}} 
d2 = {'a': {'b': 1.1234578}} 

、落とし穴のトンは、浮動小数点値を「比較」の存在を知っていますある範囲。例えば、(私がまだ望んでいる精度を確信していない)である場合、異なる値を数えないでください。

私は再帰的に自分自身を調べて、手動でnumpy.allclose()を浮動小数点値として使用し、他のすべての型の通常の等価性テストに戻すことができると思います。しかし、これはややこしいエラーです。私はこれが許容される解決策であると思うし、私はそれを好きに見たいと思う。うまくいけば、よりエレガントなものがあるでしょう。

私の頭の中の上品な解決策は、次のようになります。このようなものでも可能である場合しかし、私は知らない。

with hacked_float_compare: 
    result = d1 != d2 

したがって、このコンテキストマネージャの内部で、私は私自身の比較やnumpy.allclose()のいずれかで(ちょうど標準float()値に対する浮動小数点比較を交換することになります。

は再び、私はそれはCで書かれているのでfloat()にパッチを適用サルが実際に行うことができないので、これが可能であるか分からない。私はまた私自身にdicts内のすべての浮動小数点値を変更することを避けるためにたい__eq__()を持つfloatクラスです。 wでもね?

+0

オプションは、floatのラッパーを作成し、そこに '__eq__'をオーバーライドすることです。 – NullUserException

+0

しかし、すべてのフロートを 'fuzzyfloat(0.5)'などで作成する必要があります。 – alexis

+0

右。私はこのアプローチが有効であることを知っています。私がそれを避けることができれば、特別なオブジェクト/クラスを使用する必要はありません。私は実際には比較がこの1つの事例では「あいまい」である必要があります。このため、私はコンテキストマネージャを使い、限られた時間だけ異なる「モード」に入ることを望んでいたのです。 –

答えて

5

組み込み型のサブクラス化は避けてください。あなたは、あなたのオブジェクトが何らかの未知の理由のためにタイプを変更したことを知ったときにそれを後悔します。代わりに委任を使用してください。たとえば、次のように

import operator as op 


class FuzzyDict(object): 
    def __init__(self, iterable=(), float_eq=op.eq): 
     self._float_eq = float_eq 
     self._dict = dict(iterable) 

    def __getitem__(self, key): 
     return self._dict[key] 

    def __setitem__(self, key, val): 
     self._dict[key] = val 

    def __iter__(self): 
     return iter(self._dict) 

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

    def __contains__(self, key): 
     return key in self._dict 

    def __eq__(self, other): 
     def compare(a, b): 
      if isinstance(a, float) and isinstance(b, float): 
       return self._float_eq(a, b) 
      else: 
       return a == b 
     try: 
      if len(self) != len(other): 
       return False 
      for key in self: 
       if not compare(self[key], other[key]): 
        return False 
      return True 
     except Exception: 
      return False 

    def __getattr__(self, attr): 
     # free features borrowed from dict 
     attr_val = getattr(self._dict, attr) 
     if callable(attr_val): 
      def wrapper(*args, **kwargs): 
       result = attr_val(*args, **kwargs) 
       if isinstance(result, dict): 
        return FuzzyDict(result, self._float_eq) 
       return result 
      return wrapper 
     return attr_val 

と使用例:

>>> def float_eq(a, b): 
...  return abs(a - b) < 0.01 
... 
>>> A = FuzzyDict(float_eq=float_eq) 
>>> B = FuzzyDict(float_eq=float_eq) 
>>> A['a'] = 2.345 
>>> A['b'] = 'a string' 
>>> B['a'] = 2.345 
>>> B['b'] = 'a string' 
>>> B['a'] = 2.3445 
>>> A == B 
True 
>>> B['a'] = 234.55 
>>> A == B 
False 
>>> B['a'] = 2.345 
>>> B['b'] = 'a strin' 
>>> A == B 
False 

そして、彼らが入れ子になった場合でも動作します。

>>> A['nested'] = FuzzyDict(float_eq=float_eq) 
>>> A['nested']['a'] = 17.32 
>>> B['nested'] = FuzzyDict(float_eq=float_eq) 
>>> B['nested']['a'] = 17.321 
>>> B['b'] = 'a string' # changed before 
>>> A == B 
True 
>>> B['nested']['a'] = 17.34 
>>> A == B 
False 

dictための完全な交換が少しより多くのコードを必要とし、おそらくいくつかのでしょうそれがどれほど堅牢であるかをテストすることができますが、上記のソリューションでさえ、多くの機能を提供します(例えば、copysetdefaultgetupdateなど)


あなたはビルトインのサブクラスはならない理由について。

この解決策は簡単で正しいと思われますが、一般的にはそうではありません。 まず、組み込み型をサブクラス化することはできますが、サブクラスとして使用するように記述されているわけではありませんので、何らかの作業をするために思ったよりも多くのコードを書く必要があります。

また、組み込みのメソッドを使用することもできますが、これらのメソッドはクラスのインスタンスではなく組み込み型のインスタンスを返します。つまり、すべての単一メソッドを再実装する必要がありますタイプの。また、組み込み関数で実装されていない他のメソッドを実装する必要があることもあります。例えば

、あなたはlistだけ__iadd____add__を実装しているので、あなたがこれらの2つの方法を安全​​に再実装されます、と思うかもしれませんが、あなたは間違っているlistサブクラス化!

[1,2,3] + MyList([1,2,3]) 

MyListlist正常ではないが返されます:あなたはまた、それ以外のような表現は、__radd__を実装する必要があります。

要約すると、組み込み関数をサブクラス化すると、最初に考えたよりも多くの結果が発生し、予期せぬ種類や動作の変化による予期しないバグが発生する可能性があります。ログにオブジェクトのインスタンスを単に印刷することはできないので、デバッグもより困難になります。表現は正しいでしょう!この微妙なバグをキャッチするには、実際にはすべてのオブジェクトのクラスをチェックする必要があります。

特定の状況では、1つのメソッド内でしか辞書を変換しない場合は、サブクラス化の多くの欠点を避けることができますが、その時点で関数を書いてdictそれと? dictを比較を行うライブラリ関数に渡したい場合を除いて、これはうまくいくはずです。

+1

これはかなり良いですね。しかし、私の場合、 'dict'をサブクラス化するだけで大​​丈夫かもしれません。私はちょうどこれを比較するためにローカルにdictsを変換したい。したがって、この新しいクラスは、1つのメソッドに対して内部的にのみ使用されます。これは理にかなって合理的に見えますか? –

+0

しかし、他のdictが最初のdictにないキーを持つ場合、この解決法はFalseを返さないでしょう。だから、この解決法は、単に浮動小数点比較以上の方法で比較辞書のセマンティクスを変更するのですか? –

+0

@ durden2.0最初に投稿したときから少し変更しましたが、大丈夫だと思いました。この違いを読んだ 'ソート済み(自己)==ソート済(その他)'を最初にチェックする前に、私はこれも大丈夫だと思います。なぜなら、キーの数が違うと、それは長さを比較することによって捕らえられ、その後、私は 'self 'のすべてのキーをチェックし、' other'になければ 'KeyError'が呼び出されます。 'Exception'を正しく返して' False'を返すので、大丈夫です。 とにかく、変更がメソッド内にしかないことが保証されているなら、おそらく 'dict'のサブクラス化は大丈夫です。 – Bakuriu

1

比較演算子をオーバーライドするには、別の演算子を使用する派生クラスを定義する必要があります。だからあなたはそれをあなたが提案するようにすることはできません。何あなたができることを示唆し(@Nullとして)「ファジーフロート」クラスを派生、またはdictから派生したクラスとそれが山車にファジー比較を使用するように指定され:

class fuzzydict(dict): 
    def __eq__(self, other): 
     """Manually compare each element of `self` with `other`. 
      Float values are compared up to reasonable precision.""" 

あなたを介して解約する必要がありますあなた自身の辞書比較のロジックは、組み込みの比較のように速くないかもしれませんが、コードにdict1 == dict2と書くことができます。浮動小数点を含む可能性のあるすべての(ネストされた)辞書には、dictの代わりにfuzzydictを必ず使用してください。

しかし、あなたが不確定性を危険にさらしていることを追加する必要があります。辞書は同じですがわずかに異なる数字が含まれているため、使用する辞書に応じてではない結果が返されます。私の意見では、より安全な(そしてサワー)アプローチは、あなたが辞書に挿入するときに浮動小数点数を丸めて、厳密に等しいと比較することです。

+0

はい、これも機能します。しかし、私は自分自身のdictクラスを作成し、dictの '__eq__'に比較コードを入れることにはあまり効果がありません。このまったく同じコードは、2つのディクテーションをとる方法に過ぎません。それから、私はこの新しいタイプのdictをどこにでも使用したり、既存のdictsを変換する必要はありません。また、私が多くの場所でこれをやっていれば、この解決策はうまくいくでしょう。しかし、この比較が重要な領域は1つだけです。 –

+0

また、これらの浮動小数点数での計算は、とにかく浮動小数点表現などのためにやっかいです。さらに、私のシナリオでは、これらの数値がお互いの範囲内にあることに本当に気にしません。これは、道路の下で奇妙な操作を引き起こすことはありません。これは、これを適用したいコードのごく限られた領域です。 –

+0

Pythonのdict比較は再帰的です。クラスを派生した場合、Pythonは再帰を処理します。欠落しているか余分なキーがあるかどうかをチェックし、値を比較するだけでフラットロジックを実装するだけです。 – alexis

2

私の状況では、サブクラス化は最良の方法ではなかったと思います。私はおそらくhereを使用する解決策を策定しました。

これは私がこのスレッドから学んだことに基づいた共同アプローチだったので、これは受け入れられた答えではありません。ただ他者が恩恵を受けることができる「解決策」がほしいと思った。

関連する問題