NumPy - 将值围绕任意边界包装

3

我有一个数组x,其值包含在[a, b](包括a,不包括b)中。现在我想计算x + y(其中y是随机的),使得x + y仍然包含在[a, b]中,通过将结果值绕过左边界或右边界(取决于哪个被穿过)。

因为一个例子胜过千言万语:

>>> a, b = -5, 5
>>> x = np.array([-4, -2, 0, 2, 4])
>>> wrap(x, 3)  # Compute x + 3.
[-1, 1, 3, -5, -3]
>>> wrap(x, -3)  # Compute x - 3.
[3, -5, -3, -1, 1]

我想出了以下实现方法,但它并不完全令人满意。
def wrap(x, y):
    if y > 0:
        return (x - a + y) % (b - a) + a
    elif y < 0:
        return (x - b + y) % (a - b) + b
    else:
        return x
  1. 这个函数没有正确处理包含/排除边界条件;对于 wrap(x, -3) 它返回 array([ 3, 5, -3, -1, 1]) 而实际上 5 应该是 -5。我不确定如何包含此功能。
  2. 此外,显式的 if 子句看起来有些人为,我想知道是否可以以更一般的方式处理它(次要问题)。

有人有解决这个问题的想法吗?


我猜想 a < b - user202729
@user202729 是的,尽管它们可以取任意值,但假设a < b - a_guest
2个回答

3

我理解“wrapping”应该是指结果应该与x+yb-a同余(请注意,b-a在此情况下是“窗口大小”)。

考虑特殊情况a==0。我们有:

def wrap(x, y):
    return (x + y) % b

简单明了,对吧?请注意,在Python中,百分号(%)是向下取整的模运算符——结果的符号始终与除数的符号相同。因此,结果将位于[0..b[之间。
在更一般的情况下:
def wrap(x, y):
    return (x + y - a) % (b-a) + a

证明返回值与x+y mod b-a同余非常简单。

另外请注意,这是OP代码的第一个情况。对于所有的y值都有效。


但是,如果x是一个numpy数组,则会执行len(x) %操作,这可能会很慢。因此,可以采用以下方式:

def wrap2(x, y):
    y %= b - a
    x = x + y
    return x - (b-a)*(x >= b)

它应该更快。在线尝试! 解释:
  • y %= b-a:将y减少到范围[0..b-a[。这不会影响结果。
  • x = x + y:将y添加到x中。此步骤后,x将包含正确的值,模b-ax中的所有值都应在范围[a..b+(b-a)[内。
  • x - (b-a)*(x >= b)(x >= b)(如果解释为整数)将在x >= b时评估为1,否则为0。从乘以b-a的结果中减去x将从所有值>= b中减去b-a,并保持那些< b不变。

比大卫的解决方案更快,使用这个答案简化。[在线试用!](https://tio.run/##lY9Bi8MgEIXv/govBW1NSRN6aXEPhR63h4U9LaWMxLKBaMRYVn99VtMklIUc1oMy4/vemzHBfbe67PvL7fL5fjp/8CJPB6FK3vGPBbMjngUGTNADttI9rMbEb0IGdEVEvDcwS4sXaVjx@Hv0PGqP/su/cXHNhtZo4mesXMImZTYErUkyoQjVyrTWYf1QJmDosDbIc222FnTVquGptSM52@VsWosi6DoZKTLttGdJQDHn8@xji26haUgMuttWYVcrWTs8hj4rZGxKeBakASUqOPwxZmlAIS3f59FykSj@TZRLRN//Ag“Python 3-尝试在线”) - user202729
这样做更快,因为它更加向量化。干得好。不过需要一些解释才能处理它。这正是numpy的用途。通过使用等效的数学方法,您可以将问题转化为快速代码。 - David
@user202729 看起来我对 Python 中的 mod 运算符并不完全了解。感谢您详细的回答! - a_guest

0
假设您的输入数组已经在范围[a,b)内,则我们可以将wrap重写为以下函数;
def wrap(x,y,a,b):
    if y > 0:
        x += y
        x[x >=b] -= (b-a)
    elif y < 0:
        x += y
        x[x < a] += (b-a)
    return x

当然不会,为什么要将(a,b)范围之外的值添加到x中呢?你应该先对它进行取模。你问的是如何包装数值,而不是多次包装。 - David

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