np.dot是否会自动转置向量?

时间:2019-01-12 13:44:26

标签: python numpy dot-product

我正在尝试计算股票投资组合的一阶和二阶矩(即预期收益和标准差)。

expected_returns_annual
Out[54]: 
           ticker
adj_close  CNP       0.091859
           F        -0.007358
           GE        0.095399
           TSLA      0.204873
           WMT      -0.000943
dtype: float64

type(expected_returns_annual)
Out[55]: pandas.core.series.Series



weights = np.random.random(num_assets)
weights /= np.sum(weights)
returns = np.dot(expected_returns_annual, weights)

所以通常期望收益是由

计算的

(x1,...,xn'*(R1,...,Rn)

具有x1,...,xn的权重具有一个约束,即所有权重之和必须等于1,并且'表示向量已转置。

现在我对numpy点函数有些疑惑,因为

returns = np.dot(expected_returns_annual, weights)

returns = np.dot(expected_returns_annual, weights.T)

给出相同的结果。

我还测试了重物的形状T和重物。

weights.shape
Out[58]: (5,)
weights.T.shape
Out[59]: (5,)

weights.T的形状应该是(,5)而不是(5,),但是numpy将它们显示为相等(我也尝试过np.transpose,但结果相同)

有人知道为什么numpy这样行吗?在我看来,np.dot乘积会自动调整向量的形状,以使向量乘积很好地工作。正确吗?

最好的问候 汤姆

4 个答案:

答案 0 :(得分:1)

我前段时间也有同样的问题。看来,当您的矩阵之一是一维时,numpy会自动找出您要执行的操作。

The documentation中的点函数对所应用的逻辑有更具体的解释:

  

如果a和b都是一维数组,则它是向量的内积   (没有复杂的共轭)。

     

如果a和b均为二维数组,则为矩阵乘法,但使用   最好是matmul或a @ b。

     

如果a或b为0-D(标量),则等于乘和   最好使用numpy.multiply(a,b)或a * b。

     

如果a是一个N-D数组而b是一个一维数组,则它是   a和b的最后一个轴。

     

如果a是一个N-D数组而b是一个M-D数组(其中M> = 2),则它是一个和   a的最后一个轴和b的倒数第二个轴上的乘积:

答案 1 :(得分:1)

在NumPy中,转置.T会颠倒维度的顺序,这意味着它对您的一维数组weights没有任何作用。

这是来自Matlab的人们的普遍困惑,在Matlab中,一维数组不存在。有关更早的讨论,请参见Transposing a NumPy Array

np.dot(x,y)在高维数组上具有复杂的行为,但是在馈入两个一维数组时其行为非常简单:它需要内积。如果我们想获得等效结果作为行和列的矩阵乘积,则必须编写类似

的内容
np.asscalar(x @ y[:, np.newaxis])

y上添加尾随尺寸以将其转换为“列”,相乘,然后将我们的单元素数组转换回标量。但是np.dot(x,y)更快,更高效,因此我们只使用它。


编辑:实际上,这对我来说是愚蠢的。当然,您可以编写矩阵乘法x @ y以获得与一维数组np.dot等效的行为,如tel的出色答案所指出的那样。

答案 2 :(得分:1)

np.dot的语义不是很好

正如多米尼克·保罗(Dominique Paul)所指出的,np.dot具有非常不同的行为,具体取决于输入的形状。正如OP在他的问题中所指出的那样,令weights为一维数组的np.array_equal(weights, weights.T)Truearray_equal检验值和形状)。

建议:改为使用np.matmul或等效的@

如果您刚从Numpy入手,我对您的建议是完全抛弃np.dot。根本不要在代码中使用它。而是使用np.matmul或等效的运算符@@的行为比np.dot的行为更可预测,同时仍易于使用。例如,对于代码中的两个1D数组,您将获得相同的点积,如下所示:

returns = expected_returns_annual @ weights

您可以证明自己,此np.dot的答案与assert相同:

assert expected_returns_annual @ weights == expected_returns_annual.dot(weights)

