NumPy 中方便且经济的向量操作

Convenient and economic vector manipulation in NumPy

有没有一种方法可以在不过度使用 flatten()ravel()、创建用于创建每个矩阵的元组等的情况下在 NumPy 中操作矩阵?

我知道它不是 matlab,但是写 40 个字符而不是 4 个字符似乎效率不高。

例如:

A = ones(2,2) # doesn't work
A = ones((2,2)) # works with tuple
v = np.matlib.rand(2)
dot(v, A@v) # doesn't work: shapes are not aligned
vdot(v,A@v) # works 

现在我想更新矩阵列:

A[:,0]=A@v # nope! shapes are not aligned
# beautiful solution:
c = v.reshape(2,1)
c = A@c
c = c.flatten()
A[:,0]=c

我假设 A 的初始化是使用 numpy.ones 中的 ones。我们可以有一个单线,就像这样 -

A[:,[0]] = A@v.T

LHS : A[:,[0]] 保持维数不变 2D,与 A[:,0] 相比,维数会减少从而允许我们分配 A@v.T,这也是 2D.

RHS : A@v.T 处理前两行代码 :

c = v.reshape(2,1)
c = A@c

我们不需要 c = c.flatten() 的第三步,因为对于 LHS,我们正在使用带有 A[:,[0]]2D 视图,如前所述。

因此,我们只剩下经过修改的第四步,即解决方案本身列为此 post 中的第一个代码。


另一种方式

A[:,0] 将是一个 (2,) 数组,而 A@v.T 将是一个 (2,1) 数组。因此,(A@v.T).T 将是一个 (1,2) 数组, 可广播 反对 A[:,0]。所以,这给了我们另一种方式 -

A[:,0] = (A@v.T).T

ones 的参数签名是:

ones(shape, dtype=None, order='C')

shape 是一个参数,而不是一个开放式的 *args.

ones(2,2)2 作为 shape 传递,并且 2 asdtype`;所以它不起作用。

ones((2,2)) 将元组 (2,2) 作为 shape 传递。

有时函数被编写为接受元组或扩展元组,例如foo((1,2))foo(*(1,2))foo(1,2)。但这需要在函数内部进行额外检查。自己写个这样的函数试试看吧

另外tuples不增加计算成本。 Python 一直在创建和使用元组;只需在表达式中使用逗号即可创建元组(如果它不是制作列表的一部分)。

简单地定义一个函数来接受一个开放式 'list' 参数创建一个元组:

def foo(*args):
    print(type(args))
    return args
In [634]: foo(1)
<class 'tuple'>
Out[634]: (1,)
In [635]: foo(1,2)
<class 'tuple'>
Out[635]: (1, 2)
In [636]: foo((1,2))
<class 'tuple'>
Out[636]: ((1, 2),)
In [637]: foo(*(1,2))
<class 'tuple'>
Out[637]: (1, 2)

v = np.matlib.rand(2) 对我不起作用。什么是 v(形状,dtype)? matlab 具有最小二维维度;所以我怀疑 v 是二维的,甚至可能是 np.matrix class 数组。

vdotflattens input arguments to 1-D vectors first


好的,通过特殊导入我得到 matlib(一个旧的兼容包):

In [644]: from numpy import matlib
In [645]: matlib.rand(2)
Out[645]: matrix([[ 0.32975512,  0.3491822 ]])
In [646]: _.shape
Out[646]: (1, 2)

让我们试试双点:

In [647]: v=matlib.rand(2)
In [648]: A=np.ones((2,2))
In [649]: A@v
...
ValueError: shapes (2,2) and (1,2) not aligned: 2 (dim 1) != 1 (dim 0)

为什么它对你有用?对于二维数组,我们可以直接使用 dot@ 有时作为操作员工作,但增加了一些它自己的怪癖。

(编辑 - 稍后您使用 A@c,其中 c 是重塑的 v,相当于 v.T(转置)。)

In [650]: np.dot(A,v.T)     # (2,2) dot (2,1) => (2,1)
Out[650]: 
matrix([[ 0.63976046],
        [ 0.63976046]])
In [651]: np.dot(v,np.dot(A,v.T))    # (1,2) dot with (2,1) -> (1,1)
Out[651]: matrix([[ 0.40929344]])

想想看,因为 v 是 np.matrix,这也有效:v * A * v.T


我们不需要使用 matlib 来制作随机浮点数的二维数组:

In [662]: v1 = np.random.rand(1,2)
In [663]: v1.shape
Out[663]: (1, 2)
In [668]: np.dot(A,v1.T)
Out[668]: 
array([[ 1.63412808],
       [ 1.63412808]])
In [669]: np.dot(v1,np.dot(A,v1.T))
Out[669]: array([[ 2.67037459]])

或者如果我们跳过 2d,则 v1 1d

In [670]: v1 = np.random.rand(2)
In [671]: np.dot(A,v1)
Out[671]: array([ 0.8922862,  0.8922862])
In [672]: np.dot(v1, np.dot(A,v1))
Out[672]: 0.79617465579446423

请注意,在最后一种情况下,我们得到的是标量,而不是 (1,1) 数组(或矩阵)。

np.random.rand 是那些接受 *args 的函数,扩展的 'tuple'.


在你的最后一个例子中你必须使用 flat 因为 A[:,0] 插槽是 (2,) (如果 Anp.matrix 它仍然是 (2 ,1)), 而 @ 产生一个 (2,1),它必须被展平以适应 (2,)

In [675]: A@v.T
Out[675]: 
matrix([[ 0.63976046],
        [ 0.63976046]])
In [676]: A[:,0].shape
Out[676]: (2,)

使用我的 1d v1A[:,0] = np.dot(A,v1) 无需进一步整形即可工作。

一般来说,matlibnp.matrix 函数会增加混淆。创建它们是为了让任性的 MATLAB 编码人员更容易。


最简单的计算方法:

In [681]: np.einsum('i,ij,j',v1,A,v1)
Out[681]: 0.77649708535481299

但是使用 (1,2) 版本我们可以做到:

In [683]: v2 = v1[None,:]
In [684]: v2
Out[684]: array([[ 0.20473681,  0.68754938]])
In [685]: v2 @ A @ v2.T
Out[685]: array([[ 0.77649709]])