我正在尝试计算股票投资组合的一阶和二阶矩(即预期收益和标准差)。
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乘积会自动调整向量的形状,以使向量乘积很好地工作。正确吗?
最好的问候 汤姆
答案 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)
为True
(array_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
数组的所有y
和1D
,2D
。那么,为什么我(以及您为什么应该)更喜欢@
?我认为使用@
的最佳论据是,它有助于以小而有意义的方式改进您的代码:
@
是明确的矩阵乘法运算符。如果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的边界没有那么关键的区别,但不应忽略。