numpy.isclose(https://docs.scipy.org/doc/numpy-1.13.0/reference/generation/numpy.isclose.html):
abs(a - b) <= (atol + rtol * abs(b))
math.isclose(https://docs.python.org/3/library/math.html#math.isclose):
abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)
请注意,Numpy的方程不是对称的,并且将atol
和rtol
参数相关,这都是不好的事情(IMO)。
在这种情况下,Numpy“错误地”将两个数字标记为相等:
a = 0.142253
b = 0.142219
rtol = 1e-4
atol = 2e-5
# true because atol interferes with the rtol measurement
abs(a - b) <= (atol + rtol * abs(b))
Out[24]: True
# correct result, rtol fails
abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)
Out[29]: False
这是另一个案例对称性问题:
a = 0.142253
b = 0.142219
rtol = 1e-4
atol = 1.9776e-05
# relative to b
abs(a - b) <= (atol + rtol * abs(b))
Out[73]: False
#relative to a
abs(a - b) <= (atol + rtol * abs(a))
Out[74]: True
# math one has no problems with this
abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)
Out[75]: False
Python数学版本看起来是防弹的,Numpy应该开始使用它吗? 使用Numpy版本有什么好处?
改变任何恕我直言为时已晚。
这是所有numpy中最广泛使用的函数之一(通过assert_allclose
),唯一的途径是选择另一个名称。
我承认当前的实现看起来像个错误。 请注意,使用max(abs(a), abs(b))
代替abs(b)
不会破坏任何现有测试,它只是放宽了abs(a) > abs(b)
。
至少,这需要在文档字符串中警告它与内置不匹配
如何添加呢?
from numpy.__future__ import isclose
顺便说一句,这可能是随机数版本控制的一个主意...
为了澄清起见,导入实际上不会导入isclose,但会启用新的语义。 它将被挖到模块中。
我们的想法是,我们可以允许人们使用正确的功能,同时,我们不需要破坏任何现有代码。
,导入实际上不会导入isclose,但将启用新的语义
我不认为这是不可能的。 只需选择一个较长的名称,例如mathlike_ufunc
s,它也可以覆盖remainder
。
请注意, from __future__ import print_function
实际上确实会产生全局的print_function
实际上, from __future__ import *
导入样式可能不适合此处-在python中,它会在每个文件级别影响_syntax_,并且在numpy中执行类似操作会很棘手
迭代器异常不是语法更改。 我们只需要一个小的C模块来检查调用模块的名称空间。 我们甚至可以缓存模块,以便仅减少导入时间,并且只是微不足道。
您是对的- true_division
显然类似于isclose。
实际上,这是避免#9444中问题的一种好方法,也可以使用with
语句来管理不推荐使用的功能。
让我们CC @ ChrisBarker-NOAA作为math.isclose
的创建者。
这是IMO的一个很小的问题。 通常,通过摆弄它们直到测试通过才选择atol
和rtol
,目的是捕获比容差大一个数量级的错误。 像@charris建议的那样放松rtol
部分也许是有意义的,但是我真的不认为值得为此微不足道的内容使用堆栈自省技巧。 而且我们仍然会遇到这样的问题: isclose
通常被诸如assert_allclose
类的东西或各种第三方测试助手间接调用。
我在StackOverflow上询问有关使用__future__
样式导入的信息: https :
TLDR:可能,但不容易或不干净。
堆栈自省不仅用于此目的,而且还被建议作为更改函数返回值的一般策略。 问题是:原则上应该改变吗? 我确实认为,如果答案是肯定的,那么考虑到广泛使用,弃用期限至少应为几年。 Python从未包含过去的模块,但是如果有需要,这是尝试提高API稳定性的一种选择。
另一种方法是将其作为选项isclose(...,symmetric=True)
或assert_allclose(symmetric=True)
,其中默认False
。
我同意,当您不对rtol
和atol
值赋予含义时,只需调整它们以通过单元测试即可,这是一个小问题,如@njsmith所述。
但是,有时您想说的是误差在例如1%( rtol=0.01
)之内。
在这种情况下,关于rtol
测量的最坏情况错误是100%( rtol * abs(b)
接近atol
,然后是atol + rtol * abs(b) ~= 2 * rtol * abs(b)
)。
这意味着某些值可以通过,误差约为2%:
atol = 1e-8 #default
rtol = 0.01 # 1%
b = 1e-6
a = 1.0199e-6 # ~ 2% larger comapared to b
abs(a - b) <= (atol + rtol * abs(b))
True
实现@charris的想法会使此特定情况变得更糟,因为它使比较更加轻松,但是仍然值得这样做,因为它摆脱了对称性问题并且确实向后兼容。
IMO,如果Numpy使用Math函数会更好,但我知道更改可能会造成破坏性,对于大多数用户而言可能并不重要。 可以选择在isclose
核心之间进行切换。
@njsmith :感谢您的加入。
一段历史:当我为stdlib提出isclose时,我们当然将numpy视为现有技术。 如果仅仅是我,为了兼容性好,我可能使用了兼容方法:-)。
但是社区的其他人认为,做对Python来说“正确”的事情更为重要,因此进行了长时间的讨论……如果您想看看,我试图抓住PEP的大部分内容。
这是对numpy的引用:
https://www.python.org/dev/peps/pep-0485/#numpy -isclose
您可以看到与本次讨论的观点相同。
最后,提出了三个关键点:
1)对称方法将产生最少的惊喜。
2)默认绝对公差可能应该为零,以免对参数的数量级做任何假设。
3)当以较小的公差使用时,“弱”和“强”测试之间的差异是无关紧要的,这与预期的用例一样。
最后,我认为我们为math.isclose提出了“最佳”解决方案。
但是它是否“更好”到足以打破向后兼容性? 我不这么认为。
不幸的是,许多numpy(和python)都添加了许多功能,因为它们很有用,但是由于目前没有很多讨论,所以这些事情得到了解决,因此我们有很多次优的设计。 对于一个年轻的图书馆来说,这是预期的(也是必要的),我们现在必须忍受它。
@njsmith是正确的-我认为np.isclose()的使用很少有通过严格的FP错误分析设置的公差,而是使用np.isclose()本身尝试错误。
但是,我认为更大的问题是默认的atol
-它假设您的参数为1阶-这可能是一个非常错误的假设,并且由于它通常会导致测试通过而不应该,用户可能不会注意到。
In [85]: np.isclose(9.0e-9, 1.0e-9)
Out[85]: True
哎哟!
是的,这是造成此问题的atol
:
In [86]: np.isclose(9.0e-9, 1.0e-9, atol=0.0)
Out[86]: False
因此,有一条前进的道路可能是一个好主意。
我喜欢关键字参数的想法-似乎比尝试弄乱__future__之类的东西要简单得多。
然后我们可以决定是否要开始发出弃用警告,并最终更改下游的许多默认版本...
我想提出@bashtage的建议:
“”
另一种方法是将其添加为isclose(...,symmetric = True)或assert_allclose(symmetric = True)选项,当前情况下默认值为False。
“”
我认为这是一个比新函数名称更好的选择,并且可能导致将来弃用和更改默认值(或不更改)。
我认为除了(强)对称测试之外, atol
应该默认为零。
鉴于此,也许我们需要一个比symmetric
更好的参数名称,尽管还不错...
哦-最好确保除assert allclose()
之外,不在numpy中其他地方调用isclose()
assert allclose()
。
最简单的方法可能是在
然后在合并之前将其删除。 我们可以继续将
弃用警告,表示将现有代码更改为
symmetric = False; 没有理由说默认值必须是
即使有警告,也随时会更改。
我们是否也需要将其添加到assert_allclose
,还是只是默默地进行更改? 让人们更新他们的每一项测试以使警告静音,与使他们更新测试以降低容忍度并没有什么不同。
@xoviat @ eric-wieser不能对assert_allclose
进行任何向后不兼容的更改,也不能对其进行弃用警告,这会造成破坏性。 除非我想念您想弃用警告的地方?
恐怕这仍然听起来像是无穷小的痛苦
获得,对我来说。
默认情况下,让atol为非零并不是偶然的。 需要获得
任何预期结果包括的测试的明智默认行为
确切的零。
无需更改任何assert_allclose,因为它将更新为
内部使用旧行为。
IMO的无声变化是令人讨厌的! 弃用警告仅是为了
人们最终不需要在交互式提示中键入标志即可
得到新的行为,仅此而已
IMO的无声变化是令人讨厌的!
同意但是,应该零变化。 广泛使用的功能中的弃用警告也会迫使用户(至少是实际进行维护的用户)进行更改。
如何解决@charris建议的非对称问题呢? 这也将释放一些文档空间,其中可能充满了有关atol != 0.0
时可能发生的不良情况的警告。
如果不让人们知道更改,我们将无法解决该问题
行为。 我知道该怎么做的唯一方法是发出警告。
真正好的是一个自动重构工具来注入标记
成旧代码。 然后,“维护”将只是在您的计算机上运行脚本
码; 5分钟的工作。
抱歉,可以使用新标记来解决。
如何解决@charris建议的非对称问题呢?
该建议(https://github.com/numpy/numpy/issues/10161#issuecomment-349384830)似乎可行。
如果不让人们知道已更改的行为,我们将无法解决该问题。
我们没有修复它。 这就是语义版本控制问题中刚刚讨论的任何更改的成本/收益问题。 绝对是在这种情况下,我们不会对广泛使用的测试功能进行任何更改或发出任何警告。 请注意,当前行为不是错误(尽管可以说是次优选择)。
需要明确的是,我提议对isclose进行更改,但不对assert_allclose进行更改。
从来源看,这种变化将是破坏性的三分之一。
但是,收益可能仍然太小。 我没有人
对象虽然添加一个标志,对吗?
我认为没有人反对添加标志,对吗?
不确定,取决于细节。 @charris的建议可能不需要标志,而任何引入此标志的标志对于数组都有些可疑:
In [1]: import math
In [2]: math.isclose(0, 1e-200)
Out[2]: False
我反对添加没有具体动机和用例的标志。 有用的石蕊测试:如果新手开发人员问您为什么存在此标志,您将如何解释?
您将如何解释?
math.isclose
呼叫进行矢量化处理,请使用symmetric=True
标志我正在寻找一些实际问题需要解决的情况。
也许唯一要解决的实际问题是使初学者
多考虑浮点精度。 我的意思是@rgommers的例子
似乎是错的,但是如果您要处理的很小
数字? IMO的math.isclose实现更好,但我什至没有
认为对此有共识。 不幸的是,实际上并没有
一种适合所有人的解决方案,用于确定数字是否“接近”。 但
根据其他人的回答(这没有错!),我并不是真的
对API的任何更改。 我想唯一要采取的行动是
可能是文档更新(著名的遗言,因为我
以前以为旗帜可以吗?
当然,与math.isclose
进行比较,并记录如何通过设置rtol和atol来获得相同的矢量化行为(如果需要的话),便可以了。
另外,如果我理解正确, @ charris提出了一种解决方案,允许
几乎没有现在那么宽容,这不会破坏
任何测试。 我仍然认为这将是一个好主意,发出警告(中
警告应该发出一次),如果存在isclose的情况
认为数字在以前没有时是“接近”的。 好多更好
当它影响到他们时,任何人都知道。
我认为这里需要对我们正在讨论的内容进行澄清。 math.isclose
有两点不同:
atol
其他默认值rtol
不同定义我认为除了第一个问题与“ math.isclose”不同之外,我们无能为力。
我认为第二个问题最好通过添加默认为False
的symmetric
参数来解决。 现在我们可以在文档中写_“ np.isclose(x, y, symmetric=True, atol=0)
是math.isclose(x, y)
” _的矢量化版本,在我看来,这似乎是我们要解决的问题。
从这里开始,我们有三个选择:
isclose
调用,迫使用户编写isclose(..., symmetric=False)
以获得旧的行为而没有警告(和allclose
相似)。 我怀疑这不会花费太多代码,但是结果可读性差,没有太大的收获。 assert_close
将更改为在内部调用isclose(..., symmetric=False)
,因此不会影响其用户symmetric
参数设置为assert_close
。 这将在下游大量流失在我看来,选项1毫无争议,但其余的听起来似乎不值得破坏。
编辑:如果仅在行为发生变化时才发出警告,则2可能是可以接受的,这将大大减少噪音。
@ eric-wieser还有第三点区别: atol
和rtol
的组合方式。 ( math.isclose
使用max
, numpy.isclose
使用+
)。 这意味着除非atol
或rtol
为零,否则没有任何通用方法可以使math.isclose
呼叫与numpy.isclose
呼叫相匹配。
我仍然不认为这值得添加任何用户可见的API。
我赞成第二种选择。 我仍然支持该选项,
numpy将提供自动的附加规定
重构工具(已添加到entry_points中),您可以在其上运行
现有项目来修复它们。 根据别人说的,听起来像
其他人不会喜欢这个选项。
我不是,也从来没有赞成第三个或四个选择。 在
此外,我不建议在警告之前更改功能行为
已经发布了至少四个主要版本。
假设其他人不同意第二种选择(他们拥有),我将
赞成方案一。 但是其他人(尤其是@njsmith)并不赞成
您在此处提供的任何选项。 至少那是我的看法。
@njsmith这是不正确的; 您可以使用该标志更改函数行为。
我将第三种不同归纳为手挥手_“ rtol不同” _
“”
我正在寻找一些实际问题需要解决的情况。
“”
我将赌注押在甜甜圈上(我会这样做,因为我不知道那是什么意思。应该是。
当前的实现似乎存在三个“问题”:
1)不对称
2)即使不与零比较(不可避免)atol也会影响结果-但有效地将容差更改为两倍左右-如果atol较大,则公差甚至更大,如果使用大阶的atol可能会更大幅度值。
3)atol默认为非零-我实际上认为这是最大的问题(特别是在当前的算法中两者都加),因为它很容易导致测试通过而不应该通过-而且我们经常有点偷懒-我们使用默认的容差编写测试,如果通过则回答为ans,我们认为我们已经完成了。 (当我意识到那是工作原理时,我回到了y代码,然后DID找到了其中的几个-哎呀!
该线程中的某人说:“
isclose(1e-200,0.0)
默认情况下返回False。 我不同意-是的,这令人惊讶,但这将迫使用户考虑正在发生的事情,而当前的实现导致(例如):
在[8]中:np.isclose(1e-20,1e-10)
出[8]:是的
真? 一个是“ MAGNITUDE”的“十个订单”比其他订单大,然后返回“真”?
我的观点是,在与零比较的常见情况下,将atol非零给出的结果可能不那么令人惊讶,但是当使用小数(小的实数小于1的数量级时)时,结果会更加危险和错误。
而且,如果要处理大量数字,例如大于1e8,则默认的atol也不合适。
再加上(2),这意味着默认atol也可以以令人惊讶的方式弄乱相对公差测试。
所以:不,这不是错误,也不是“残破”,而是次优的,因此,有一种更好的实现方法将是很好的。
我确实喜欢标志方法,但是我想改变主意了-问题是,除非我们弃用它,并且在某个时候更改了默认标志,否则几乎所有使用“旧”算法的人几乎永远。 而且有很多很好的论据,为什么我们可能无法弃用它。
因此,可能需要使用新名称的新功能。 我们可以在文档中添加鼓励人们使用新代码的文档,并且_也许_在人们使用新代码的某个时候添加警告,但是我们绝不会破坏任何人的代码。
任何人都有新功能的好名字的主意吗????
也许是np.math.isclose和朋友? 我不知道。
这也解决了np.remainder
的不同,并且还允许您使用from numpy import math
轻松地矢量化代码
所以我将是+1
np.math
模块上的
引入新功能时,其行为与运行了十年的其他功能仅稍有不同,这通常是一个非常糟糕的主意。 在这种情况下,确实没有很多问题无法用文档充分解决。 因此,-1为一个新函数。 在一个全新的子模块上绝对为-1。
真? 一个是“ MAGNITUDE”的“十个订单”比其他订单大,然后返回“真”?
您的期望是对还是错完全取决于上下文。 如果那是来自[0,1)上连续分布的数字,那么是的,您可能会期望True。 用户确实应该了解绝对/相对公差以及函数的实际作用,这就是文档的用途。
symmetric_isclose()
有点long,但对我的眼睛并不冒犯。
2017年12月10日晚上8:09,Ralf Gommers [email protected]写道:
引入功能与其他功能略有不同的新功能
像这样工作了十年的功能通常非常差
理念。
我也不喜欢-但是通过更改来破坏人们的代码会更糟。
除了简化numpy之外,您还有其他想法吗?
在这种情况下,确实没有什么问题不能充分解决
用文档解决。
我不同意-默认问题-很多。
而且我认为没有任何方法可以调整参数来为您提供
对称比较。
在一个全新的子模块上绝对为-1。
我也是。 如果数学模块中有其他没有numpy的内容
等效项可能有用,可以将它们添加到numpy的命名空间中
像其他一切。
真? 一个是MAGNITUDE的十阶大于另一个,然后又回来了
真正????
您的期望是对还是错完全取决于上下文。
的确如此-这就是为什么atol没有“合理的”默认设置的原因。
如果是因为数字来自[0,1)上的连续分布
那么是的,您可能会期望True。
但isclose被广告宣传为相对比较-在这里
相对而言,绝对是绝对的比较,
无需用户考虑。
这就是我的全部观点-默认设置仅在您期望
值大约为1的数量级。
普遍。
这可归结为更糟-误报或误报。
在测试的常见用例中,误报要严重得多。
(即不应该通过的考试)
用户确实应该了解绝对/相对公差以及
功能确实可以做到
绝对可以-但是函数还应该具有合理的默认值和一个
健壮的算法,易于理解。
我倾向于改进文档,不理会该功能。 缺乏对称性可以解释为“ isclose(a,b)表示a接近b,但是由于浮点的绝对精度变化,b并不总是接近a的情况。 b应该是预期结果,而a应该是实际结果。” 注意,这对于测试是有意义的,对称函数的情况实际上要更复杂一些。 涉及除法的相对误差不是对称的。
我也不喜欢-但是通过更改来破坏人们的代码会更糟。 除了简化numpy之外,您还有其他想法吗?
您已经在这里通过了价值判断,即添加新功能总比没有新功能好。 不是恕我直言三种选择:
总的来说2是比3更好的选择,因此这才使numpy变得“更好”。 您还忽略了这个atol / rtol问题不仅限于单个功能,那么下一步是什么-一个新的且稍好一些的assert_allclose
? 这使得仅文档的情况更加清楚。
这是一个非常严重的缺陷,基本上,测试您的代码的代码已被窃听……不好。 您是否会将任何经过numpy.isclose和默认atol测试的东西发送到月球? 我会三思而后行……这就是为什么我们需要在文档中突出这些陷阱。
除非将别名函数强加给用户(不会发生),否则添加别名函数只会使代码库混乱。
我确实同意对称性是次要的,但是我们仍然应该修复它。 离开它可能会分散用户的实际陷阱。
@rgommers写道:
“”
您已经在这里通过了价值判断,即添加新功能总比没有新功能好。 不是恕我直言
“”
好吧,我花了很多时间思考和辩论math.isclose
,我们确实从研究numpy实现开始。 因此,是的,我确实认为该方法更好。 我从这次讨论中认为,这几乎是一个共识。
而且,将更好的算法/接口引入numpy会使它变得更好,是的。
也许您的意思是,在numpy中同时拥有旧的和新的更好的功能并不比仅保留旧的(也许文档更好)的功能更好。 当然,那是完全正确的观点,但是我试图不进行讨论,并且先前的评论是“引入新功能的行为与其他已经使用了十年的功能略有不同的行为,通常是一个非常糟糕的主意”似乎终止了讨论-我的观点是,如果新方法“足够好”,那将是值得的。 我们显然没有达成共识,是该特定选项是否“足够好”,而不是“更好”。
顺便说一下,我个人并没有说服自己值得改变,但是我确实想进行讨论。
这方面的关键是我的一些假设。 我不知道我们能否确定它们是否正确,但是:
1)np.isclose()的最大用途是用于测试-您的答案是否足够接近您的期望? -这是通过pytest测试中的np.assert_all_close或更直接地实现的,或者...。
2)大多数人在大多数情况下都不会进行严格的浮点误差分析等操作来确定期望的结果。 相反,他们尝试一个值,如果失败,则查看结果并确定它是否确实是错误,或者是否需要调整测试的容差。
3)很多时候,很多人会使用默认的容差开始(2)中的过程,并且只有在测试失败的情况下才能调整容差。
4)人们不阅读文档(超出了最初的“我怎么称呼”阶段)–至少直到他们发现令人困惑的行为之后才开始阅读,然后他们可能会去尝试理解某些东西是如何真正起作用的以消除困惑。 但是请参阅(3)-如果测试没有失败,他们不知道去看文档来了解原因。
所有这些使我得出这样的结论,即numpy将具有更好的类似于math.isclose的FP紧密度测试。
以及为什么更好的文档是一个好主意,但还不够。
也许我完全错了,大多数人在大多数情况下都仔细阅读了文档,并仔细选择了rtol和atol来解决问题-但我知道我,也没有团队中的六个人,直到我成为意识到这些问题。
:bikeshed:(该死,这没用-没有漂亮的表情符号)
也许relatively_close
或rel_close
?
另外一个“有趣”的皱纹:assert_allclose实际上确实使用atol = 0
默认。 还有另一个线程正在争论我们是否可以解决
那不一致的地方。 (在我的手机上,所以很难找到它。)
2017年12月11日14:58,“克里斯·巴克” [email protected]写道:
@rgommers https://github.com/rgommers写道:
“”
您已经在这里通过了价值判断,即添加了新功能
总比没有新功能好。 不是恕我直言
“”
好吧,我花了很多时间思考和辩论
math.isclose,我们确实从查看numpy实现开始
其中。 因此,是的,我确实认为该方法更好。 我想
通过这次讨论,这几乎是一个共识。而且,将更好的算法/接口引入numpy会使它变得更好,是的。
也许您的意思是,旧的和新的更好的功能在
numpy并不比单纯保留旧版本更好(也许更好)
记录)功能。 当然,这是完全正确的一点,但是我当时
试图进行讨论,而先前的评论是“
新功能,其行为与其他功能仅稍有不同
像这样工作了十年的想法通常是一个非常糟糕的主意。”
似乎终止了讨论-我的意思是,如果新方法是
“足够好”将是值得的。 我们显然没有
关于此特定选项是否“足够好”的共识,不是
是否“更好”。顺便说一句,我个人并没有说服自己值得
变化,但我确实想进行讨论。这方面的关键是我的一些假设。 我不知道我们可以
曾经肯定知道它们是否正确,但是:1。
np.isclose()的最大用途是用于测试-是您的答案
足够接近您的期望? -通过np.assert_all_close,或
更直接地,在pytest测试中,或...
2。大多数人在大多数时候不做严格的事情
浮点误差分析,以确定答案的好坏
预期是。 相反,他们尝试一个值,如果失败,他们会看
结果并确定它是否确实是一个错误,或者是否需要调整
测试的容忍度。
这意味着atol和
rtol合并,并且测试是否对称。很多人在很多时候开始(2)中的过程
默认公差,并且只有在测试失败的情况下才能调整公差。这意味着拥有默认的atol非常危险-测试
通过那不应该是一件真正的坏事。人们不阅读文档(除了最初的“我怎么称呼它”之外)
阶段)-至少直到他们发现令人困惑的行为,然后他们
可能会去尝试了解某些东西如何真正清除
混乱。 但是请参阅(3)-如果测试没有失败,他们不知道
去看看文档,了解原因。所有这些使我得出这样的结论,即numpy可以“更好”地使用
更多类似于math.isclose的FP紧密度测试。以及为什么更好的文档是一个好主意,但还不够。
也许我完全错了,大多数人都仔细阅读了文档,
大多数情况下,请仔细选择rtol和atol来解决问题-
但我知道,直到我的团队中没有六个人,我才这样做
意识到了这些问题。:bikeshed:(该死,这没用-没有漂亮的表情符号)
也许relative_close或rel_close?
-
您收到此邮件是因为有人提到您。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/10161#issuecomment-350886540或静音
线程
https://github.com/notifications/unsubscribe-auth/AAlOaNquy47fsOkvBlxcSa-Mxyhgdimlks5s_bORgaJpZM4Q2J-P
。
您是说#3183?
assert_allclose实际上确实默认使用atol = 0。
有趣-确实让我感觉更好。
这使人们对过去关于两者之间差异的讨论产生了朦胧的记忆-那是唯一的吗?
你好
我的热库,流体库和ht库大量使用了numpy的assert_allclose,因此在这里我分享了一些相关的想法。 该线程使行为上的差异看起来非常令人震惊,就像人们应该期望的那样,由于缺乏对称性和/或组合atol和rtol以及默认atol(在assert_allclose中不存在)的差异,人们应该在代码中发现错误。 , 我知道)。 所以我想看看我的库是否有任何错误,并破解了assert_allclose的一个非常粗糙的实现,然后我更改了测试以暂时使用它。
我认为我对assert_allclose的使用具有代表性-我比较各个选择的值,并进行模糊测试,并对参数结果调用assert_allclose进行测试。 我比较各种大小的整数和浮点数。 我很少考虑选择rtol或atol,只是很少添加atol或将rtol更改为不太严格的默认设置。 这些库分别调用assert_allclose 13159、1178和4243次。
因此,我进行了更改,并进行了测试测试。 我很高兴地说我没有发现任何新的错误或测试失败; 我唯一能找到的测试失败是我的assert_allclose实现。
我知道还有其他人不太幸运,如果assert_allclose或isclose中的任何内容发生更改,都会遇到问题。 如果我有一个assert_allclose模式可以复制math.allclose的行为,不管是通过另一个函数还是symmetric
标志,我个人都会觉得编写测试更舒服。 但是我知道有人要做很多工作,而且也没有公关人员。 我很高兴能舒适地检查我的代码是否有此漏洞!
2017年12月11日,下午5:01,Nathaniel J.Smith [email protected]
写道:
的。
谢谢,请注意,statsmodel示例使我对
atol的默认值-0.0以外的任何值都是危险的。
每两年出现一次的事实是否表明我们应该
终于为此做些什么?
也许是的。 这是一个小疣,但有时那些小的烦恼会增加
随着时间的流逝。
虽然我很满意assert_allclose的atol默认为零
(支持我的观点,即使您不接受该选项,
同意这是最好的选择)我们很多人-一直以来-并不
使用unittest,因此可以直接在测试中使用np.all_close。
是的,拉尔夫,如果我们做出更改,我们将希望更改所有三个
密切相关的功能:-(。但这使我们有机会制作它们
更一致,奖金?
尽管py2-3过渡进展不顺利,但仍有一些事情要做
表示在某个时候拥有“可以清除疣的版本” :-)
还有另一种选择,实际上是将is_close
到atol=0
的(实际上更好)数学版本:将默认值更改为None
,然后尝试atol=0
; 如果所有的True
返回了; 如果某些False
,请使用标准atol
再试一次,并且如果结果更改将发出弃用警告(并返回“旧”结果)。
可悲的是,一个人不能同时进行对称和atol运算,因为它们的方向不同(即,总是必须同时运行这两种情况,这似乎有点多)。 但是我不喜欢atol
默认而不是缺乏对称性...
我不赞成更改任何功能,除非有一个功能
您可以使用具有旧行为的文件。 另外,应该有一个
自动重构工具以完成更改。 但是那不是
即使在桌子上
py3k过渡不是我们想要再次拥有的东西,它
不是榜样。 将清理汇总为一个大版本不是一个好习惯
接近IMO。
尽管py2-3过渡进展不顺利,但在某些时候拥有“可以清除疣”版本是可以说的:-)
只是为了解决这一总体思路是特殊的版本号可以进行重大更改确定:总的原则是,这是确定清理疣,如果有,避免痛苦的不可接受的水平明显的过渡计划和好处足以证明费用。 这里的关键点是,判断需要根据对用户的影响,而不是根据我们是否“遵守规则”。 (如果您想花哨的话,我们是后果论者,而不是
atol缺省为非零-我实际上认为这是最大的问题(尤其是当前的两种算法都存在),因为它很容易导致测试通过而不应该通过-而且我们经常有点懒惰-我们用默认的容差编写测试,如果通过则回答为ans,我们认为已经完成了。 (当我意识到那是工作原理时,我回到了y代码,然后DID找到了其中的几个-哎呀!
请注意,statsmodel示例使我对atol的默认值很了解-除0.0以外的任何值都是危险的。 我们中的很多人-一直都有-一直没有使用unittest,因此可能直接在测试中使用np.all_close。
在成本/收益方面,“我们应该破坏用户代码以提高与其他软件包的一致性”。 “ numpy内部的不一致不仅令人困惑,而且以直接导致用户代码中的静默错误的方式引起混乱,我们可以解决这一问题”,这种方式更具吸引力。 我自己尚未形成意见,但是如果您想在这里取得进展,那么我将继续努力。
一张纸条:
“”“我很少考虑选择rtol或atol,
很少添加atol
-snip-
$分别调用assert_allclose 13159、1178和4243次。
-snip-
我没有发现任何新的错误或测试失败;
“””
好消息是,尽管我注意到assert_allclose的atol默认为零。 和
这就是为什么 :-)
@njsmith写道:
“我们应该破坏用户代码以增加与其他软件包的一致性”
我认为该线程上没有人提倡这一点-我敢肯定不是。 任何人都提倡的唯一一致性是相关的numpy函数之间的一致性。
“ numpy内部存在的不一致不仅会造成混淆,而且还会以直接导致用户代码中出现静默错误的方式引起混淆,我们可以解决此问题”
至少我是这样主张的。 我想大多数其他人。
问题在于,如果没有以下任何一项,我们将无法修复:
打破向后兼容性,即使有弃用周期,我也不认为有人认为这样做足够严重。
要么
制作新的标志或函数。
我认为新功能将是一种更清洁的方法。 只要有需要,旧代码就可以保持不变,新代码可以使用新功能,并且搜索和替换(或一些导入作为调用)可以轻松地一次切换一个文件。
(我想您甚至可以在测试代码中使用猴子补丁numpy...。)
我们已经有:
allclose
assert_allclose
assert_almost_equal
assert_approx_equal
assert_array_almost_equal
assert_array_almost_equal_nulp
assert_array_max_ulp
我认为向此列表添加更多选项实际上不会对实际用户产生太大影响。
好吧,我花了很多时间思考和辩论math.isclose,我们的确从研究numpy实现开始。 因此,是的,我确实认为该方法更好。
我也是,所以我一直是numpy.testing
的主要维护者之一-这几乎不是新问题。 坚持大写字母是对的并不能做到这一点。
也许您的意思是,在numpy中同时拥有旧的和新的更好的功能并不比仅保留旧的(也许文档更好)的功能更好。 当然,这是完全正确的观点,但是我试图不进行讨论,
确实,应该清楚这就是我的意思。
“引入新功能的行为与其他已使用了十年的功能略有不同的行为,通常是一个非常糟糕的主意”,似乎使讨论停了下来。
不,它指出了一个真正的问题,那就是添加新功能,而这些新功能经常被忽略或没有得到足够的重视。 在这种情况下,对于许多核心开发人员来说似乎很清楚-似乎您正在判断这艘船是从@njsmith , @pv , @charris和我
我为重新散布而事先表示歉意,但是assert_allclose
是正确的行为(或至少足够接近它)。 但是, isclose
可能会给人们带来麻烦,因为它假定了绝对的容忍度,正如其他人所指出的那样。 我正在考虑提交PR,以便在np.isclose
文档页面上放置一个大红色框,以警告人们这种行为。 听上去怎么样?
我为此预先表示歉意,但是assert_allclose是
正确的行为(或至少足够接近)。 但是,isclose可能会得到
人们陷入困境,因为它像其他人一样承担绝对的宽容
注意。 我正在考虑提交一份PR,以便在
np.isclose文档页面可以警告人们这种行为。 如何
那个声音?
+1
我认为文档应该更好。
谢谢,
-CHB
-
您收到此邮件是因为有人提到您。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/10161#issuecomment-351182296或静音
线程
https://github.com/notifications/unsubscribe-auth/AA38YDw4sdhRWYmeeyr4met1RCwWsjMQks5s_uBmgaJpZM4Q2J-P
。
我支持改进文档,但我认为建议的更改没有帮助。
..警告::默认
atol
不适用于
远远少于一个。
首先,一般而言,该警告甚至都不成立。 当您希望将小于1e-8
视为零时,默认的atol
_is_是合适的。 相对公差是不可能的,这就是存在绝对公差的原因。 相反,当您希望将小于1e-8
视为有意义时,默认的atol
不适用。 我们不假定一种尺寸适合所有尺寸。
因此,以最深切的敬意, @ xoviat ,我强烈反对您的主观声明:
...
assert_allclose
_is_是正确的行为(或至少接近它)。
我认为当前文档的问题在于,它很好地描述了_what_ atol
是,但是没有描述_why_它在那里,因此用户可能不理解何时更改默认值。 。
我提出以下建议:
“设置较小的非零值atol
可以使非常接近零的数字实际上等于零。因此,默认值atol
可能不适合您使用选择atol
,以使大于atol
数字被您认为是有意义的(与零不同),小于atol
数字被您认为可忽略的(等于零)。”
最后,我将相同的注释添加到isclose
和allclose
的文档字符串中。
这是我的建议。 要么接受,要么离开它。
干杯。
请留下有关PR的评论反馈。 特别是,您的建议过于冗长的IMO。
对不起,如果我太烦人了,我真的还没有达成共识。
@xoviat :感谢您处理文档。
由于这将再次出现,因此我将(可能)以旨在被拒绝的NEP的迷你版本结束本次讨论-总结提案和拒绝的原因。 作为记录-由于先前的问题已经逐渐消失。
很抱歉提出py2 / 3问题-是否/如何清除numpy中的疣是另一个时间和地点的讨论。
isclose()
此问题(及其他参考问题)是由于观察到的结果,即numpy.isclose()
和朋友使用的不是最佳算法,对于atol有争议的默认值,并且各种相关函数具有不同的默认值。 总体而言,这会给用户带来混乱,在最坏的情况下,如果用户不考虑设置atol
值,就会出现误报测试。
特别是stdlib中的实现: math.isclose()
,提供了一个不同的并且可以说更好的算法和默认值:这是一个对称测试,它不混合rtol和atol,默认atol是0.0
人们几乎一致认为情况并不理想,但没有共识表明情况不佳,无能为力。
由于向后兼容性问题,即使有弃用期和警告,它们也被普遍拒绝使用-这些功能广泛用于测试中,因此至少会带来很大的麻烦。
这不会破坏现有的代码,但是会使用丑陋而令人困惑的API永久存在。
TLDR:可能,但不容易或不干净。 似乎没有人愿意追求这一目标。
这个选项似乎是唯一获得关注的选项,但是没有得到参与讨论的核心开发人员的支持。
解决此问题的最干净方法是使用新算法和默认值(可能是math.isclose
使用的默认值)添加np.rel_close()
[或其他名称]。 新功能将被记录为“推荐”功能,以供将来的代码使用。 将来可能会向旧的警告中添加弃用警告-但似乎没有人认为这时的噪音值得。
可以构建一个重构工具来进行替换-但是谁会为此使用呢?
在可预见的将来,这可能会导致两个非常相似的功能,并且“引入新功能的行为与其他已经使用了十年的功能略有不同的行为,通常是一个非常糟糕的主意。”
为了获得少量收益而不值得,但是可以按顺序安排更好的文档,并且在这里完成:#10214
我仍然有一个问题:
@njsmith写道:
“”
我们已经有:
allclose
assert_allclose
assert_almost_equal
assert_approx_equal
assert_array_almost_equal
assert_array_almost_equal_nulp
assert_array_max_ulp
我认为向此列表添加更多选项实际上不会对实际用户产生太大影响。
“”
您的意思是说添加一个新的主意会好吗?
我还要注意:
其中大多数是断言-断言的泛滥是单元测试体系结构的副作用。
随着我们许多人转向其他测试架构(例如pytest),对断言的需求就消失了。
因此,与numpy.testing
做什么是一个与numpy core做什么无关的问题。
随着我们许多人转向其他测试架构(例如pytest),对断言的需求就消失了。
实际上,这本身就是一种危险。 测试可能从assert_allclose(...)
更改assert np.allclose(...)
,并且会变得越来越严格,这是一件坏事。
我仍然在pytest环境中使用assert_allclose
,因为它会给出有用的失败消息:
def test_shs():
a = [0.1, 0.2, 0.3, 0.4]
b = [0.2, 0.3, 0.3, 0.4]
> np.testing.assert_allclose(a,b)
E AssertionError:
E Not equal to tolerance rtol=1e-07, atol=0
E
E (mismatch 50.0%)
E x: array([ 0.1, 0.2, 0.3, 0.4])
E y: array([ 0.2, 0.3, 0.3, 0.4])
与使用assert np.allclose()
def test_shs():
a = [0.1, 0.2, 0.3, 0.4]
b = [0.2, 0.3, 0.3, 0.4]
> assert np.allclose(a, b)
E assert False
E + where False = <function allclose at 0x7f20b13c9840>([0.1, 0.2, 0.3, 0.4], [0.2, 0.3, 0.3, 0.4])
E + where <function allclose at 0x7f20b13c9840> = np.allclose
可以通过实现pytest_assertrepr_compare
来解决该问题,但是我不确定如何将其应用于函数调用,甚至无法确定pytest在哪儿调用它。
@ eric-wieser:写道:
“实际上,这本身就是一种危险。测试可能会从assert_allclose(...)更改为assert np.allclose(...),并且将变得越来越严格,这是一件坏事。”
正是我的观点-假设每个人都将使用断言进行测试,因此不关心isclose()和allclose()的默认值是否适合进行测试,这是一个“坏主意”-在理想情况下他们当然应该是世界。
您的意思是说添加一个新的主意会好吗?
不,我的意思是,鉴于我们已经掌握了表达几乎相等的测试的稍有不同的方式,不幸的是,大多数用户不会注意到或理解另一种添加方式。
我还要注意:
其中大多数是断言-断言的泛滥是单元测试体系结构的副作用。
随着我们许多人转向其他测试架构(例如pytest),对断言的需求就消失了。
它们恰好被写成断言,但是AFAICT这些功能中的每一个实际上都编码了“几乎相等”的不同定义。 (我认为。有些区别太模糊了,我无法分辨它们是否真实。)
更改算法和/或默认值:
由于向后兼容性问题,即使有弃用期和警告,它们也被普遍拒绝使用-这些功能广泛用于测试中,因此至少会带来很大的麻烦。
我不太喜欢那样说。 对我来说,这是唯一可能具有足以证明成本合理的好处的方法。 我并不是说他们愿意,我不能代表其他核心开发者。 我希望看到一些数据,但是如果没有数据,在保守主义方面犯错误似乎是正确的选择。 但是,如果有人产生了数据,我至少会看一下它:-)。
就像,如果有人出现并说:“我在3个大项目中尝试了我提出的更改,这导致10,000个测试中出现了12个额外的失败,而在这12个中,有8个是旧的隐藏的实际无声错误。错误的默认值” ...这将令人信服。 尤其是在这种情况下,我们有能力进行针对性较小的警告。 我猜实际的数字不会那么好,但是直到有人知道为止。
在2017年12月18日,星期一下午3:57,Nathaniel J. Smith <
[email protected]>写道:
不,我的意思是,鉴于我们已经拥有
不幸的是,表达几乎相等的测试的方式略有不同
大多数用户还不会注意到或理解另一种补充。
好吧,这里有一个很大的不同-每个添加都是因为它确实
一些不同的东西,以及浮点数的变化,
差异可能很重要。
一个新的相对亲近度函数基本上会做同样的事情,但是
做的更好”。 目标是建议使用新的
而不是旧的(并且可能最终弃用旧的)。
老实说,我不确定这是否支持该想法,
虽然。
例如,如果有人出现并说“我在3大
项目,导致10,000项测试中的12项额外失败,以及
12个,其中8个是被旧的坏消息隐藏的实际的无声错误。
默认值” ...这将非常有说服力。尤其是因为这是一个
我们有能力进行针对性较小的警告的情况。
我猜实际的数字不会那么好,但是直到
有人检查,谁知道。嗯,我的代码库有大约1500个测试-不是10,000个,但是我给它一个
射击。 如果没有其他问题,我可能会发现一个错误或一个或两个错误的测试。 或获得更多
放心!
大家好,
同事遇到这个问题后想在这里报时。
_免责声明:在尖叫“大厅里的火”并将其扔到栅栏上之前,我并没有真正考虑逻辑。 如果我对其进行了令人尴尬的哈希处理,请客气,很高兴。
对我而言,这听起来很像经典的架构/设计“鸡与蛋”问题-语义与实现。 并非所有的bug都存在于代码中,有时它们存在于语义中。 与仅完美地发现算法有缺陷的完美实现算法没有什么不同(如果有的话)-错误不在代码中,而是在算法中,但是无论哪种方式,它仍然是错误。 基本上,这个讨论听起来有点像经典的笑话“这不是错误,而是功能”。 当然,“选择”就是这样,但是恕我直言,这通常表示基本原理的结束-如果语义被选择为按原样执行,那么就这样结束,讨论就很酷了。
那么isclose()
的“期望”和/或“期望”语义是什么呢,仔细考虑一下,我不可避免地会聚在用户定义的语义上,即用户需要能够定义语义“关闭”的定义。
关于默认值,这最终不会破坏任何语义。 但是,选择不当很危险。 如果选择非零默认值以仅在abs(a - b) == 0
时给出合理的行为,那么这无疑听起来像是错误的soln。 这个特殊状态最好是特殊情况,因为它在语义上是特殊情况。 当差异为零时,“关闭”的定义中没有“摆动空间”,它是自定义的基石,即它们不是_close_,而是_exact_,其中“ close”是相对于精确值的相对偏差。 (_注意:_特殊的外壳条件可能会(或可能不会)影响性能。)
+
与max
rel_tol
和a_tol
由于使用和而不是max
_does_的实现而混合或“阴影”破坏了上面的函数语义。 用户对“关闭”的定义在被弄乱时以非平凡的方式绑定,因此破坏了“相对”和“绝对”的语义。仅考虑上述语义,这方面是一个错误,我发现@gasparka开场白是这一点不可辩驳的论点。
此处实际上只有两个运算符: -
中的a-b
和<=
中的|a-b| <= f(a, b, atol, rtol)
。 虽然减法是反交换的,但根据定义, |a - b|
不是,而是交换的。 但是,仅此一项不足以说明f(a, b, atol, rtol)
的可交换性。 但是,这里有一个强而有力的论据。
|a - b|
的可交换性可能不会强制f(a, b, atol, rtol)
的可交换性,以编程方式, f(a, b, atol, rtol)
并不是真的问题,但isClose(a, b, atol, rtol)
。 在这里,计算|a - b|
是在函数内部进行的,即isClose() := |a-b| <= f(a, b, atol, rtol)
。 由于这是内部的,并且a
和b
是通过的,因此|a-b|
是可交换的, isClose()
也必须是可交换的。 如果不是,则|a-b|
的可交换性完全失去意义。<=
不是<
,因此对于比较的相等部分,您必须满足|a-b| = f(a, b, atol, rtol)
,这意味着f(a, b, atol, rtol)
_也必须是可交换的(我认为吗?)。 不这样做意味着上述相等性为_never_ true(没有满足条件的a
和b
)或严格不等式<
具有实际上是在语义上定义的,对吗?您对所有或其中任何一项所做的(或不做的)事情是一个完全独立的问题。
最有用的评论
@njsmith :感谢您的加入。
一段历史:当我为stdlib提出isclose时,我们当然将numpy视为现有技术。 如果仅仅是我,为了兼容性好,我可能使用了兼容方法:-)。
但是社区的其他人认为,做对Python来说“正确”的事情更为重要,因此进行了长时间的讨论……如果您想看看,我试图抓住PEP的大部分内容。
这是对numpy的引用:
https://www.python.org/dev/peps/pep-0485/#numpy -isclose
您可以看到与本次讨论的观点相同。
最后,提出了三个关键点:
1)对称方法将产生最少的惊喜。
2)默认绝对公差可能应该为零,以免对参数的数量级做任何假设。
3)当以较小的公差使用时,“弱”和“强”测试之间的差异是无关紧要的,这与预期的用例一样。
最后,我认为我们为math.isclose提出了“最佳”解决方案。
但是它是否“更好”到足以打破向后兼容性? 我不这么认为。
不幸的是,许多numpy(和python)都添加了许多功能,因为它们很有用,但是由于目前没有很多讨论,所以这些事情得到了解决,因此我们有很多次优的设计。 对于一个年轻的图书馆来说,这是预期的(也是必要的),我们现在必须忍受它。
@njsmith是正确的-我认为np.isclose()的使用很少有通过严格的FP错误分析设置的公差,而是使用np.isclose()本身尝试错误。
但是,我认为更大的问题是默认的
atol
-它假设您的参数为1阶-这可能是一个非常错误的假设,并且由于它通常会导致测试通过而不应该,用户可能不会注意到。哎哟!
是的,这是造成此问题的
atol
:因此,有一条前进的道路可能是一个好主意。
我喜欢关键字参数的想法-似乎比尝试弄乱__future__之类的东西要简单得多。
然后我们可以决定是否要开始发出弃用警告,并最终更改下游的许多默认版本...