27

Pythonのunittestモジュールを使用していて、2つの複雑なデータ構造が等しいかどうかを確認したいと思います。numpyオブジェクトは、数値、文字列、Pythonコンテナ(リスト/タプル/ディクト)、配列など、あらゆる種類の値を持つdictのリストにすることができます。後者は私がただすることができないので質問をする理由です

self.assertEqual(big_struct1, big_struct2)

それは

ValueError: The truth value of an array with more than one element is ambiguous.
Use a.any() or a.all()

私はこれのために私自身の平等テストを書く必要があると想像します。任意の構造で機能するはずです。私の現在のアイデアは、次のような再帰関数です。

  • の現在の「ノード」arg1をの対応するノードと直接比較しようとしarg2ます。
  • 例外が発生しない場合は、次に進みます(「ターミナル」ノード/リーフもここで処理されます)。
  • 捕まえられた場合ValueError、それが見つかるまで深くなりnumpy.arrayます;
  • 配列を比較します(たとえば、このように)。

少し問題があるように思われるのは、2つの構造の「対応する」ノードを追跡することですが、zipここで必要なのはおそらくそれだけです。

問題は、このアプローチの良い(より単純な)代替案はあるかということです。多分numpyこれのためのいくつかのツールを提示しますか?代替案が提案されていない場合は、このアイデアを実装し(より良いアイデアがない限り)、回答として投稿します。

PSこの問題に対処する質問を見たかもしれないと漠然と感じていますが、今は見つかりません。

PPS別のアプローチは、構造をトラバースしてすべてのをリストに変換する関数ですが、numpy.arrayこれは実装が簡単ですか?私には同じようです。


編集:サブクラス化numpy.ndarrayは非常に有望に聞こえますが、明らかに、比較の両側をテストにハードコードしていません。ただし、そのうちの1つは実際にハードコーディングされているため、次のことができます。

  • numpy.array;のカスタムサブクラスを入力します。
  • jterraceの答えに変更isinstance(other, SaneEqualityArray)します;isinstance(other, np.ndarray)
  • 比較では常にLHSとして使用してください。

この点に関する私の質問は次のとおりです。

  1. それは機能しますか(つまり、私には大丈夫に聞こえますが、いくつかのトリッキーなエッジケースは正しく処理されない可能性があります)?私のカスタムオブジェクトは、私が期待するように、再帰的な等価性チェックで常にLHSとして終了しますか?
  2. numpy繰り返しますが、より良い方法があります(実際の配列を持つ構造の少なくとも1つを取得した場合)。

編集2:試してみましたが、(一見)機能する実装がこの回答に示されています。

4

7 に答える 7

13

コメントしただろうが、長すぎる...

