使用NumPy,从1D和2D的功能创建3D数组,无需循环

时间:2018-03-15 01:31:03

标签: python arrays numpy numpy-broadcasting

使用python / numpy,我可以创建3D数组(注意矩阵指数函数)我想这样

import numpy as np
from scipy.linalg import expm

a = np.arange(3)
B = np.ones((2,2))
C = np.zeros((2,2,3))

for i in range(3):
    C[:,:,i] = expm(a[i]*B)

产生C,即3D阵列

[[[  1.           4.19452805  27.79907502]
  [  0.           3.19452805  26.79907502]]

 [[  0.           3.19452805  26.79907502]
  [  1.           4.19452805  27.79907502]]]

但我想消除循环。有什么办法可以摆脱for循环吗?也许是通过NumPy广播?我曾想过np.kron,但似乎无法找到一个好的方法来重塑,以便我可以应用expm函数,这需要一个正方形数组作为参数。

3 个答案:

答案 0 :(得分:1)

使用Sylvester's Formula和稍微不同的形状,您可以执行此向量化操作:

C = a[:, None, None] * B

C.shape
 (3, 2, 2)

E, V = np.linalg.eig(C)
V.swapaxes(-1,-2) @ np.exp(E)[..., None] * V
array([[[  1.        ,   0.        ],
        [  0.        ,   1.        ]],

       [[  4.19452805,  -4.19452805],
        [ -3.19452805,  -3.19452805]],

       [[ 27.79907502, -27.79907502],
        [-26.79907502, -26.79907502]]])

此方法可以将任意数量的方形矩阵作为(*, N, N)数组中的输入,但是当它消除了for循环开销时,它会将其交换为np.linalg.eig调用,这可能是如果N很大,则会缓慢。

答案 1 :(得分:0)

我不确定这是否与您所希望的完全相同,但如果没有for循环,它会给出相同的结果。

a = np.arange(3)
B = np.ones((2,2))
C = np.array([expm(a[i]*B) for i in range(3)]).T

在那里,仍然是" for"在列表理解中,不确定这是否符合您的目的。

答案 2 :(得分:0)

如果你想在for循环中多次调用expm来获得结果,你可以像这样构造输入:

import numpy as np
from scipy.linalg import expm

n = 4
t = np.zeros([n*2, n*2])
for i in range(n):
    t[2*i:2*i+2, 2*i:2*i+2] = i

C0 = expm(t)

这会产生格式

>>> t
array([[0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 0., 0., 0., 0.],
       [0., 0., 1., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 2., 2., 0., 0.],
       [0., 0., 0., 0., 2., 2., 0., 0.],
       [0., 0., 0., 0., 0., 0., 3., 3.],
       [0., 0., 0., 0., 0., 0., 3., 3.]])
>>> C0
array([[  1.        ,   0.        ,   0.        ,   0.        ,
          0.        ,   0.        ,   0.        ,   0.        ],
       [  0.        ,   1.        ,   0.        ,   0.        ,
          0.        ,   0.        ,   0.        ,   0.        ],
       [  0.        ,   0.        ,   4.19452805,   3.19452805,
          0.        ,   0.        ,   0.        ,   0.        ],
       [  0.        ,   0.        ,   3.19452805,   4.19452805,
          0.        ,   0.        ,   0.        ,   0.        ],
       [  0.        ,   0.        ,   0.        ,   0.        ,
         27.79907502,  26.79907502,   0.        ,   0.        ],
       [  0.        ,   0.        ,   0.        ,   0.        ,
         26.79907502,  27.79907502,   0.        ,   0.        ],
       [  0.        ,   0.        ,   0.        ,   0.        ,
          0.        ,   0.        , 202.21439675, 201.21439675],
       [  0.        ,   0.        ,   0.        ,   0.        ,
          0.        ,   0.        , 201.21439675, 202.21439675]])

如果您坚持在C的示例结构中使用结果,我相信您必须使用某种循环(列表理解,range或其他方式)来实现此目的,因为我不认为这是这种功能的标准输出结构。

i1 = range(0, 2*n, 2)
i2 = range(1, 2*n+1, 2)
i = (tuple(i1 + i2 + i1 + i2), tuple(i1 + i1 + i2 + i2))
C = C0[i].reshape(2,2,n)

>>> C
array([[[  1.        ,   4.19452805,  27.79907502, 202.21439675],
        [  0.        ,   3.19452805,  26.79907502, 201.21439675]],

       [[  0.        ,   3.19452805,  26.79907502, 201.21439675],
        [  1.        ,   4.19452805,  27.79907502, 202.21439675]]])

对于较大的输入,expm处理稀疏的方阵,因此您不应该对内存有任何问题。