我有三个 numpy 2D 数组:
A1 with the shape of (x * y)
A2 with the shape of (x * z)
A3 with the shape of (y * z)
三个数组中的值为 True 或 False。现在,我想创建一个形状 (x * y * z) 的 3D 数组,以便 3D 数组中的每个元素都遵循:
3D_array[x, y, z] = A1[x, y] & A2[x, z] & A3[y, z]
我知道我可以用循环来做。但是有没有更快的方法可以做到这一点?喜欢通过矢量化?
或者,三个 2D 阵列实际上只是三个 1D 阵列之间的一些成对交互。因此,提出我的问题的一种更通用的方法是:
给出三个长度分别为 x、y、z 的 1D numpy 数组 (X)、(Y)、(Z),创建 3D numpy 数组的最佳方法是什么,以便 3D 数组中的每个元素都等于:
3D_array[i, j, k] = my_function(X[i], Y[j], Z[k])
其中my_function是一个自定义函数,例如返回 True/False。同样,我正在寻找比循环更好的东西。
欢迎对第一个问题或更一般的问题提出任何解决办法。非常感谢!
TL;博士:
np.apply_along_axis(sum, axis=0, np.stack(np.meshgrid(x, y, z)))
演练:
让我们看一个具体的例子,从创建三个一维数组开始:
x, y, z = [1, 2], [3, 4], [5, 6]
现在,让我们将它们转换为网格:
a, b, c = np.meshgrid(x, y, z)
这次我们得到了三个 3-D 数组:
>>> a
array([[[1, 1],
[2, 2]],
[[1, 1],
[2, 2]]])
这个数组对应于我们的第一个参数,它1
一半的时间,2
另一半。这是我们在x
中的两个值。我们总共有八个值,因为我们最终输出的形状应该是2 x 2 x 2 = 8
。
同样,我们有b
和c
:
>>> b
array([[[3, 3],
[3, 3]],
[[4, 4],
[4, 4]]])
>>> c
array([[[5, 6],
[5, 6]],
[[5, 6],
[5, 6]]])
此时,您已经可以开始以各种方式组合这三个数组,因此对于简单的函数(您可以分为两步),您可以简单地运行a + b + c
或a & b & c
。
在一般情况下,您可能希望定义采用三个数字并基于任意逻辑生成单个输出的函数。要应用这些函数,我们首先需要通过以下方式将这三个数组堆叠成一个数组:
>>> np.stack([a, b, c])
array([[[[1, 1],
[2, 2]],
[[1, 1],
[2, 2]]],
[[[3, 3],
[3, 3]],
[[4, 4],
[4, 4]]],
[[[5, 6],
[5, 6]],
[[5, 6],
[5, 6]]]])
现在我们有三个大小为 8 的数组(实际形状为3,2,2,2
),我们希望一次将一个函数应用于三个数字八次:
>>> np.apply_along_axis(sum, axis=0, np.stack(np.meshgrid(x, y, z)))
array([[[ 9, 10],
[10, 11]],
[[10, 11],
[11, 12]]])
我们得到 8 分作为预期的结果,其中每个点是1
或2
之一、3
或4
之一以及5
或6
之一的总和。
请注意,numpy
会将一维数组传递给f
而不是一系列参数,因此如果您有以下函数:
def f(a, b, c):
return a + b - c
您需要定义一个包装原始函数的附加函数:
def f2(vals):
return f(*vals)
# Alternatively:
f2 = lambda x: f(*x)
因此,现在我们可以将f2
应用于我们的数据:
>>> np.apply_along_axis(f2, 0, np.stack(np.meshgrid(x, y, z)))
array([[[-1, -2],
[ 0, -1]],
[[ 0, -1],
[ 1, 0]]])
带广播
3D_array[x, y, z] = A1[x, y] & A2[x, z] & A3[y, z]
可以通过以下方式完成:
B = A1[:,:,None] & A2[:,None,:] & A3[None,:,:]
实际上,这会将所有 3 个数组转换为 3D 数组,与大多数numpy
运算符协同工作。
更一般的情况:
3D_array[i, j, k] = my_function(X[i], Y[j], Z[k])
更难。
B = X[:,None,None] + Y[None,:,None] + Z[None,None,:]
同样,这适用于使用广播的运算符(和ufunc
),但不适用于仅适用于标量的用户定义函数。
我犹豫是否要提出这个建议,但也许有必要。np.vectorize
可以创建一个适用于此类广播的函数。 但它不是一个性能工具。
其他答案中建议apply_along_axis
不是性能工具。 它在other
轴上迭代。 对于这样的多维数组,它可以比更显式的迭代更简单的代码,但不是更快。