在NumPy或Python中等效的Matlab函数mod

4

我正在将一些 Matlab 代码转换为 Python。

在 Matlab 中有一个名为 mod 的函数,用于进行取模运算。

例如,以下示例展示了 Matlab 的 mod 函数与 numpy 等效的 remainder 函数之间的不同结果:

Matlab:

>> mod(6, 0.05)

ans =

     0

Numpy:
np.remainder(6, 0.05) 
0.04999999999999967

np.mod(6, 0.05)
0.04999999999999967

Python的取模运算符与numpy提供的结果相同。

6%0.05
0.04999999999999967

在Python中有没有与Matlab中相同的模操作?最好能够对numpy的2d/3d数组进行操作。

numpy文档显示,numpy.mod等同于Matlab中的mod


1
根据文档,这个函数计算与floor_divide函数互补的余数。np.floor_divide(6, 0.05)=119.0119.0*.05=5.95。因此,你得到的余数值为0.04999999999999967 - Aditya
@Aditya 但是 6//0.05 == 120 不是吗? - Quang Hoang
6//0.05 == 119.0。它将数字四舍五入到较低的值。 - Aditya
2个回答

6

这是问题的核心,在Python中:

>>> 6/0.05 == 120
True

>>> 6//0.05 == 120  # this is 119 instead
False
6/0.05 的浮点数结果足够接近 120(即在双精度精度范围内),所以它被四舍五入为 120.0。然而,它比 120 稍微小一些,所以显式的地板除法将在将该数字规范化为 120.0 之前将其截断为 119。
以下是一些证明:
>>> from decimal import Decimal
... print(6/Decimal(0.05))  # exactly approximate
... print(6/Decimal('0.05'))  # exact
119.9999999999999933386618522
1.2E+2

第一个数字是使用 6/0.05 得到的,但是数字 119.9999999999999933386618522 被舍入为最接近双精度浮点数表示的数字,这个数字是120。可以轻松地证明在双精度内这两个数字确实相同:

>>> print(6/Decimal('0.05') - 6/Decimal(0.05))
6.6613381478E-15

>>> 120 - 6.6613381478E-15 == 120
True

现在,这里有来自MATLAB的help mod:
    MOD(x,y) returns x - floor(x./y).*y if y ~= 0, carefully computed to
    avoid rounding error. If y is not an integer and the quotient x./y is
    within roundoff error of an integer, then n is that integer.

这表明当x/y接近整数时,它会先四舍五入,而不像Python中一样截断。所以MATLAB在浮点数结果方面进行了一些魔法。

最简单的解决方案是自己进行四舍五入(除非你可以使用类似于decimal.Decimal之类的东西,但这意味着你应该完全放弃本地双精度,包括字面量)并以这种方式复制MATLAB的mod,假设这对你的用例有意义。


1
谢谢,我采纳了您的建议并自己四舍五入了数字。np.round(6/array, decimals=3)*array - 6 在我的数据集中,精度限制为3个小数点。为了检查是否没有余数,我执行了 np.isclose(np.round(6/array, decimals=3)*array - 6, 0) - Khalil Al Hooti
1
@KhalilAlHooti,这基本上就是我会做的 :) 由于MATLAB正在做一些奇怪的事情,因此您必须使用类似于“isclose”的东西来复制它们的行为。至少您拥有的3位小数精度使得这相当稳健,这将是我的主要关注点。 - Andras Deak -- Слава Україні
再次感谢,我认为numpy的开发人员不应该声称他们的mod函数与Matlab的mod函数等效。 - Khalil Al Hooti
1
@KhalilAlHooti,"The MATLAB function equivalent to np.remainder is mod"的确切措辞并没有明确宣称它们完全相同(即对于相同的输入返回完全相同的输出),但我可以理解为什么会这样。每个人对“等效”的理解可能不同。您可以在numpy的问题页面上提出此问题,并且可能会进行更改(不是行为,而是文档中的措辞)。 - Andras Deak -- Слава Україні

2
这里有一个解决方法。它基本上将分母舍入为其字符串表示形式,然后进行整数运算: "最初的回答"
import numpy as np
import decimal

def round_divmod(b,a):
     n,d = np.frompyfunc(lambda x:decimal.Decimal(x).as_integer_ratio(),1,2)(a.astype('U'))
     n,d = n.astype(int),d.astype(int)
     q,r = np.divmod(b*d,n)
     return q,r/d

a = np.round(np.linspace(0.05,1,20),2).reshape(4,5)
a
# array([[0.05, 0.1 , 0.15, 0.2 , 0.25],
#        [0.3 , 0.35, 0.4 , 0.45, 0.5 ],
#        [0.55, 0.6 , 0.65, 0.7 , 0.75],
#        [0.8 , 0.85, 0.9 , 0.95, 1.  ]])
round_divmod(6,a)
# (array([[120,  60,  40,  30,  24],
#        [ 20,  17,  15,  13,  12],
#        [ 10,  10,   9,   8,   8],
#        [  7,   7,   6,   6,   6]]), array([[0.  , 0.  , 0.  , 0.  , 0.  ],
#        [0.  , 0.05, 0.  , 0.15, 0.  ],
#        [0.5 , 0.  , 0.15, 0.4 , 0.  ],
#        [0.4 , 0.05, 0.6 , 0.3 , 0.  ]]))

非常感谢,它运作地很好。但是我发现像@Andras Deak建议的四舍五入数字更容易和更快速地操作。 - Khalil Al Hooti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接