assertAlmostEqual in Python unit-test for collections of floats











up vote
49
down vote

favorite
9












The assertAlmostEqual(x, y) method in Python's unit testing framework tests whether x and y are approximately equal assuming they are floats.



The problem with assertAlmostEqual() is that it only works on floats. I'm looking for a method like assertAlmostEqual() which works on lists of floats, sets of floats, dictionaries of floats, tuples of floats, lists of tuples of floats, sets of lists of floats, etc.



For instance, let x = 0.1234567890, y = 0.1234567891. x and y are almost equal because they agree on each and every digit except for the last one. Therefore self.assertAlmostEqual(x, y) is True because assertAlmostEqual() works for floats.



I'm looking for a more generic assertAlmostEquals() which also evaluates the following calls to True:





  • self.assertAlmostEqual_generic([x, x, x], [y, y, y]).


  • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y}).


  • self.assertAlmostEqual_generic([(x,x)], [(y,y)]).


Is there such a method or do I have to implement it myself?



Clarifications:




  • assertAlmostEquals() has an optional parameter named places and the numbers are compared by computing the difference rounded to number of decimal places. By default places=7, hence self.assertAlmostEqual(0.5, 0.4) is False while self.assertAlmostEqual(0.12345678, 0.12345679) is True. My speculative assertAlmostEqual_generic() should have the same functionality.


  • Two lists are considered almost equal if they have almost equal numbers in exactly the same order. formally, for i in range(n): self.assertAlmostEqual(list1[i], list2[i]).


  • Similarly, two sets are considered almost equal if they can be converted to almost equal lists (by assigning an order to each set).


  • Similarly, two dictionaries are considered almost equal if the key set of each dictionary is almost equal to the key set of the other dictionary, and for each such almost equal key pair there's a corresponding almost equal value.


  • In general: I consider two collections almost equal if they're equal except for some corresponding floats which are just almost equal to each other. In other words, I would like to really compare objects but with a low (customized) precision when comparing floats along the way.











