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

Does np.dot automatically transpose vectors?

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

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)

所以通常预期的return是由

计算的

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

x1,...,xn 是具有约束的权重,所有权重总和必须为 1,' 表示向量已转置。

现在我有点想知道 numpy 点函数,因为

returns = np.dot(expected_returns_annual, weights)

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

给出相同的结果。

我还测试了 weights.T 的形状和权重。

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

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

有人知道为什么 numpy 会这样吗?在我看来,np.dot 乘积会自动按正确的原因塑造矢量,以便矢量乘积运行良好。对吗?

此致 汤姆

前段时间我也有同样的问题。似乎当您的矩阵之一是一维时,numpy 会自动计算出您要做什么。

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

If both a and b are 1-D arrays, it is inner product of vectors (without complex conjugation).

If both a and b are 2-D arrays, it is matrix multiplication, but using matmul or a @ b is preferred.

If either a or b is 0-D (scalar), it is equivalent to multiply and using numpy.multiply(a, b) or a * b is preferred.

If a is an N-D array and b is a 1-D array, it is a sum product over the last axis of a and b.

If a is an N-D array and b is an M-D array (where M>=2), it is a sum product over the last axis of a and the second-to-last axis of b:

在 NumPy 中,转置 .T 颠倒维度的顺序,这意味着它不会对您的一维数组做任何事情 weights

对于来自 Matlab 的人来说,这是一个常见的混淆来源,其中不存在一维数组。有关此问题的一些早期讨论,请参阅 Transposing a NumPy Array

np.dot(x,y) 在高维数组上有复杂的行为,但它在输入两个一维数组时的行为非常简单:它取内积。如果我们想获得与行和列的矩阵乘积相同的结果,我们必须编写类似

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

y 添加尾随维度以将其变成 "column",相乘,然后将我们的单元素数组转换回标量。但是 np.dot(x,y) 更快更有效,所以我们就用它。


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

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

你可以用 assert:

向自己证明这给出了与 np.dot 相同的答案
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:

  • If both arguments are 2-D they are multiplied like conventional matrices.
  • If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the last two indexes and broadcast accordingly.
  • If the first argument is 1-D, it is promoted to a matrix by prepending a 1 to its dimensions. After matrix multiplication the prepended 1 is removed.
  • If the second argument is 1-D, it is promoted to a matrix by appending a 1 to its dimensions. After matrix multiplication the appended 1 is removed.

注意事项:使用 @ 而不是 dot

的参数

正如 hpaulj 在评论中指出的那样,np.array_equal(x.dot(y), x @ y) 对于所有 xy1D2D 数组。那么为什么我(以及你为什么)更喜欢 @?我认为使用 @ 的最佳理由是它有助于以小而重要的方式改进您的代码:

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

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

    • 公平地说,虽然 [0,1,2].dot(arr) 显然无效,但 np.dot([0,1,2], arr) 有效(尽管比使用 @ 更冗长)。
  • 当您确实需要扩展您的代码以处理许多矩阵乘法而不是一个矩阵乘法时,@ND 案例在概念上很简单 generalization/vectorization 较低的 D 个案例。

The shape of weights.T should be (,5) and not (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)` 等形状。

np.dot 文档中最好地解释了示例中一维 weight 数组的处理。将其描述为 inner product 对我来说似乎很清楚。但我也很满意

sum product over the last axis of a and the second-to-last axis of 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 边界那样重要,但不应忽略。