我有一个函数x,如下所示,它接受两个NumPy数组作为输入,并且我想通过一些计算得到一个布尔值.

import numpy as np

def x(a,b):
    print(a)
    print(b)
    # Some computation...
    return boolean_value

wrappedFunc = np.frompyfunc(x,nin=2,nout=1)

arg_a = np.arange(8).reshape(2,4)
# arg_b is a numpy array having shape (2,1)
arg_b = np.array((np.array([[0, 1, 0],
                            [0, 0, 0],
                            [1, 0, 0],
                            [1, 1, 0]]),
                  np.array([[0., 1., 0.],
                            [0., 0., 0.],
                            [1., 0., 0.],
                            [1., 1., 0.],
                            [0.5, 0.5, 0.]])), dtype=object).reshape(2, 1)

执行上面的代码将产生以下输出.

# Output of a is:
0
1
2
3
4
5
6
7
# output of b is:
[[0 1 0]
 [0 0 0]
 [1 0 0]
 [1 1 0]]

[[0 1 0]
 [0 0 0]
 [1 0 0]
 [1 1 0]]

[[0 1 0]
 [0 0 0]
 [1 0 0]
 [1 1 0]]

[[0 1 0]
 [0 0 0]
 [1 0 0]
 [1 1 0]]

[[0.  1.  0. ]
 [0.  0.  0. ]
 [1.  0.  0. ]
 [1.  1.  0. ]
 [0.5 0.5 0. ]]

[[0.  1.  0. ]
 [0.  0.  0. ]
 [1.  0.  0. ]
 [1.  1.  0. ]
 [0.5 0.5 0. ]]

[[0.  1.  0. ]
 [0.  0.  0. ]
 [1.  0.  0. ]
 [1.  1.  0. ]
 [0.5 0.5 0. ]]

[[0.  1.  0. ]
 [0.  0.  0. ]
 [1.  0.  0. ]
 [1.  1.  0. ]
 [0.5 0.5 0. ]]

正如您可以看到的,变量ab分别打印了8次,这不是预期的行为,因为我预计会分别看到ab的打印语句的输出两次.print(a)print(b)语句的预期输出如下所示:

On first call:
a needs to be:[0,1,2,3]
b needs to be:[[0 1 0]
              [0 0 0]
              [1 0 0]
              [1 1 0]]
On second call:
a needs to be:[4,5,6,7]
b needs to be:[[0.  1.  0. ]
              [0.  0.  0. ]
              [1.  0.  0. ]
              [1.  1.  0. ]
              [0.5 0.5 0. ]]

我到底做错了什么?

推荐答案

让我们看看frompyfunc和一个简单的b,并将其与简单的numpy相加进行比较.

In [267]: a=np.arange(1,9).reshape(2,4); a
Out[267]: 
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

In [268]: b = np.array([[10],[20]]); b
Out[268]: 
array([[10],
       [20]])

A(2,4)与a(2,1)相加得到a(2,4).根据broadcasting的规则,大小为1的维度被‘复制’以匹配a的4:

In [269]: a+b
Out[269]: 
array([[11, 12, 13, 14],
       [25, 26, 27, 28]])

定义一个简单地添加两个"标量"的函数.正如所写的,它适用于数组,包括ab,但想象一下有大约if行只适用于标量.

In [270]: def x(i,j):
     ...:     print(i,j)
     ...:     return i+j
     ...:     

使用frompyfunc来生成可以broadcast其参数的ufunc,将标量值传递给x:

In [271]: np.frompyfunc(x,2,1)(a,b)
1 10
2 10
3 10
4 10
5 20
6 20
7 20
8 20
Out[271]: 
array([[11, 12, 13, 14],
       [25, 26, 27, 28]], dtype=object)

您似乎想要的是第一维的zip个数组:

In [272]: [x(i,j) for i,j in zip(a,b)]
[1 2 3 4] [10]
[5 6 7 8] [20]
Out[272]: [array([11, 12, 13, 14]), array([25, 26, 27, 28])]

注意,这里的x得到一个(4,)和(1,)形数组,再乘以broadcasting得到一个(4,)结果.

这两个输出数组可以合并,以得到与前面相同的(4,2):

In [273]: np.array(_)
Out[273]: 
array([[11, 12, 13, 14],
       [25, 26, 27, 28]])

一个相关的函数vectorize接受signature,它允许我们指定第一个轴上的迭代.要做到这一点需要一些练习(尽管我第一次try 就做对了!):

In [274]: np.vectorize(x, otypes=[int], 
    signature='(n,m),(n,1)->(n,m)')(a,b)
[[1 2 3 4]
 [5 6 7 8]] [[10]
 [20]]
Out[274]: 
array([[11, 12, 13, 14],
       [25, 26, 27, 28]])

vectorize有一份性能免责声明,这一点在signature版本中加倍适用.frompyfunc通常执行得更好(当它做我们想要的事情时).

对于小的数组,列表理解通常做得更好,但是对于大的数组,vectorize的伸缩性似乎更好,并且最终具有适度的速度优势.但是为了获得最佳的numpy性能,最好是使用整个数组(真正的矢量化),而不是任何这种"迭代".

Python相关问答推荐

如何使用上下文管理器创建类的实例?

如何使用stride_tricks.as_strided逆转NumPy数组

拆分pandas列并创建包含这些拆分值计数的新列

在Python中为变量的缺失值创建虚拟值

Python plt.text中重叠,包adjust_text不起作用,如何修复?

通过仅导入pandas来在for循环中进行多情节

如果索引不存在,pandas系列将通过索引获取值,并填充值

使用scipy. optimate.least_squares()用可变数量的参数匹配两条曲线

运行回文查找器代码时发生错误:[类型错误:builtin_index_or_system对象不可订阅]

使用numpy提取数据块

为什么默认情况下所有Python类都是可调用的?

如何在solve()之后获得症状上的等式的值

为什么NumPy的向量化计算在将向量存储为类属性时较慢?'

UNIQUE约束失败:customuser. username

并行编程:同步进程

Pandas 数据帧中的枚举,不能在枚举列上执行GROUP BY吗?

如何将泛型类类型与函数返回类型结合使用?

浏览超过10k页获取数据,解析:欧洲搜索服务:从欧盟站点收集机会的微小刮刀&

Pandas:将值从一列移动到适当的列

如何从一个维基页面中抓取和存储多个表格?