assertAlmostEqual in Python unit-test for collections of floats
up vote
49
down vote
favorite
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 namedplaces
and the numbers are compared by computing the difference rounded to number of decimalplaces
. By defaultplaces=7
, henceself.assertAlmostEqual(0.5, 0.4)
is False whileself.assertAlmostEqual(0.12345678, 0.12345679)
is True. My speculativeassertAlmostEqual_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
add a comment |
up vote
49
down vote
favorite
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 namedplaces
and the numbers are compared by computing the difference rounded to number of decimalplaces
. By defaultplaces=7
, henceself.assertAlmostEqual(0.5, 0.4)
is False whileself.assertAlmostEqual(0.12345678, 0.12345679)
is True. My speculativeassertAlmostEqual_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
add a comment |
up vote
49
down vote
favorite
up vote
49
down vote
favorite
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 namedplaces
and the numbers are compared by computing the difference rounded to number of decimalplaces
. By defaultplaces=7
, henceself.assertAlmostEqual(0.5, 0.4)
is False whileself.assertAlmostEqual(0.12345678, 0.12345679)
is True. My speculativeassertAlmostEqual_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
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 namedplaces
and the numbers are compared by computing the difference rounded to number of decimalplaces
. By defaultplaces=7
, henceself.assertAlmostEqual(0.5, 0.4)
is False whileself.assertAlmostEqual(0.12345678, 0.12345679)
is True. My speculativeassertAlmostEqual_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
python unit-testing
edited Aug 27 '12 at 6:43
asked Aug 27 '12 at 5:46
snakile
23.6k48137207
23.6k48137207
add a comment |
add a comment |
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)
1
That's close, butnumpy.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
add a comment |
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.
Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Likefmt="{{0:{0}f}}".format(decimals)
, and use thisfmt
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, comparing1024.123
and1023.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
|
show 9 more comments
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 )
add a comment |
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.
{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 forplaces
they do almost equal.
– snakile
Aug 27 '12 at 6:49
add a comment |
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.
add a comment |
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))
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
add a comment |
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))
add a comment |
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)
1
That's close, butnumpy.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
add a comment |
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)
1
That's close, butnumpy.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
add a comment |
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)
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)
edited Jun 19 '13 at 15:25
answered Aug 27 '12 at 10:04
Pierre GM
13.6k33859
13.6k33859
1
That's close, butnumpy.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
add a comment |
1
That's close, butnumpy.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
add a comment |
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.
Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Likefmt="{{0:{0}f}}".format(decimals)
, and use thisfmt
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, comparing1024.123
and1023.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
|
show 9 more comments
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.
Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Likefmt="{{0:{0}f}}".format(decimals)
, and use thisfmt
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, comparing1024.123
and1023.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
|
show 9 more comments
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.
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.
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? Likefmt="{{0:{0}f}}".format(decimals)
, and use thisfmt
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, comparing1024.123
and1023.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
|
show 9 more comments
Instead of comparing floats, you're comparing strings? OK... But then, wouldn't it be easier to set a common format? Likefmt="{{0:{0}f}}".format(decimals)
, and use thisfmt
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, comparing1024.123
and1023.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
|
show 9 more comments
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 )
add a comment |
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 )
add a comment |
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 )
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 )
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
add a comment |
add a comment |
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.
{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 forplaces
they do almost equal.
– snakile
Aug 27 '12 at 6:49
add a comment |
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.
{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 forplaces
they do almost equal.
– snakile
Aug 27 '12 at 6:49
add a comment |
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.
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.
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 forplaces
they do almost equal.
– snakile
Aug 27 '12 at 6:49
add a comment |
{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 forplaces
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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Feb 11 '14 at 12:00
DJCowley
5314
5314
add a comment |
add a comment |
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))
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
add a comment |
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))
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
add a comment |
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))
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))
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
add a comment |
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
add a comment |
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))
add a comment |
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))
add a comment |
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))
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))
answered Aug 23 at 15:51
Karl Rosaen
3,11012128
3,11012128
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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