创建包含 3 个 2D 数字数组或 3 个 1D 数字数组的 3D 数字数组



我有三个 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

同样,我们有bc

>>> b
array([[[3, 3],
[3, 3]],
[[4, 4],
[4, 4]]])
>>> c
array([[[5, 6],
[5, 6]],
[[5, 6],
[5, 6]]])

此时,您已经可以开始以各种方式组合这三个数组,因此对于简单的函数(您可以分为两步),您可以简单地运行a + b + ca & 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 分作为预期的结果,其中每个点是12之一、34之一以及56之一的总和。

请注意,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轴上迭代。 对于这样的多维数组,它可以比更显式的迭代更简单的代码,但不是更快。

最新更新