numpy的就地操作(例如`+ =`)如何工作?

时间:2013-04-16 10:26:28

标签: python numpy

基本问题是:在做什么时会发生什么:a[i] += b

鉴于以下内容:

import numpy as np
a = np.arange(4)
i = a > 0
i
= array([False,  True,  True,  True], dtype=bool)

我理解:

  • a[i] = xa.__setitem__(i, x)相同,后者直接指定i
  • 指示的项目
  • a += xa.__iadd__(x)相同,后者会添加

但是当我做时会发生什么:

a[i] += x

具体做法是:

  1. 这与a[i] = a[i] + x相同吗? (这不是就地操作)
  2. 如果i是:int,它会对这种情况产生影响吗?
    • ndarray索引或
    • slice
    • 一个a = np.zeros(4) x = np.arange(4) indices = np.zeros(4,dtype=np.int) # duplicate indices a[indices] += x a = array([ 3., 0., 0., 0.]) 对象

  3. 背景

    我开始深入研究这个问题的原因是我在使用重复索引时遇到了非直观的行为:

    {{1}}

    有关this question中重复索引的更多有趣内容。

4 个答案:

答案 0 :(得分:11)

您需要意识到的第一件事是a += x并不完全映射到a.__iadd__(x),而是映射到a = a.__iadd__(x)。请注意,documentation明确指出就地运算符返回其结果,并且不必是self(尽管实际上通常是这样)。这意味着a[i] += x通常会映射到:

a.__setitem__(i, a.__getitem__(i).__iadd__(x))

因此,添加技术上就地发生,但仅限于临时对象。尽管如此,创建的临时对象仍然可能比调用__add__少一个。

答案 1 :(得分:4)

实际上这与numpy无关。 python中没有“set / getitem in”,这些东西相当于a[indices] = a[indices] + x。知道了,事情变得非常明显。 (编辑:正如lvc所写的那样,实际上右侧是合适的,因此如果这是合法的语法,那么它是a[indices] = (a[indices] += x),虽然具有相同的效果,但是这样做很好)

当然a += x通过将a映射到np.add out参数来实际就位。

之前已经讨论过,numpy对此无能为力。虽然有一个想法让np.add.at(array, index_expression, x)至少允许这样的操作。

答案 2 :(得分:2)

正如Ivc所解释的那样,没有就地项目添加方法,因此它使用__getitem__,然后使用__iadd__,然后使用__setitem__。这是一种凭经验观察这种行为的方法:

import numpy

class A(numpy.ndarray):
    def __getitem__(self, *args, **kwargs):
        print "getitem"
        return numpy.ndarray.__getitem__(self, *args, **kwargs)
    def __setitem__(self, *args, **kwargs):
        print "setitem"
        return numpy.ndarray.__setitem__(self, *args, **kwargs)
    def __iadd__(self, *args, **kwargs):
        print "iadd"
        return numpy.ndarray.__iadd__(self, *args, **kwargs)

a = A([1,2,3])
print "about to increment a[0]"
a[0] += 1

打印

about to increment a[0]
getitem
iadd
setitem

答案 3 :(得分:2)

我认为这里的主要区别是就地运算符可能会返回相同的引用,但NumPy的效果与Python不同。

从Python

开始
string

这些是相同的参考。

>>> a = 1
>>> b = a
>>> a is b
True

就地添加会创建一个新参考。

现在为NumPy

>>> a += 4
>>> a
5
>>> b
1

同样这些是相同的参考,但是就地,运算符会有不同的效果。

>>> import numpy as np
>>> a = np.array([1, 2, 3], float)
>>> b = a
>>> a is b
True

添加ndarray更新引用。这与调用>>> a += 4 >>> a array([ 5., 6., 7.]) >>> b array([ 5., 6., 7.]) 不同,后者在新引用中创建副本。

numpy.add

借用参考文献的就地操作

这里的危险是如果引用被传递到不同的范围。

>>> a = a + 4
>>> a
array([  9.,  10.,  11.])
>>> b
array([ 5.,  6.,  7.])

>>> def f(x): ... x += 4 ... return x 的参数引用被传递到x的范围内,该范围不会复制,实际上会更改该引用的值并将其传回。

f

这可能会造成混淆,因此只能在属于当前作用域的引用上使用就地运算符,并注意借用的引用。