从概念上讲,@通过将两个1D数组提升为适当的2D数组来处理这种情况(尽管实现不一定要这样做)。例如,如果您的x的形状为(N,),而y的形状为(M,),那么如果您进行x @ y的话,形状将被提升为:

x.shape == (1, N)
y.shape == (M, 1)

matmul / @的完整行为

docs have to say about matmul/@ and the shapes of inputs/outputs的内容如下:

  
      
  • 如果两个参数都是二维的,它们将像常规矩阵一样相乘。
  •   
  • 如果任一自变量为N-D,N> 2,则将其视为位于最后两个索引中并相应广播的一组矩阵。
  •   
  • 如果第一个参数是1-D,则通过在其尺寸前面加1来将其提升为矩阵。矩阵相乘后,前面的1被删除。
  •   
  • 如果第二个自变量是1-D,则通过在其尺寸后附加1来将其提升为矩阵。矩阵相乘后,附加的1被删除。
  •   

注意:在@上使用dot的参数

hpaulj在注释中指出,对于np.array_equal(x.dot(y), x @ y)x数组的所有y1D2D。那么,为什么我(以及您为什么应该)更喜欢@?我认为使用@的最佳论据是,它有助于以小而有意义的方式改进您的代码:

  • @是明确的矩阵乘法运算符。如果x @ y是标量,则y将引发错误,而dot将假设您实际上只是想进行元素乘法。这可能会导致难以定位的错误,其中dot会静默返回垃圾结果(我个人碰到过那个错误)。因此,@允许您明确说明自己对代码行行为的意图。

  • 由于@是一个运算符,它具有一些不错的简短语法,用于将各种序列类型强制转换为数组,而不必显式转换它们。例如,[0,1,2] @ np.arange(3)是有效的语法。

    • 公平地说,虽然[0,1,2].dot(arr)显然无效,但np.dot([0,1,2], arr)是有效的(尽管比使用@更为冗长)。
  • 当您确实需要扩展代码以处理许多矩阵乘法而不是仅处理一次时,ND的{​​{1}}情况是下位{ {1}}个案例。

答案 3 :(得分:0)

  

权重的形状T应该是(,5)而不是(5,)

建议对shape属性感到困惑。 shape是一个普通的Python元组,即只是一组数字,每个数字对应数组的每个维度。这类似于MATLAB矩阵的size

(5,)只是显示1个元素元组的方式。由于旧的使用,作为简单分组的Python历史,因此需要()

In [22]: tuple([5])
Out[22]: (5,)

因此,中的(5,)没有特殊的numpy含义,并且

In [23]: (,5)
  File "<ipython-input-23-08574acbf5a7>", line 1
    (,5)
     ^
SyntaxError: invalid syntax

numpy与MATLAB之间的主要区别在于数组可以具有任意数量的维度(最多32个)。 MATLAB的下限为2。

结果是5个元素的numpy数组可以具有形状(5,)(1,5)(5,1),(1,5,1)等。

最好在weight文档中说明如何处理示例中的1d np.dot数组。对我来说,将其描述为inner product似乎很清楚。但是我对

也很满意
  

a的最后一个轴和b的倒数第二个轴上的乘积求和

说明,针对b仅具有一个轴的情况进行了调整。

(5,) with (5,n) => (n,)     # 5 is the common dimension
(n,5) with (5,) => (n,)
(n,5) with (5,1) => (n,1)

在:

(x1,...,xn' * (R1,...,Rn)

您是否错过了)

(x1,...,xn)' * (R1,...,Rn)

*表示矩阵乘积吗?不是元素积(MATLAB中的.*)? (R1,...,Rn)的大小为(n,1)。 (x1,...,xn)'大小(1,n)。产品(1,1)

顺便说一句,这带来了另外一个差异。 MATLAB将向右扩展尺寸(n,1,1 ...)。 numpy将其向左扩展(1,1,n)(如果广播需要)。初始尺寸是最外面的尺寸。这与下限2的边界没有那么关键的区别,但不应忽略。