share|improve this question




























    up vote
    49
    down vote

    favorite
    9












    The assertAlmostEqual(x, y) method in Python's unit testing framework tests whether x and y are approximately equal assuming they are floats.



    The problem with assertAlmostEqual() is that it only works on floats. I'm looking for a method like assertAlmostEqual() which works on lists of floats, sets of floats, dictionaries of floats, tuples of floats, lists of tuples of floats, sets of lists of floats, etc.



    For instance, let x = 0.1234567890, y = 0.1234567891. x and y are almost equal because they agree on each and every digit except for the last one. Therefore self.assertAlmostEqual(x, y) is True because assertAlmostEqual() works for floats.



    I'm looking for a more generic assertAlmostEquals() which also evaluates the following calls to True:





    • self.assertAlmostEqual_generic([x, x, x], [y, y, y]).


    • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y}).


    • self.assertAlmostEqual_generic([(x,x)], [(y,y)]).


    Is there such a method or do I have to implement it myself?



    Clarifications:




    • assertAlmostEquals() has an optional parameter named places and the numbers are compared by computing the difference rounded to number of decimal places. By default places=7, hence self.assertAlmostEqual(0.5, 0.4) is False while self.assertAlmostEqual(0.12345678, 0.12345679) is True. My speculative assertAlmostEqual_generic() should have the same functionality.


    • Two lists are considered almost equal if they have almost equal numbers in exactly the same order. formally, for i in range(n): self.assertAlmostEqual(list1[i], list2[i]).


    • Similarly, two sets are considered almost equal if they can be converted to almost equal lists (by assigning an order to each set).


    • Similarly, two dictionaries are considered almost equal if the key set of each dictionary is almost equal to the key set of the other dictionary, and for each such almost equal key pair there's a corresponding almost equal value.


    • In general: I consider two collections almost equal if they're equal except for some corresponding floats which are just almost equal to each other. In other words, I would like to really compare objects but with a low (customized) precision when comparing floats along the way.











    share|improve this question


























      up vote
      49
      down vote

      favorite
      9









      up vote
      49
      down vote

      favorite
      9






      9





      The assertAlmostEqual(x, y) method in Python's unit testing framework tests whether x and y are approximately equal assuming they are floats.



      The problem with assertAlmostEqual() is that it only works on floats. I'm looking for a method like assertAlmostEqual() which works on lists of floats, sets of floats, dictionaries of floats, tuples of floats, lists of tuples of floats, sets of lists of floats, etc.



      For instance, let x = 0.1234567890, y = 0.1234567891. x and y are almost equal because they agree on each and every digit except for the last one. Therefore self.assertAlmostEqual(x, y) is True because assertAlmostEqual() works for floats.



      I'm looking for a more generic assertAlmostEquals() which also evaluates the following calls to True:





      • self.assertAlmostEqual_generic([x, x, x], [y, y, y]).


      • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y}).


      • self.assertAlmostEqual_generic([(x,x)], [(y,y)]).


      Is there such a method or do I have to implement it myself?



      Clarifications:




      • assertAlmostEquals() has an optional parameter named places and the numbers are compared by computing the difference rounded to number of decimal places. By default places=7, hence self.assertAlmostEqual(0.5, 0.4) is False while self.assertAlmostEqual(0.12345678, 0.12345679) is True. My speculative assertAlmostEqual_generic() should have the same functionality.


      • Two lists are considered almost equal if they have almost equal numbers in exactly the same order. formally, for i in range(n): self.assertAlmostEqual(list1[i], list2[i]).


      • Similarly, two sets are considered almost equal if they can be converted to almost equal lists (by assigning an order to each set).


      • Similarly, two dictionaries are considered almost equal if the key set of each dictionary is almost equal to the key set of the other dictionary, and for each such almost equal key pair there's a corresponding almost equal value.


      • In general: I consider two collections almost equal if they're equal except for some corresponding floats which are just almost equal to each other. In other words, I would like to really compare objects but with a low (customized) precision when comparing floats along the way.











      share|improve this question















      The assertAlmostEqual(x, y) method in Python's unit testing framework tests whether x and y are approximately equal assuming they are floats.



      The problem with assertAlmostEqual() is that it only works on floats. I'm looking for a method like assertAlmostEqual() which works on lists of floats, sets of floats, dictionaries of floats, tuples of floats, lists of tuples of floats, sets of lists of floats, etc.



      For instance, let x = 0.1234567890, y = 0.1234567891. x and y are almost equal because they agree on each and every digit except for the last one. Therefore self.assertAlmostEqual(x, y) is True because assertAlmostEqual() works for floats.



      I'm looking for a more generic assertAlmostEquals() which also evaluates the following calls to True:





      • self.assertAlmostEqual_generic([x, x, x], [y, y, y]).


      • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y}).


      • self.assertAlmostEqual_generic([(x,x)], [(y,y)]).


      Is there such a method or do I have to implement it myself?



      Clarifications:




      • assertAlmostEquals() has an optional parameter named places and the numbers are compared by computing the difference rounded to number of decimal places. By default places=7, hence self.assertAlmostEqual(0.5, 0.4) is False while self.assertAlmostEqual(0.12345678, 0.12345679) is True. My speculative assertAlmostEqual_generic() should have the same functionality.


      • Two lists are considered almost equal if they have almost equal numbers in exactly the same order. formally, for i in range(n): self.assertAlmostEqual(list1[i], list2[i]).


      • Similarly, two sets are considered almost equal if they can be converted to almost equal lists (by assigning an order to each set).


      • Similarly, two dictionaries are considered almost equal if the key set of each dictionary is almost equal to the key set of the other dictionary, and for each such almost equal key pair there's a corresponding almost equal value.


      • In general: I consider two collections almost equal if they're equal except for some corresponding floats which are just almost equal to each other. In other words, I would like to really compare objects but with a low (customized) precision when comparing floats along the way.








      python unit-testing






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Aug 27 '12 at 6:43

























      asked Aug 27 '12 at 5:46









      snakile

      23.6k48137207




      23.6k48137207
























          7 Answers
          7






          active

          oldest

          votes

















          up vote
          46
          down vote













          if you don't mind using NumPy (which comes with your Python(x,y)), you may want to look at the np.testing module which defines, among others, a assert_almost_equal function.



          The signature is np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)



          >>> x = 1.000001
          >>> y = 1.000002
          >>> np.testing.assert_almost_equal(x, y)
          AssertionError:
          Arrays are not almost equal to 7 decimals
          ACTUAL: 1.000001
          DESIRED: 1.000002
          >>> np.testing.assert_almost_equal(x, y, 5)
          >>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5)
          >>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5)





          share|improve this answer



















          • 1




            That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
            – snakile
            Aug 27 '12 at 10:31










          • Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
            – Pierre GM
            Aug 27 '12 at 10:46












          • Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
            – Pierre GM
            Aug 27 '12 at 10:47


















          up vote
          6
          down vote



          accepted










          Here's how I've implemented a generic is_almost_equal(first, second) function:



          First, duplicate the objects you need to compare (first and second), but don't make an exact copy: cut the insignificant decimal digits of any float you encounter inside the object.



          Now that you have copies of first and second for which the insignificant decimal digits are gone, just compare first and second using the == operator.



          Let's assume we have a cut_insignificant_digits_recursively(obj, places) function which duplicates obj but leaves only the places most significant decimal digits of each float in the original obj. Here's a working implementation of is_almost_equals(first, second, places):



          from insignificant_digit_cutter import cut_insignificant_digits_recursively

          def is_almost_equal(first, second, places):
          '''returns True if first and second equal.
          returns true if first and second aren't equal but have exactly the same
          structure and values except for a bunch of floats which are just almost
          equal (floats are almost equal if they're equal when we consider only the
          [places] most significant digits of each).'''
          if first == second: return True
          cut_first = cut_insignificant_digits_recursively(first, places)
          cut_second = cut_insignificant_digits_recursively(second, places)
          return cut_first == cut_second


          And here's a working implementation of cut_insignificant_digits_recursively(obj, places):



          def cut_insignificant_digits(number, places):
          '''cut the least significant decimal digits of a number,
          leave only [places] decimal digits'''
          if type(number) != float: return number
          number_as_str = str(number)
          end_of_number = number_as_str.find('.')+places+1
          if end_of_number > len(number_as_str): return number
          return float(number_as_str[:end_of_number])

          def cut_insignificant_digits_lazy(iterable, places):
          for obj in iterable:
          yield cut_insignificant_digits_recursively(obj, places)

          def cut_insignificant_digits_recursively(obj, places):
          '''return a copy of obj except that every float loses its least significant
          decimal digits remaining only [places] decimal digits'''
          t = type(obj)
          if t == float: return cut_insignificant_digits(obj, places)
          if t in (list, tuple, set):
          return t(cut_insignificant_digits_lazy(obj, places))
          if t == dict:
          return {cut_insignificant_digits_recursively(key, places):
          cut_insignificant_digits_recursively(val, places)
          for key,val in obj.items()}
          return obj


          The code and its unit tests are available here: https://github.com/snakile/approximate_comparator. I welcome any improvement and bug fix.






          share|improve this answer























          • Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
            – Pierre GM
            Aug 27 '12 at 15:04








          • 1




            This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
            – Rodney Richardson
            Dec 17 '14 at 17:23








          • 1




            @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
            – Jérôme
            Feb 27 '17 at 14:32








          • 1




            @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
            – Jérôme
            Feb 27 '17 at 14:40








          • 2




            @Jérôme, thanks for the comment. I've just added an MIT license.
            – snakile
            Feb 27 '17 at 14:55


















          up vote
          6
          down vote













          As of python 3.5 you may compare using



          math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)


          As described in pep-0485.
          The implementation should be equivalent to



          abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )





          share|improve this answer






























            up vote
            4
            down vote













            There is no such method, you'd have to do it yourself.



            For lists and tuples the definition is obvious, but note that the other cases you mention aren't obvious, so it's no wonder such a function isn't provided. For instance, is {1.00001: 1.00002} almost equal to {1.00002: 1.00001}? Handling such cases requires making a choice about whether closeness depends on keys or values or both. For sets you are unlikely to find a meaningful definition, since sets are unordered, so there is no notion of "corresponding" elements.






            share|improve this answer























            • {1.00001: 1.00002] typo?
              – Samy Vilar
              Aug 27 '12 at 6:12










            • Fixed it, thanks.
              – BrenBarn
              Aug 27 '12 at 6:31










            • BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
              – snakile
              Aug 27 '12 at 6:49


















            up vote
            4
            down vote













            If you don't mind using the numpy package then numpy.testing has the assert_array_almost_equal method.



            This works for array_like objects, so it is fine for arrays, lists and tuples of floats, but does it not work for sets and dictionaries.



            The documentation is here.






            share|improve this answer




























              up vote
              0
              down vote













              You may have to implement it yourself, while its true that list and sets can be iterated the same way, dictionaries are a different story, you iterate their keys not values, and the third example seems a bit ambiguous to me, do you mean to compare each value within the set, or each value from each set.



              heres a simple code snippet.



              def almost_equal(value_1, value_2, accuracy = 10**-8):
              return abs(value_1 - value_2) < accuracy

              x = [1,2,3,4]
              y = [1,2,4,5]
              assert all(almost_equal(*values) for values in zip(x, y))





              share|improve this answer























              • Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                – snakile
                Aug 27 '12 at 7:04


















              up vote
              0
              down vote













              An alternative approach is to convert your data into a comparable form by e.g turning each float into a string with fixed precision.



              def comparable(data):
              """Converts `data` to a comparable structure by converting any floats to a string with fixed precision."""
              if isinstance(data, (int, str)):
              return data
              if isinstance(data, float):
              return '{:.4f}'.format(data)
              if isinstance(data, list):
              return [comparable(el) for el in data]
              if isinstance(data, tuple):
              return tuple([comparable(el) for el in data])
              if isinstance(data, dict):
              return {k: comparable(v) for k, v in data.items()}


              Then you can:



              self.assertEquals(comparable(value1), comparable(value2))





              share|improve this answer





















                Your Answer






                StackExchange.ifUsing("editor", function () {
                StackExchange.using("externalEditor", function () {
                StackExchange.using("snippets", function () {
                StackExchange.snippets.init();
                });
                });
                }, "code-snippets");

                StackExchange.ready(function() {
                var channelOptions = {
                tags: "".split(" "),
                id: "1"
                };
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function() {
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled) {
                StackExchange.using("snippets", function() {
                createEditor();
                });
                }
                else {
                createEditor();
                }
                });

                function createEditor() {
                StackExchange.prepareEditor({
                heartbeatType: 'answer',
                convertImagesToLinks: true,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: 10,
                bindNavPrevention: true,
                postfix: "",
                imageUploader: {
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                },
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                });


                }
                });














                 

                draft saved


                draft discarded


















                StackExchange.ready(
                function () {
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f12136762%2fassertalmostequal-in-python-unit-test-for-collections-of-floats%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                7 Answers
                7






                active

                oldest

                votes








                7 Answers
                7






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes








                up vote
                46
                down vote













                if you don't mind using NumPy (which comes with your Python(x,y)), you may want to look at the np.testing module which defines, among others, a assert_almost_equal function.



                The signature is np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)



                >>> x = 1.000001
                >>> y = 1.000002
                >>> np.testing.assert_almost_equal(x, y)
                AssertionError:
                Arrays are not almost equal to 7 decimals
                ACTUAL: 1.000001
                DESIRED: 1.000002
                >>> np.testing.assert_almost_equal(x, y, 5)
                >>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5)
                >>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5)





                share|improve this answer



















                • 1




                  That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
                  – snakile
                  Aug 27 '12 at 10:31










                • Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
                  – Pierre GM
                  Aug 27 '12 at 10:46












                • Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
                  – Pierre GM
                  Aug 27 '12 at 10:47















                up vote
                46
                down vote













                if you don't mind using NumPy (which comes with your Python(x,y)), you may want to look at the np.testing module which defines, among others, a assert_almost_equal function.



                The signature is np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)



                >>> x = 1.000001
                >>> y = 1.000002
                >>> np.testing.assert_almost_equal(x, y)
                AssertionError:
                Arrays are not almost equal to 7 decimals
                ACTUAL: 1.000001
                DESIRED: 1.000002
                >>> np.testing.assert_almost_equal(x, y, 5)
                >>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5)
                >>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5)





                share|improve this answer



















                • 1




                  That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
                  – snakile
                  Aug 27 '12 at 10:31










                • Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
                  – Pierre GM
                  Aug 27 '12 at 10:46












                • Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
                  – Pierre GM
                  Aug 27 '12 at 10:47













                up vote
                46
                down vote










                up vote
                46
                down vote









                if you don't mind using NumPy (which comes with your Python(x,y)), you may want to look at the np.testing module which defines, among others, a assert_almost_equal function.



                The signature is np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)



                >>> x = 1.000001
                >>> y = 1.000002
                >>> np.testing.assert_almost_equal(x, y)
                AssertionError:
                Arrays are not almost equal to 7 decimals
                ACTUAL: 1.000001
                DESIRED: 1.000002
                >>> np.testing.assert_almost_equal(x, y, 5)
                >>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5)
                >>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5)





                share|improve this answer














                if you don't mind using NumPy (which comes with your Python(x,y)), you may want to look at the np.testing module which defines, among others, a assert_almost_equal function.



                The signature is np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)



                >>> x = 1.000001
                >>> y = 1.000002
                >>> np.testing.assert_almost_equal(x, y)
                AssertionError:
                Arrays are not almost equal to 7 decimals
                ACTUAL: 1.000001
                DESIRED: 1.000002
                >>> np.testing.assert_almost_equal(x, y, 5)
                >>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5)
                >>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5)






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Jun 19 '13 at 15:25

























                answered Aug 27 '12 at 10:04









                Pierre GM

                13.6k33859




                13.6k33859








                • 1




                  That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
                  – snakile
                  Aug 27 '12 at 10:31










                • Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
                  – Pierre GM
                  Aug 27 '12 at 10:46












                • Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
                  – Pierre GM
                  Aug 27 '12 at 10:47














                • 1




                  That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
                  – snakile
                  Aug 27 '12 at 10:31










                • Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
                  – Pierre GM
                  Aug 27 '12 at 10:46












                • Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
                  – Pierre GM
                  Aug 27 '12 at 10:47








                1




                1




                That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
                – snakile
                Aug 27 '12 at 10:31




                That's close, but numpy.testing almost-equal methods work only on numbers, arrays, tuples and lists. They do not work on dictionaries, sets and collections of collections.
                – snakile
                Aug 27 '12 at 10:31












                Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
                – Pierre GM
                Aug 27 '12 at 10:46






                Indeed, but that's a start. Besides, you have access to the source code that you can modify to allow the comparison of dictionaries, collections and so forth. np.testing.assert_equal does recognize dictionaries as arguments, for example (even if the comparison is done by a == which won't work for you).
                – Pierre GM
                Aug 27 '12 at 10:46














                Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
                – Pierre GM
                Aug 27 '12 at 10:47




                Of course, you'll still run into troubles when comparing sets, as @BrenBarn mentioned.
                – Pierre GM
                Aug 27 '12 at 10:47












                up vote
                6
                down vote



                accepted










                Here's how I've implemented a generic is_almost_equal(first, second) function:



                First, duplicate the objects you need to compare (first and second), but don't make an exact copy: cut the insignificant decimal digits of any float you encounter inside the object.



                Now that you have copies of first and second for which the insignificant decimal digits are gone, just compare first and second using the == operator.



                Let's assume we have a cut_insignificant_digits_recursively(obj, places) function which duplicates obj but leaves only the places most significant decimal digits of each float in the original obj. Here's a working implementation of is_almost_equals(first, second, places):



                from insignificant_digit_cutter import cut_insignificant_digits_recursively

                def is_almost_equal(first, second, places):
                '''returns True if first and second equal.
                returns true if first and second aren't equal but have exactly the same
                structure and values except for a bunch of floats which are just almost
                equal (floats are almost equal if they're equal when we consider only the
                [places] most significant digits of each).'''
                if first == second: return True
                cut_first = cut_insignificant_digits_recursively(first, places)
                cut_second = cut_insignificant_digits_recursively(second, places)
                return cut_first == cut_second


                And here's a working implementation of cut_insignificant_digits_recursively(obj, places):



                def cut_insignificant_digits(number, places):
                '''cut the least significant decimal digits of a number,
                leave only [places] decimal digits'''
                if type(number) != float: return number
                number_as_str = str(number)
                end_of_number = number_as_str.find('.')+places+1
                if end_of_number > len(number_as_str): return number
                return float(number_as_str[:end_of_number])

                def cut_insignificant_digits_lazy(iterable, places):
                for obj in iterable:
                yield cut_insignificant_digits_recursively(obj, places)

                def cut_insignificant_digits_recursively(obj, places):
                '''return a copy of obj except that every float loses its least significant
                decimal digits remaining only [places] decimal digits'''
                t = type(obj)
                if t == float: return cut_insignificant_digits(obj, places)
                if t in (list, tuple, set):
                return t(cut_insignificant_digits_lazy(obj, places))
                if t == dict:
                return {cut_insignificant_digits_recursively(key, places):
                cut_insignificant_digits_recursively(val, places)
                for key,val in obj.items()}
                return obj


                The code and its unit tests are available here: https://github.com/snakile/approximate_comparator. I welcome any improvement and bug fix.






                share|improve this answer























                • Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
                  – Pierre GM
                  Aug 27 '12 at 15:04








                • 1




                  This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
                  – Rodney Richardson
                  Dec 17 '14 at 17:23








                • 1




                  @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
                  – Jérôme
                  Feb 27 '17 at 14:32








                • 1




                  @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
                  – Jérôme
                  Feb 27 '17 at 14:40








                • 2




                  @Jérôme, thanks for the comment. I've just added an MIT license.
                  – snakile
                  Feb 27 '17 at 14:55















                up vote
                6
                down vote



                accepted










                Here's how I've implemented a generic is_almost_equal(first, second) function:



                First, duplicate the objects you need to compare (first and second), but don't make an exact copy: cut the insignificant decimal digits of any float you encounter inside the object.



                Now that you have copies of first and second for which the insignificant decimal digits are gone, just compare first and second using the == operator.



                Let's assume we have a cut_insignificant_digits_recursively(obj, places) function which duplicates obj but leaves only the places most significant decimal digits of each float in the original obj. Here's a working implementation of is_almost_equals(first, second, places):



                from insignificant_digit_cutter import cut_insignificant_digits_recursively

                def is_almost_equal(first, second, places):
                '''returns True if first and second equal.
                returns true if first and second aren't equal but have exactly the same
                structure and values except for a bunch of floats which are just almost
                equal (floats are almost equal if they're equal when we consider only the
                [places] most significant digits of each).'''
                if first == second: return True
                cut_first = cut_insignificant_digits_recursively(first, places)
                cut_second = cut_insignificant_digits_recursively(second, places)
                return cut_first == cut_second


                And here's a working implementation of cut_insignificant_digits_recursively(obj, places):



                def cut_insignificant_digits(number, places):
                '''cut the least significant decimal digits of a number,
                leave only [places] decimal digits'''
                if type(number) != float: return number
                number_as_str = str(number)
                end_of_number = number_as_str.find('.')+places+1
                if end_of_number > len(number_as_str): return number
                return float(number_as_str[:end_of_number])

                def cut_insignificant_digits_lazy(iterable, places):
                for obj in iterable:
                yield cut_insignificant_digits_recursively(obj, places)

                def cut_insignificant_digits_recursively(obj, places):
                '''return a copy of obj except that every float loses its least significant
                decimal digits remaining only [places] decimal digits'''
                t = type(obj)
                if t == float: return cut_insignificant_digits(obj, places)
                if t in (list, tuple, set):
                return t(cut_insignificant_digits_lazy(obj, places))
                if t == dict:
                return {cut_insignificant_digits_recursively(key, places):
                cut_insignificant_digits_recursively(val, places)
                for key,val in obj.items()}
                return obj


                The code and its unit tests are available here: https://github.com/snakile/approximate_comparator. I welcome any improvement and bug fix.






                share|improve this answer























                • Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
                  – Pierre GM
                  Aug 27 '12 at 15:04








                • 1




                  This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
                  – Rodney Richardson
                  Dec 17 '14 at 17:23








                • 1




                  @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
                  – Jérôme
                  Feb 27 '17 at 14:32








                • 1




                  @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
                  – Jérôme
                  Feb 27 '17 at 14:40








                • 2




                  @Jérôme, thanks for the comment. I've just added an MIT license.
                  – snakile
                  Feb 27 '17 at 14:55













                up vote
                6
                down vote



                accepted







                up vote
                6
                down vote



                accepted






                Here's how I've implemented a generic is_almost_equal(first, second) function:



                First, duplicate the objects you need to compare (first and second), but don't make an exact copy: cut the insignificant decimal digits of any float you encounter inside the object.



                Now that you have copies of first and second for which the insignificant decimal digits are gone, just compare first and second using the == operator.



                Let's assume we have a cut_insignificant_digits_recursively(obj, places) function which duplicates obj but leaves only the places most significant decimal digits of each float in the original obj. Here's a working implementation of is_almost_equals(first, second, places):



                from insignificant_digit_cutter import cut_insignificant_digits_recursively

                def is_almost_equal(first, second, places):
                '''returns True if first and second equal.
                returns true if first and second aren't equal but have exactly the same
                structure and values except for a bunch of floats which are just almost
                equal (floats are almost equal if they're equal when we consider only the
                [places] most significant digits of each).'''
                if first == second: return True
                cut_first = cut_insignificant_digits_recursively(first, places)
                cut_second = cut_insignificant_digits_recursively(second, places)
                return cut_first == cut_second


                And here's a working implementation of cut_insignificant_digits_recursively(obj, places):



                def cut_insignificant_digits(number, places):
                '''cut the least significant decimal digits of a number,
                leave only [places] decimal digits'''
                if type(number) != float: return number
                number_as_str = str(number)
                end_of_number = number_as_str.find('.')+places+1
                if end_of_number > len(number_as_str): return number
                return float(number_as_str[:end_of_number])

                def cut_insignificant_digits_lazy(iterable, places):
                for obj in iterable:
                yield cut_insignificant_digits_recursively(obj, places)

                def cut_insignificant_digits_recursively(obj, places):
                '''return a copy of obj except that every float loses its least significant
                decimal digits remaining only [places] decimal digits'''
                t = type(obj)
                if t == float: return cut_insignificant_digits(obj, places)
                if t in (list, tuple, set):
                return t(cut_insignificant_digits_lazy(obj, places))
                if t == dict:
                return {cut_insignificant_digits_recursively(key, places):
                cut_insignificant_digits_recursively(val, places)
                for key,val in obj.items()}
                return obj


                The code and its unit tests are available here: https://github.com/snakile/approximate_comparator. I welcome any improvement and bug fix.






                share|improve this answer














                Here's how I've implemented a generic is_almost_equal(first, second) function:



                First, duplicate the objects you need to compare (first and second), but don't make an exact copy: cut the insignificant decimal digits of any float you encounter inside the object.



                Now that you have copies of first and second for which the insignificant decimal digits are gone, just compare first and second using the == operator.



                Let's assume we have a cut_insignificant_digits_recursively(obj, places) function which duplicates obj but leaves only the places most significant decimal digits of each float in the original obj. Here's a working implementation of is_almost_equals(first, second, places):



                from insignificant_digit_cutter import cut_insignificant_digits_recursively

                def is_almost_equal(first, second, places):
                '''returns True if first and second equal.
                returns true if first and second aren't equal but have exactly the same
                structure and values except for a bunch of floats which are just almost
                equal (floats are almost equal if they're equal when we consider only the
                [places] most significant digits of each).'''
                if first == second: return True
                cut_first = cut_insignificant_digits_recursively(first, places)
                cut_second = cut_insignificant_digits_recursively(second, places)
                return cut_first == cut_second


                And here's a working implementation of cut_insignificant_digits_recursively(obj, places):



                def cut_insignificant_digits(number, places):
                '''cut the least significant decimal digits of a number,
                leave only [places] decimal digits'''
                if type(number) != float: return number
                number_as_str = str(number)
                end_of_number = number_as_str.find('.')+places+1
                if end_of_number > len(number_as_str): return number
                return float(number_as_str[:end_of_number])

                def cut_insignificant_digits_lazy(iterable, places):
                for obj in iterable:
                yield cut_insignificant_digits_recursively(obj, places)

                def cut_insignificant_digits_recursively(obj, places):
                '''return a copy of obj except that every float loses its least significant
                decimal digits remaining only [places] decimal digits'''
                t = type(obj)
                if t == float: return cut_insignificant_digits(obj, places)
                if t in (list, tuple, set):
                return t(cut_insignificant_digits_lazy(obj, places))
                if t == dict:
                return {cut_insignificant_digits_recursively(key, places):
                cut_insignificant_digits_recursively(val, places)
                for key,val in obj.items()}
                return obj


                The code and its unit tests are available here: https://github.com/snakile/approximate_comparator. I welcome any improvement and bug fix.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Aug 27 '12 at 22:51

























                answered Aug 27 '12 at 13:37









                snakile

                23.6k48137207




                23.6k48137207












                • Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
                  – Pierre GM
                  Aug 27 '12 at 15:04








                • 1




                  This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
                  – Rodney Richardson
                  Dec 17 '14 at 17:23








                • 1




                  @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
                  – Jérôme
                  Feb 27 '17 at 14:32








                • 1




                  @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
                  – Jérôme
                  Feb 27 '17 at 14:40








                • 2




                  @Jérôme, thanks for the comment. I've just added an MIT license.
                  – snakile
                  Feb 27 '17 at 14:55


















                • Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
                  – Pierre GM
                  Aug 27 '12 at 15:04








                • 1




                  This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
                  – Rodney Richardson
                  Dec 17 '14 at 17:23








                • 1




                  @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
                  – Jérôme
                  Feb 27 '17 at 14:32








                • 1




                  @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
                  – Jérôme
                  Feb 27 '17 at 14:40








                • 2




                  @Jérôme, thanks for the comment. I've just added an MIT license.
                  – snakile
                  Feb 27 '17 at 14:55
















                Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
                – Pierre GM
                Aug 27 '12 at 15:04






                Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Like fmt="{{0:{0}f}}".format(decimals), and use this fmt format to "stringify" your floats?
                – Pierre GM
                Aug 27 '12 at 15:04






                1




                1




                This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
                – Rodney Richardson
                Dec 17 '14 at 17:23






                This looks nice, but a small point: places gives the number of decimal places, not the number of significant figures. For example, comparing 1024.123 and 1023.999 to 3 significant should return equal, but to 3 decimal places they're not.
                – Rodney Richardson
                Dec 17 '14 at 17:23






                1




                1




                @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
                – Jérôme
                Feb 27 '17 at 14:32






                @pir, the license is indeed undefined. See snalile's answer in this issue in which he says he doesn't have time to choose/add a license, but grants use/modification permissions. Thanks for sharing this, BTW.
                – Jérôme
                Feb 27 '17 at 14:32






                1




                1




                @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
                – Jérôme
                Feb 27 '17 at 14:40






                @RodneyRichardson, yes this is decimal places, like in assertAlmostEqual: "Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits."
                – Jérôme
                Feb 27 '17 at 14:40






                2




                2




                @Jérôme, thanks for the comment. I've just added an MIT license.
                – snakile
                Feb 27 '17 at 14:55




                @Jérôme, thanks for the comment. I've just added an MIT license.
                – snakile
                Feb 27 '17 at 14:55










                up vote
                6
                down vote













                As of python 3.5 you may compare using



                math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)


                As described in pep-0485.
                The implementation should be equivalent to



                abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )





                share|improve this answer



























                  up vote
                  6
                  down vote













                  As of python 3.5 you may compare using



                  math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)


                  As described in pep-0485.
                  The implementation should be equivalent to



                  abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )





                  share|improve this answer

























                    up vote
                    6
                    down vote










                    up vote
                    6
                    down vote









                    As of python 3.5 you may compare using



                    math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)


                    As described in pep-0485.
                    The implementation should be equivalent to



                    abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )





                    share|improve this answer














                    As of python 3.5 you may compare using



                    math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)


                    As described in pep-0485.
                    The implementation should be equivalent to



                    abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Nov 16 '16 at 1:43









                    Elias Zamaria

                    40.7k2587127




                    40.7k2587127










                    answered Jul 1 '16 at 18:33









                    Maximiliano Ramirez Mellado

                    7113




                    7113






















                        up vote
                        4
                        down vote













                        There is no such method, you'd have to do it yourself.



                        For lists and tuples the definition is obvious, but note that the other cases you mention aren't obvious, so it's no wonder such a function isn't provided. For instance, is {1.00001: 1.00002} almost equal to {1.00002: 1.00001}? Handling such cases requires making a choice about whether closeness depends on keys or values or both. For sets you are unlikely to find a meaningful definition, since sets are unordered, so there is no notion of "corresponding" elements.






                        share|improve this answer























                        • {1.00001: 1.00002] typo?
                          – Samy Vilar
                          Aug 27 '12 at 6:12










                        • Fixed it, thanks.
                          – BrenBarn
                          Aug 27 '12 at 6:31










                        • BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
                          – snakile
                          Aug 27 '12 at 6:49















                        up vote
                        4
                        down vote













                        There is no such method, you'd have to do it yourself.



                        For lists and tuples the definition is obvious, but note that the other cases you mention aren't obvious, so it's no wonder such a function isn't provided. For instance, is {1.00001: 1.00002} almost equal to {1.00002: 1.00001}? Handling such cases requires making a choice about whether closeness depends on keys or values or both. For sets you are unlikely to find a meaningful definition, since sets are unordered, so there is no notion of "corresponding" elements.






                        share|improve this answer























                        • {1.00001: 1.00002] typo?
                          – Samy Vilar
                          Aug 27 '12 at 6:12










                        • Fixed it, thanks.
                          – BrenBarn
                          Aug 27 '12 at 6:31










                        • BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
                          – snakile
                          Aug 27 '12 at 6:49













                        up vote
                        4
                        down vote










                        up vote
                        4
                        down vote









                        There is no such method, you'd have to do it yourself.



                        For lists and tuples the definition is obvious, but note that the other cases you mention aren't obvious, so it's no wonder such a function isn't provided. For instance, is {1.00001: 1.00002} almost equal to {1.00002: 1.00001}? Handling such cases requires making a choice about whether closeness depends on keys or values or both. For sets you are unlikely to find a meaningful definition, since sets are unordered, so there is no notion of "corresponding" elements.






                        share|improve this answer














                        There is no such method, you'd have to do it yourself.



                        For lists and tuples the definition is obvious, but note that the other cases you mention aren't obvious, so it's no wonder such a function isn't provided. For instance, is {1.00001: 1.00002} almost equal to {1.00002: 1.00001}? Handling such cases requires making a choice about whether closeness depends on keys or values or both. For sets you are unlikely to find a meaningful definition, since sets are unordered, so there is no notion of "corresponding" elements.







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Aug 27 '12 at 6:29

























                        answered Aug 27 '12 at 6:06









                        BrenBarn

                        157k20273280




                        157k20273280












                        • {1.00001: 1.00002] typo?
                          – Samy Vilar
                          Aug 27 '12 at 6:12










                        • Fixed it, thanks.
                          – BrenBarn
                          Aug 27 '12 at 6:31










                        • BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
                          – snakile
                          Aug 27 '12 at 6:49


















                        • {1.00001: 1.00002] typo?
                          – Samy Vilar
                          Aug 27 '12 at 6:12










                        • Fixed it, thanks.
                          – BrenBarn
                          Aug 27 '12 at 6:31










                        • BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
                          – snakile
                          Aug 27 '12 at 6:49
















                        {1.00001: 1.00002] typo?
                        – Samy Vilar
                        Aug 27 '12 at 6:12




                        {1.00001: 1.00002] typo?
                        – Samy Vilar
                        Aug 27 '12 at 6:12












                        Fixed it, thanks.
                        – BrenBarn
                        Aug 27 '12 at 6:31




                        Fixed it, thanks.
                        – BrenBarn
                        Aug 27 '12 at 6:31












                        BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
                        – snakile
                        Aug 27 '12 at 6:49




                        BrenBarn: I've added clarifications to the question. The answer to your question is that {1.00001: 1.00002} almost equals {1.00002: 1.00001} if and only if 1.00001 almost equals 1.00002. By default they do not almost equal (because the default precision is 7 decimal places) but for a small enough value for places they do almost equal.
                        – snakile
                        Aug 27 '12 at 6:49










                        up vote
                        4
                        down vote













                        If you don't mind using the numpy package then numpy.testing has the assert_array_almost_equal method.



                        This works for array_like objects, so it is fine for arrays, lists and tuples of floats, but does it not work for sets and dictionaries.



                        The documentation is here.






                        share|improve this answer

























                          up vote
                          4
                          down vote













                          If you don't mind using the numpy package then numpy.testing has the assert_array_almost_equal method.



                          This works for array_like objects, so it is fine for arrays, lists and tuples of floats, but does it not work for sets and dictionaries.



                          The documentation is here.






                          share|improve this answer























                            up vote
                            4
                            down vote










                            up vote
                            4
                            down vote









                            If you don't mind using the numpy package then numpy.testing has the assert_array_almost_equal method.



                            This works for array_like objects, so it is fine for arrays, lists and tuples of floats, but does it not work for sets and dictionaries.



                            The documentation is here.






                            share|improve this answer












                            If you don't mind using the numpy package then numpy.testing has the assert_array_almost_equal method.



                            This works for array_like objects, so it is fine for arrays, lists and tuples of floats, but does it not work for sets and dictionaries.



                            The documentation is here.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Feb 11 '14 at 12:00









                            DJCowley

                            5314




                            5314






















                                up vote
                                0
                                down vote













                                You may have to implement it yourself, while its true that list and sets can be iterated the same way, dictionaries are a different story, you iterate their keys not values, and the third example seems a bit ambiguous to me, do you mean to compare each value within the set, or each value from each set.



                                heres a simple code snippet.



                                def almost_equal(value_1, value_2, accuracy = 10**-8):
                                return abs(value_1 - value_2) < accuracy

                                x = [1,2,3,4]
                                y = [1,2,4,5]
                                assert all(almost_equal(*values) for values in zip(x, y))





                                share|improve this answer























                                • Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                                  – snakile
                                  Aug 27 '12 at 7:04















                                up vote
                                0
                                down vote













                                You may have to implement it yourself, while its true that list and sets can be iterated the same way, dictionaries are a different story, you iterate their keys not values, and the third example seems a bit ambiguous to me, do you mean to compare each value within the set, or each value from each set.



                                heres a simple code snippet.



                                def almost_equal(value_1, value_2, accuracy = 10**-8):
                                return abs(value_1 - value_2) < accuracy

                                x = [1,2,3,4]
                                y = [1,2,4,5]
                                assert all(almost_equal(*values) for values in zip(x, y))





                                share|improve this answer























                                • Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                                  – snakile
                                  Aug 27 '12 at 7:04













                                up vote
                                0
                                down vote










                                up vote
                                0
                                down vote









                                You may have to implement it yourself, while its true that list and sets can be iterated the same way, dictionaries are a different story, you iterate their keys not values, and the third example seems a bit ambiguous to me, do you mean to compare each value within the set, or each value from each set.



                                heres a simple code snippet.



                                def almost_equal(value_1, value_2, accuracy = 10**-8):
                                return abs(value_1 - value_2) < accuracy

                                x = [1,2,3,4]
                                y = [1,2,4,5]
                                assert all(almost_equal(*values) for values in zip(x, y))





                                share|improve this answer














                                You may have to implement it yourself, while its true that list and sets can be iterated the same way, dictionaries are a different story, you iterate their keys not values, and the third example seems a bit ambiguous to me, do you mean to compare each value within the set, or each value from each set.



                                heres a simple code snippet.



                                def almost_equal(value_1, value_2, accuracy = 10**-8):
                                return abs(value_1 - value_2) < accuracy

                                x = [1,2,3,4]
                                y = [1,2,4,5]
                                assert all(almost_equal(*values) for values in zip(x, y))






                                share|improve this answer














                                share|improve this answer



                                share|improve this answer








                                edited Aug 27 '12 at 6:55

























                                answered Aug 27 '12 at 6:08









                                Samy Vilar

                                7,56012231




                                7,56012231












                                • Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                                  – snakile
                                  Aug 27 '12 at 7:04


















                                • Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                                  – snakile
                                  Aug 27 '12 at 7:04
















                                Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                                – snakile
                                Aug 27 '12 at 7:04




                                Thanks, the solution is correct for lists and tuples but not for other types of collections (or nested collections). See the clarifications I've added to the question. I hope my intention is clear now. Two sets are almost equal if they would have been considered equal in a world where numbers aren't measured very precisely.
                                – snakile
                                Aug 27 '12 at 7:04










                                up vote
                                0
                                down vote













                                An alternative approach is to convert your data into a comparable form by e.g turning each float into a string with fixed precision.



                                def comparable(data):
                                """Converts `data` to a comparable structure by converting any floats to a string with fixed precision."""
                                if isinstance(data, (int, str)):
                                return data
                                if isinstance(data, float):
                                return '{:.4f}'.format(data)
                                if isinstance(data, list):
                                return [comparable(el) for el in data]
                                if isinstance(data, tuple):
                                return tuple([comparable(el) for el in data])
                                if isinstance(data, dict):
                                return {k: comparable(v) for k, v in data.items()}


                                Then you can:



                                self.assertEquals(comparable(value1), comparable(value2))





                                share|improve this answer

























                                  up vote
                                  0
                                  down vote













                                  An alternative approach is to convert your data into a comparable form by e.g turning each float into a string with fixed precision.



                                  def comparable(data):
                                  """Converts `data` to a comparable structure by converting any floats to a string with fixed precision."""
                                  if isinstance(data, (int, str)):
                                  return data
                                  if isinstance(data, float):
                                  return '{:.4f}'.format(data)
                                  if isinstance(data, list):
                                  return [comparable(el) for el in data]
                                  if isinstance(data, tuple):
                                  return tuple([comparable(el) for el in data])
                                  if isinstance(data, dict):
                                  return {k: comparable(v) for k, v in data.items()}


                                  Then you can:



                                  self.assertEquals(comparable(value1), comparable(value2))





                                  share|improve this answer























                                    up vote
                                    0
                                    down vote










                                    up vote
                                    0
                                    down vote









                                    An alternative approach is to convert your data into a comparable form by e.g turning each float into a string with fixed precision.



                                    def comparable(data):
                                    """Converts `data` to a comparable structure by converting any floats to a string with fixed precision."""
                                    if isinstance(data, (int, str)):
                                    return data
                                    if isinstance(data, float):
                                    return '{:.4f}'.format(data)
                                    if isinstance(data, list):
                                    return [comparable(el) for el in data]
                                    if isinstance(data, tuple):
                                    return tuple([comparable(el) for el in data])
                                    if isinstance(data, dict):
                                    return {k: comparable(v) for k, v in data.items()}


                                    Then you can:



                                    self.assertEquals(comparable(value1), comparable(value2))





                                    share|improve this answer












                                    An alternative approach is to convert your data into a comparable form by e.g turning each float into a string with fixed precision.



                                    def comparable(data):
                                    """Converts `data` to a comparable structure by converting any floats to a string with fixed precision."""
                                    if isinstance(data, (int, str)):
                                    return data
                                    if isinstance(data, float):
                                    return '{:.4f}'.format(data)
                                    if isinstance(data, list):
                                    return [comparable(el) for el in data]
                                    if isinstance(data, tuple):
                                    return tuple([comparable(el) for el in data])
                                    if isinstance(data, dict):
                                    return {k: comparable(v) for k, v in data.items()}


                                    Then you can:



                                    self.assertEquals(comparable(value1), comparable(value2))






                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered Aug 23 at 15:51









                                    Karl Rosaen

                                    3,11012128




                                    3,11012128






























                                         

                                        draft saved


                                        draft discarded



















































                                         


                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function () {
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f12136762%2fassertalmostequal-in-python-unit-test-for-collections-of-floats%23new-answer', 'question_page');
                                        }
                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown







                                        Popular posts from this blog

                                        Schultheiß

                                        Liste der Kulturdenkmale in Wilsdruff

                                        Android Play Services Check