Python单元测试中的assertAlmostEqual用于浮点数的集合 [英] assertAlmostEqual in Python unit-test for collections of floats
问题描述
x
和 y
是否大致相等(假设它们是浮点型)
assertAlmostEqual()
的问题在于它仅适用于浮点数.我正在寻找类似 assertAlmostEqual()
的方法,该方法适用于浮点数列表,浮点数集,浮点数字典,浮点数元组,浮点数元组列表,浮点数列表集等
例如,让 x = 0.1234567890
, y = 0.1234567891
. x
和 y
几乎相等,因为它们在除最后一位以外的每个数字上都一致.因此 self.assertAlmostEqual(x,y)
是 True
,因为 assertAlmostEqual()
适用于浮点数.
我正在寻找一个更通用的 assertAlmostEquals()
,它还会评估对 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)])
.
是否有这种方法,还是我必须自己实现?
说明:
-
assertAlmostEquals()
有一个名为places
的可选参数,并且通过计算四舍五入到小数位数的places
.默认情况下,places = 7
,因此self.assertAlmostEqual(0.5,0.4)
为False,而self.assertAlmostEqual(0.12345678,0.12345679)
为True.我的推测性assertAlmostEqual_generic()
应该具有相同的功能. -
如果两个列表具有相同顺序的几乎相等的数字,则认为它们几乎相等.形式上,
for range(n)中的i:self.assertAlmostEqual(list1 [i],list2 [i])
. -
类似地,如果两个集合可以转换为几乎相等的列表(通过为每个集合分配顺序),则认为它们几乎相等.
-
类似地,如果每个字典的键集几乎等于另一个字典的键集,则两个字典被认为几乎相等,并且对于每个这样的几乎相等的键对,都有一个相应的几乎相等的值.
p> -
通常:我认为如果两个集合相等,则除两个相应的浮点数几乎相等之外,它们几乎相等.换句话说,我真的想比较对象,但在沿途比较浮点数时精度(自定义)较低.
这是我实现通用 is_almost_equal(first,second)
函数的方法:
首先,复制您需要比较的对象( first
和 second
),但不要进行精确的复制:切掉您所用浮点数的无关紧要的十进制数字在物体内部遇到.
现在,您已经拥有了 first
和 second
的副本,对于这些副本来说,十进制数字已经消失了,只需比较 first
和 second
使用 ==
运算符.
让我们假设我们有一个 cut_insignificant_digits_recursively(obj,place)
函数,该函数复制 obj
但只保留每个 places
最高有效十进制数字漂浮在原始的 obj
中.这是 is_almost_equals(第一,第二,地方)
的有效实现:
从insignificant_digit_cutter递归导入cut_insignificant_digits_def is_almost_equal(第一,第二,位置):如果第一和第二相等,则返回True.如果第一和第二个不相等但具有完全相同的值,则返回true结构和值,除了一堆几乎差不多的浮点数相等(如果我们仅考虑浮点数,则浮点数相等,则浮点数几乎相等(将每个数字的最高有效位)).'''如果第一个==第二个:返回Truecut_first = cut_insignificant_digits_recursively(第一,地方)cut_second =递减cut_insignificant_digits_digits(秒,位)返回cut_first == cut_second
这是 cut_insignificant_digits_recursively(obj,place)
的有效实现:
def cut_insignificant_digits(数字,位):'''截断数字的最低有效十进制数字,仅保留[位数]个十进制数字'''如果type(number)!= float:返回数字number_as_str = str(数字)end_of_number = number_as_str.find('.')+位数+1如果end_of_number>len(number_as_str):返回数字返回float(number_as_str [:end_of_number])def cut_insignificant_digits_lazy(可迭代,位置):对于可迭代的obj:递归地产生cut_insignificant_digits_递归(obj,地方)def cut_insignificant_digits_recursively(obj,位):'''返回obj的副本,但每个浮点都失去其最低有效位小数位数仅剩余[位]小数位数'''t =类型(obj)如果t == float:返回cut_insignificant_digits(obj,位)如果t in(列表,元组,集合):返回t(cut_insignificant_digits_lazy(obj,位))如果t == dict:返回{cut_insignificant_digits_recursively(键,位置):cut_insignificant_digits_recursively(val,地方)用于键,val在obj.items()}中返回obj
可在此处获得代码及其单元测试: https://github.com/snakile/approximate_comparator.我欢迎进行任何改进和错误修复.
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.
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.
这篇关于Python单元测试中的assertAlmostEqual用于浮点数的集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!