==おもしろいことに、配列が同じかどうかをテストするために使用することはできませんnp.testing.assert_array_equal。代わりに使用することをお勧めします。

  1. dtype、shapeなどをチェックします。
  2. これは、(float('nan') == float('nan')) == False(通常のPythonシーケンス==には、これを無視するさらに楽しい方法がありますPyObject_RichCompareBool。これは、(NaNが正しくない場合)isクイックチェック(もちろん、完璧なテスト用)を使用するためです。。
  3. またassert_allclose、実際の計算を行う場合、浮動小数点の等式は非常に扱いにくくなる可能性があり、通常はほぼ同じ値が必要になるためです。値はハードウェアに依存するか、場合によってはランダムになる可能性があるためです。

これがめちゃくちゃネストされているものが必要な場合は、pickleでシリアル化することをお勧めしますが、それは過度に厳密です(そして、ポイント3はもちろん完全に壊れています)。たとえば、配列のメモリレイアウトは重要ではありませんが、シリアル化。

于 2013-01-10T02:02:22.667 に答える
9

この関数は、オブジェクトのメソッドをassertEqual呼び出します。このメソッドは、複雑なデータ型に対して再帰する必要があります。__eq__例外はnumpyで、これには適切な__eq__メソッドがありません。この質問のnumpyサブクラスを使用すると、正気を平等な動作に戻すことができます。

import copy
import numpy
import unittest

class SaneEqualityArray(numpy.ndarray):
    def __eq__(self, other):
        return (isinstance(other, SaneEqualityArray) and
                self.shape == other.shape and
                numpy.ndarray.__eq__(self, other).all())

class TestAsserts(unittest.TestCase):

    def testAssert(self):
        tests = [
            [1, 2],
            {'foo': 2},
            [2, 'foo', {'d': 4}],
            SaneEqualityArray([1, 2]),
            {'foo': {'hey': SaneEqualityArray([2, 3])}},
            [{'foo': SaneEqualityArray([3, 4]), 'd': {'doo': 3}},
             SaneEqualityArray([5, 6]), 34]
        ]
        for t in tests:
            self.assertEqual(t, copy.deepcopy(t))

if __name__ == '__main__':
    unittest.main()

このテストは合格です。

于 2013-01-10T01:07:28.040 に答える
7

したがって、jterraceによって示されているアイデアは、わずかな変更を加えるだけでうまくいくようです。

class SaneEqualityArray(np.ndarray):
    def __eq__(self, other):
        return (isinstance(other, np.ndarray) and self.shape == other.shape and 
            np.allclose(self, other))

私が言ったように、これらのオブジェクトを含むコンテナは、同等性チェックの左側にある必要があります。次のようSaneEqualityArrayに既存のオブジェクトからオブジェクトを作成します。numpy.ndarray

SaneEqualityArray(my_array.shape, my_array.dtype, my_array)

ndarrayコンストラクターの署名に従って:

ndarray(shape, dtype=float, buffer=None, offset=0,
        strides=None, order=None)

このクラスはテストスイート内で定義され、テスト目的でのみ機能します。等価性チェックのRHSは、テストされた関数によって返される実際のオブジェクトであり、実際のオブジェクトが含まれていnumpy.ndarrayます。

PSこれまでに投稿された両方の回答の作成者のおかげで、両方とも非常に役に立ちました。このアプローチで問題が発生した場合は、フィードバックをお寄せください。

于 2013-01-11T11:09:38.110 に答える
2

使用したい比較を明示的に行う独自のassertNumpyArraysEqual()メソッドを定義します。そうすれば、本番コードは変更されませんが、単体テストで妥当なアサーションを作成できます。__unittest = Trueスタックトレースに含まれないように、を含むモジュールで定義してください。

import numpy
__unittest = True

def assertNumpyArraysEqual(self, other):
    if self.shape != other.shape:
        raise AssertionError("Shapes don't match")
    if not numpy.allclose(self, other)
        raise AssertionError("Elements don't match!")
于 2013-03-14T01:13:39.003 に答える
1

「2つのアイテムが目的の精度まで等しくない場合にAssertionErrorを発生させる」を確認numpy.testing.assert_almost_equalします。例:

 import numpy.testing as npt
 npt.assert_almost_equal(np.array([1.0,2.3333333333333]),
                         np.array([1.0,2.33333334]), decimal=9)
于 2016-08-19T10:53:07.210 に答える
1

私は同じ問題に遭遇し、オブジェクトの固定ハッシュの作成に基づいて同等性を比較する関数を開発しました。これには、オブジェクトのハッシュをコードに固定されているものと比較することで、オブジェクトが期待どおりであることをテストできるという追加の利点があります。

コード(スタンドアロンのPythonファイルはこちら)。fixed_hash_eq問題を解決する関数とcompute_fixed_hash、構造からハッシュを作成する 関数の2つの関数があります。テストはこちら

テストは次のとおりです。

obj1 = [1, 'd', {'a': 4, 'b': np.arange(10)}, (7, [1, 2, 3, 4, 5])]
obj2 = [1, 'd', {'a': 4, 'b': np.arange(10)}, (7, [1, 2, 3, 4, 5])]
obj3 = [1, 'd', {'a': 4, 'b': np.arange(10)}, (7, [1, 2, 3, 4, 5])]
obj3[2]['b'][4] = 0
assert fixed_hash_eq(obj1, obj2)
assert not fixed_hash_eq(obj1, obj3)
于 2017-11-01T08:23:57.200 に答える
0

@dbwに基づいて(感謝を込めて)、テストケースサブクラス内に挿入された次のメソッドは私にとってうまく機能しました:

 def assertNumpyArraysEqual(self,this,that,msg=''):
    '''
    modified from http://stackoverflow.com/a/15399475/5459638
    '''
    if this.shape != that.shape:
        raise AssertionError("Shapes don't match")
    if not np.allclose(this,that):
        raise AssertionError("Elements don't match!")

私はそれself.assertNumpyArraysEqual(this,that)を私のテストケースメソッドの内部として呼び出してもらい、魅力のように機能しました。

于 2016-03-30T21:55:02.120 に答える