Python 当函数包含条件时,使用Numpy将函数应用于数组

Python 当函数包含条件时,使用Numpy将函数应用于数组,python,numpy,lambda,conditional,vectorization,Python,Numpy,Lambda,Conditional,Vectorization,当函数包含条件时,我很难将函数应用于数组。我有一个低效的解决方法,正在寻找一个高效(快速)的方法。在一个简单的例子中: pts = np.linspace(0,1,11) def fun(x, y): if x > y: return 0 else: return 1 pts = np.linspace(0,1,11) def fun(x, y): if x > y: return 0 else:

当函数包含条件时,我很难将函数应用于数组。我有一个低效的解决方法,正在寻找一个高效(快速)的方法。在一个简单的例子中:

pts = np.linspace(0,1,11)
def fun(x, y):
    if x > y:
        return 0
    else:
        return 1
pts = np.linspace(0,1,11)
def fun(x, y):
    if x > y:
        return 0
    else:
        return 1
现在,如果我跑步:

result = fun(pts, pts)
result = fun(pts, pts)
然后我得到了错误

ValueError:包含多个元素的数组的真值不明确。使用a.any()或a.all()

在x>y行的
处升高。我的低效变通方法给出了正确的结果,但速度太慢:

result = np.full([len(pts)]*2, np.nan)
for i in range(len(pts)):
    for j in range(len(pts)):
        result[i,j] = fun(pts[i], pts[j])
result = np.full([len(pts)]*2, np.nan)
for i in range(len(pts)):
    for j in range(len(pts)):
        result[i,j] = fun(pts[i], pts[j])
以更好(更重要的是,更快)的方式实现这一目标的最佳方式是什么

当函数包含条件时,我很难将函数应用于数组。我有一个低效的解决方法,正在寻找一个高效(快速)的方法。在一个简单的例子中:

pts = np.linspace(0,1,11)
def fun(x, y):
    if x > y:
        return 0
    else:
        return 1
pts = np.linspace(0,1,11)
def fun(x, y):
    if x > y:
        return 0
    else:
        return 1
现在,如果我跑步:

result = fun(pts, pts)
result = fun(pts, pts)
然后我得到了错误

ValueError:包含多个元素的数组的真值不明确。使用a.any()或a.all()

在x>y
行的
处升高。我的低效变通方法给出了正确的结果,但速度太慢:

result = np.full([len(pts)]*2, np.nan)
for i in range(len(pts)):
    for j in range(len(pts)):
        result[i,j] = fun(pts[i], pts[j])
result = np.full([len(pts)]*2, np.nan)
for i in range(len(pts)):
    for j in range(len(pts)):
        result[i,j] = fun(pts[i], pts[j])
以更好(更重要的是,更快)的方式实现这一目标的最佳方式是什么

编辑:使用

def fun(x, y):
    if x > y:
        return 0
    else:
        return 1
x = np.array(range(10))
y = np.array(range(10))
xv,yv = np.meshgrid(x,y)
result = fun(xv, yv)  

仍然会引发相同的
ValueError

错误非常明显-假设您有

x = np.array([1,2])
y = np.array([2,1])
以致

(x>y) == np.array([0,1])
如果np.array([0,1])
语句的结果应该是什么?是真是假
numpy
告诉您这是不明确的。使用

(x>y).all()

是显式的,因此
numpy
为您提供了解决方案-任何单元对都满足条件,或者所有单元对都满足条件-都是明确的真值。你必须自己定义向量x大于向量y的确切含义

操作所有
x
y
对的
numpy
解决方案是使用网格网格生成所有对:

>>> import numpy as np
>>> x=np.array(range(10))
>>> y=np.array(range(10))
>>> xv,yv=np.meshgrid(x,y)
>>> xv[xv>yv]
array([1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8,
       9, 4, 5, 6, 7, 8, 9, 5, 6, 7, 8, 9, 6, 7, 8, 9, 7, 8, 9, 8, 9, 9])
>>> yv[xv>yv]
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
       2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8])
xv
yv
发送到
fun
,或在函数中创建网格,具体取决于更有意义的内容。这将生成所有对
xi,yj
,以便
xi>yj
。如果需要实际索引,只需返回
xv>yv
,其中每个单元格
ij
对应
x[i]
y[j]
。就你而言:

def fun(x, y):
    xv,yv=np.meshgrid(x,y)
    return xv>yv
将返回一个矩阵,其中如果
x[i]>y[j]
,则
fun(x,y)[i][j]
为真,否则为假。或者

return  np.where(xv>yv)
将返回两个索引对数组的元组,这样

for i,j in fun(x,y):

也将保证
x[i]>y[j]

错误非常明确-假设您有

x = np.array([1,2])
y = np.array([2,1])
In [253]: x = np.random.randint(0,10,5)
In [254]: y = np.random.randint(0,10,5)
In [255]: x
Out[255]: array([3, 2, 2, 2, 5])
In [256]: y
Out[256]: array([2, 6, 7, 6, 5])
In [257]: x>y
Out[257]: array([ True, False, False, False, False])
In [258]: np.where(x>y,0,1)
Out[258]: array([0, 1, 1, 1, 1])
以致

