包装np.arrays __pow__方法

MSe*_*ert 6 python performance numpy

我只是在重新审视我的一些代码来改善性能并且对一些奇怪的事情感到困惑:

a = np.linspace(10,1000,1000000).reshape(1000,1000)

%timeit np.square(a)
100 loops, best of 3: 8.07 ms per loop

%timeit a*a
100 loops, best of 3: 8.18 ms per loop

%timeit a**2
100 loops, best of 3: 8.32 ms per loop
Run Code Online (Sandbox Code Playgroud)

好吧,当使用power-operator(**)时似乎有一些开销,但是否则它们看起来完全相同(我猜NumPy正在这样做)但是它很奇怪:

In [46]: %timeit np.power(a, 2)
10 loops, best of 3: 121 ms per loop
Run Code Online (Sandbox Code Playgroud)

所以没有问题,但是对于魔法战队有一个后备似乎有点不一致,但对于UFUNC则没有.但后来我感兴趣,因为我使用了很多次幂:

%timeit a*a*a
100 loops, best of 3: 18.1 ms per loop

%timeit a**3
10 loops, best of 3: 121 ms per loop

%timeit np.power(a, 3)
10 loops, best of 3: 121 ms per loop
Run Code Online (Sandbox Code Playgroud)

在第三种力量中似乎没有"捷径",而UFUNC和'magic-pow'的工作方式相同(至少在性能方面).

但这并不是那么好,因为我想要一种在我的代码中使用幂的一致方法,而我不太确定如何包装__pow__numpy.

所以为了达到目的,我的问题是:

有没有办法可以包装numpys __pow__方法?因为我想要在我的剧本中写作能力的一致方式而不是写作a**2和在其他地方power(a, 3).简单地写a**3,并将其重定向到我的幂函数,将是首选(但为此我需要以某种方式包装ndarrays __pow__或?).目前我正在使用一个快捷方式,但那不是那么漂亮(我甚至必须声明exponent == 2 case,因为np.power那里执行不是最优的):

def power(array, exponent):
    if exponent == 2: #catch this, or it calls the slow np.power(array, exponent)
        return np.square(array)
    if exponent == 3:
        return array * array * array
    #As soon as np.cbrt is avaiable catch the exponent 4/3 here too
    return np.power(array, exponent) 

%timeit power(a, 3)
100 loops, best of 3: 17.8 ms per loop
%timeit a**3
10 loops, best of 3: 121 ms per loop
Run Code Online (Sandbox Code Playgroud)

我正在使用NumPy v1.9.3,我不想np.ndarray仅仅为了包装__pow__方法而继承子类.:-)

编辑:我重写了我的问题部分.澄清一下:我不是在问NumPy为什么会这样做 - 这只是为了解释为什么我会问这个问题.

rll*_*rll 3

这是一个很好的收获。我也想知道为什么会有这种行为。但为了简短地回答这个问题,我会这样做:

\n\n
def mypower(array, exponent):\n    return reduce(lambda x,y: x*y, [array for _ in range(exponent)])\n\n\n%timeit mypower(a,2)\n100 loops, best of 3: 3.68 ms per loop\n\n%timeit mypower(a,3)\n100 loops, best of 3: 8.09 ms per loop\n\n%timeit mypower(a,4)\n100 loops, best of 3: 12.6 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

显然,开销随着指数的增加而增加,但对于较低的指数来说,优于 10 倍时间。

\n\n

请注意,这与原始 numpy 实现不同,原始 numpy 实现不是特定于数字指数的,并且支持指数数组作为第二个参数(请在此处查看)。

\n\n

运算符超载

\n\n

做你想做的事情的方法是继承 ndarray 并使用视图。请参见以下示例:

\n\n
import numexpr\nimport numpy as np\n\xe2\x80\x8b\nclass MyArray(np.ndarray):\n    def __pow__(self, other):\n        return reduce(lambda x,y: x*y, [self for _ in range(other)])\n\xe2\x80\x8b\nclass NumExprArray(np.ndarray):\n    def __pow__(self, other):\n        return numexpr.evaluate("self**%f" % other)\n        #This implies extra overhead, is as much as 4x slower:\n        #return numexpr.evaluate("self**other")\n\na = np.linspace(10,1000,1000000).reshape(1000,1000).view(MyArray)\nna = np.linspace(10,1000,1000000).reshape(1000,1000).view(NumExprArray)\n\xe2\x80\x8b\n%timeit a**2\n1000 loops, best of 3: 1.2 ms per loop\n\n%timeit na**2\n1000 loops, best of 3: 1.14 ms per loop\n\n%timeit a**3\n100 loops, best of 3: 4.69 ms per loop\n\n%timeit na**3\n100 loops, best of 3: 2.36 ms per loop\n\n%timeit a**4\n100 loops, best of 3: 6.59 ms per loop\n\n%timeit na**4\n100 loops, best of 3: 2.4 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

有关此方法的更多信息,请点击此链接。另一种方法是使用自定义中缀运算符,但出于可读性目的不太好。正如人们所看到的,numexpr 应该是一种可行的方法。

\n