将python函数广播到numpy数组

bha*_*ran 7 python numpy scipy

假设我们有一个特别简单的功能

import scipy as sp
def func(x, y):
   return x + y
Run Code Online (Sandbox Code Playgroud)

这个功能显然适用于几种内置Python数据类型xy像串,列表,整数,浮点,数组,等等.因为我们是在阵列特别感兴趣,我们考虑两个数组:

x = sp.array([-2, -1, 0, 1, 2])
y = sp.array([-2, -1, 0, 1, 2])

xx = x[:, sp.newaxis]
yy = y[sp.newaxis, :]

>>> func(xx, yy)
Run Code Online (Sandbox Code Playgroud)

这回来了

array([[-4, -3, -2, -1,  0],
  [-3, -2, -1,  0,  1],
  [-2, -1,  0,  1,  2],
  [-1,  0,  1,  2,  3],
  [ 0,  1,  2,  3,  4]])
Run Code Online (Sandbox Code Playgroud)

正如我们所期望的那样.

现在,如果想要将数组作为以下函数的输入?

def func2(x, y):
  if x > y:
     return x + y
  else:
     return x - y
Run Code Online (Sandbox Code Playgroud)

>>>func(xx, yy)会引起错误.

人们想出的第一个明显的方法是sp.vectorizescipy/numpy中的函数.尽管如此,这种方法已被证明效率不高.任何人都可以想到一种更强大的方式来广播任何功能到numpy数组吗?

如果以阵列友好的方式重写代码是唯一的方法,如果你也可以在这里提一下它会有所帮助.

unu*_*tbu 11

np.vectorize 是一种将操作数字的Python函数转换为在ndarrays上运行的numpy函数的通用方法.

但是,正如你所指出的那样,它并不是很快,因为它是在"引擎盖下"使用Python循环.

为了获得更好的速度,你必须手工制作一个将numpy数组作为输入的函数,并利用这个numpy-ness:

import numpy as np

def func2(x, y):
    return np.where(x>y,x+y,x-y)      

x = np.array([-2, -1, 0, 1, 2])
y = np.array([-2, -1, 0, 1, 2])

xx = x[:, np.newaxis]
yy = y[np.newaxis, :]

print(func2(xx, yy))
# [[ 0 -1 -2 -3 -4]
#  [-3  0 -1 -2 -3]
#  [-2 -1  0 -1 -2]
#  [-1  0  1  0 -1]
#  [ 0  1  2  3  0]]
Run Code Online (Sandbox Code Playgroud)

关于表现:

test.py:

import numpy as np

def func2a(x, y):
    return np.where(x>y,x+y,x-y)      

def func2b(x, y):
    ind=x>y
    z=np.empty(ind.shape,dtype=x.dtype)
    z[ind]=(x+y)[ind]
    z[~ind]=(x-y)[~ind]
    return z

def func2c(x, y):
    # x, y= x[:, None], y[None, :]
    A, L= x+ y, x<= y
    A[L]= (x- y)[L]
    return A

N=40
x = np.random.random(N)
y = np.random.random(N)

xx = x[:, np.newaxis]
yy = y[np.newaxis, :]
Run Code Online (Sandbox Code Playgroud)

运行:

N = 30时:

% python -mtimeit -s'import test' 'test.func2a(test.xx,test.yy)'
1000 loops, best of 3: 219 usec per loop

% python -mtimeit -s'import test' 'test.func2b(test.xx,test.yy)'
1000 loops, best of 3: 488 usec per loop

% python -mtimeit -s'import test' 'test.func2c(test.xx,test.yy)'
1000 loops, best of 3: 248 usec per loop
Run Code Online (Sandbox Code Playgroud)

N = 1000时:

% python -mtimeit -s'import test' 'test.func2a(test.xx,test.yy)'
10 loops, best of 3: 93.7 msec per loop

% python -mtimeit -s'import test' 'test.func2b(test.xx,test.yy)'
10 loops, best of 3: 367 msec per loop

% python -mtimeit -s'import test' 'test.func2c(test.xx,test.yy)'
10 loops, best of 3: 186 msec per loop
Run Code Online (Sandbox Code Playgroud)

这似乎表明它func2a略快func2c(并且func2b非常慢).


Sve*_*ach 10

对于这种特殊情况,您还可以编写一个在NumPy数组和普通Python浮点数上运行的函数:

def func2d(x, y):
    z = 2.0 * (x > y) - 1.0
    z *= y
    return x + z
Run Code Online (Sandbox Code Playgroud)

这个版本的速度也是unutbu的四倍func2a()(经过测试N = 100).