(x>y) == np.array([0,1])
如果np.array([0,1])语句的结果应该是什么?是真是假
numpy
告诉您这是不明确的。使用

(x>y).all()

是显式的,因此
numpy
为您提供了解决方案-任何单元对都满足条件,或者所有单元对都满足条件-都是明确的真值。你必须自己定义向量x大于向量y的确切含义

操作所有
x
y
对的
numpy
解决方案是使用网格网格生成所有对:

>>> import numpy as np
>>> x=np.array(range(10))
>>> y=np.array(range(10))
>>> xv,yv=np.meshgrid(x,y)
>>> xv[xv>yv]
array([1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8,
       9, 4, 5, 6, 7, 8, 9, 5, 6, 7, 8, 9, 6, 7, 8, 9, 7, 8, 9, 8, 9, 9])
>>> yv[xv>yv]
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
       2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8])
xv
yv
发送到
fun
,或在函数中创建网格,具体取决于更有意义的内容。这将生成所有对
xi,yj
,以便
xi>yj
。如果需要实际索引,只需返回
xv>yv
,其中每个单元格
ij
对应
x[i]
y[j]
。就你而言:

def fun(x, y):
    xv,yv=np.meshgrid(x,y)
    return xv>yv
将返回一个矩阵,其中如果
x[i]>y[j]
,则
fun(x,y)[i][j]
为真,否则为假。或者

return  np.where(xv>yv)
将返回两个索引对数组的元组,这样

for i,j in fun(x,y):
也将保证
x[i]>y[j]

In [253]: x = np.random.randint(0,10,5)
In [254]: y = np.random.randint(0,10,5)
In [255]: x
Out[255]: array([3, 2, 2, 2, 5])
In [256]: y
Out[256]: array([2, 6, 7, 6, 5])
In [257]: x>y
Out[257]: array([ True, False, False, False, False])
In [258]: np.where(x>y,0,1)
Out[258]: array([0, 1, 1, 1, 1])
要与这两个1d阵列进行笛卡尔比较,请对其中一个阵列进行重塑,以便它可以使用
广播

In [259]: x[:,None]>y
Out[259]: 
array([[ True, False, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [ True, False, False, False, False]])
In [260]: np.where(x[:,None]>y,0,1)
Out[260]: 
array([[0, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 1, 1, 1, 1]])
您的函数和
if
仅适用于标量输入。如果给定数组,
a>b
将生成一个布尔数组,不能在
If
语句中使用。迭代之所以有效,是因为它传递标量值。对于某些复杂函数,这是您所能做的最好的(
np.vectorize
可以使迭代更简单,但不会更快)

我的答案是查看数组比较,并从中得出答案。在本例中,3参数
where
很好地将布尔数组映射到所需的1/0。还有其他方法可以实现这种映射

您的双循环需要添加一层编码,即广播的
None

要与这两个1d阵列进行笛卡尔比较,请对其中一个阵列进行重塑,以便它可以使用
广播

In [259]: x[:,None]>y
Out[259]: 
array([[ True, False, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [ True, False, False, False, False]])
In [260]: np.where(x[:,None]>y,0,1)
Out[260]: 
array([[0, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 1, 1, 1, 1]])
您的函数和
if
仅适用于标量输入。如果给定数组,
a>b
将生成一个布尔数组,不能在
If
语句中使用。迭代之所以有效,是因为它传递标量值。对于某些复杂函数,这是您所能做的最好的(
np.vectorize
可以使迭代更简单,但不会更快)

我的答案是查看数组比较,并从中得出答案。在本例中,3参数
where
很好地将布尔数组映射到所需的1/0。还有其他方法可以实现这种映射


您的双循环需要添加一层编码,即广播的
None
NUBA 示例

import numba as nb
import numpy as np

@nb.njit()
def fun(x, y):
  if x > y:
    return 0
  else:
    return 1

@nb.njit(parallel=False)
#@nb.njit(parallel=True)
def loop(x,y):
  result=np.empty((x.shape[0],y.shape[0]),dtype=np.int32)
  for i in nb.prange(x.shape[0]):
    for j in range(y.shape[0]):
      result[i,j] = fun(x[i], y[j])
  return result

@nb.njit(parallel=False)
def loop_preallocated(x,y,result):
  for i in nb.prange(x.shape[0]):
    for j in range(y.shape[0]):
      result[i,j] = fun(x[i], y[j])
  return result
计时

x = np.array(range(1000))
y = np.array(range(1000))

#Compilation overhead of the first call is neglected

res=np.where(x[:,None]>y,0,1) -> 2.46ms
loop(single_threaded)         -> 1.23ms
loop(parallel)                -> 1.0ms
loop(single_threaded)*        -> 0.27ms
loop(parallel)*               -> 0.058ms
*可能受缓存的影响。在自己的exa上测试