在包含对象的NumPy数组上定义数学运算(如sin…)

1 投票
1 回答
1260 浏览
提问于 2025-04-15 14:52

我想为一个模块创建的类似数字的对象提供“所有”数学函数(这个模块是uncertainties.py,它可以进行带误差传播的计算)——这些对象是带有不确定性的数字。

那么,最好的方法是什么呢?

目前,我在uncertainties.py模块中重新定义了大部分来自math的函数,使它们可以处理带不确定性的数字。一个缺点是,想要使用from math import *的用户必须在import uncertainties之后再进行导入。

不过,与NumPy的交互目前仅限于基本操作(比如可以对带不确定性的数字数组进行加法等);它还不支持更复杂的函数(例如,sin()),这些函数可以在包含带不确定性数字的NumPy数组上使用。我目前的做法是建议用户定义sin = numpy.vectorize(math.sin),这样新的math.sin函数(可以处理带不确定性的数字)就可以应用到任何NumPy数组的元素上。一个缺点是,用户需要为每个感兴趣的函数都这样做,比较麻烦。

所以,扩展像sin()这样的数学函数,使它们能够方便地与简单数字和NumPy数组一起使用,最好的方法是什么呢?

NumPy选择的做法是定义自己的numpy.sin,而不是修改math.sin使其能够与NumPy数组一起使用。我是否也应该对我的uncertainties.py模块采取同样的做法,停止重新定义math.sin呢?

此外,定义sin的最有效和正确的方法是什么,以便它能够同时处理简单数字、带不确定性的数字和NumPy数组?我重新定义的math.sin已经可以处理简单数字和带不确定性的数字。然而,使用numpy.vectorize将其向量化在“常规”NumPy数组上可能会比numpy.sin慢得多。

1 个回答

0

看起来,按照NumPy自己的做法来处理事情会更清晰:一些“扩展”的数学运算(比如正弦函数sin等)可以放在一个单独的命名空间里。因此,NumPy有了numpy.sin等函数。这些运算大多数情况下和math库里的函数是兼容的,但它们也可以在NumPy数组上使用。

所以,我觉得那些应该同时适用于普通数字、NumPy数组以及带有不确定性的数字的数学函数,最好也是放在一个单独的命名空间里。比如,用户可以这样做:

from uncertainties import sin

或者

from uncertainties import *  # sin, cos, etc.

为了优化,有一个替代方案就是提供两组不同的数学函数:一组是针对带有不确定性的普通数字的函数,另一组是针对带有不确定性的数组的函数:

from uncertainties.math_ops import *  # Work on scalars and scalars with uncertainty

或者

from uncertainties.numpy_ops import *  # Work on everything (scalars, arrays, numbers with uncertainties, arrays with uncertainties)

撰写回答