关于numpy.outer [link]。
给定两个向量a = [a0, a1, ..., aM]
和b = [b0, b1, ..., bN]
,外积将为M * N矩阵。
但是如何实现3阵列外部产品,这意味着:给定
第三个向量c = [c0, c1, ..., cP]
,如何获得外部产品
3个numpy数组之间。
以及如何在numpy中为n-array获取n-way外积,对于einsum
的方法,如何将'i,j,k->ijk'
更改为处理"n"
。
答案 0 :(得分:4)
直接的做法是充分利用广播:
a[:,None,None] * b[None,:,None] * c[None,None,:]
np.ix_
会以适度的速度为您重塑
In [919]: np.ix_(a,b,c)
Out[919]:
(array([[[0]],
[[1]],
[[2]],
[[3]],
[[4]]]), array([[[10],
[11],
[12],
[13]]]), array([[[20, 21, 22]]]))
,结果数组可以乘以
np.prod(np.ix_(a,b,c))
einsum
版本简单,快速
np.einsum('i,j,k',a,b,c)
学习所有3种方法是个好主意。
嵌套outer
的问题是期望输入为1d,或者使它们变平。它可以使用,但需要一些重塑
np.outer(a,np.outer(b,c)).reshape(a.shape[0],b.shape[0],c.shape[0])
答案 1 :(得分:2)
你可以use einsum
:
>>> numpy.einsum('i,j,k->ijk', [1, 2], [3, 4, 5], [6,7,8,9])
array([[[18, 21, 24, 27],
[24, 28, 32, 36],
[30, 35, 40, 45]],
[[36, 42, 48, 54],
[48, 56, 64, 72],
[60, 70, 80, 90]]])
您不能使用einsum
跨越任意数量的向量。索引只能是字母,所以at most 52 vectors are allowed in this form(尽管it will raise "too many operands" when 32 vectors are provided):
def nary_outer_einsum_52(*vectors):
import string
subscripts = string.ascii_letters[:len(vectors)]
subscripts = ','.join(subscripts) + '->' + subscripts
# expands to `numpy.einsum('a,b,c,d,e->abcde', v[0], v[1], v[2], v[3], v[4])`
return numpy.einsum(subscripts, *vectors)
einsum支持使用数字索引而不是字母的另一种形式,遗憾的是only support up to 26 vectors,因为它只是在内部转换为字母版本。我不建议使用它,直到我提到的错误得到修复。
def nary_outer_einsum_26(*vectors):
operations = (x for i, v in enumerate(vectors) for x in (v, [i]))
# expands to `numpy.einsum(v[0], [0], v[1], [1], v[2], [2], v[3], [3], v[4], [4])`
return numpy.einsum(*operations)
numpy.ix_
(@PaulPanzer's answer)可以很容易地扩展到N-ary案例。仍然存在32个向量的实现限制:
def nary_outer_ix(*vectors):
return numpy.prod(numpy.ix_(*vectors), axis=0)
timeit with 3 vectors:
>>> timeit.Timer('nary_outer_einsum_52([1,2], [3,4,5], [6,7,8,9])', globals=globals()).autorange()
(100000, 0.8991979530110257) # 9 µs / iteration
>>> timeit.Timer('nary_outer_einsum_26([1,2], [3,4,5], [6,7,8,9])', globals=globals()).autorange()
(100000, 1.0089023629989242) # 10 µs / iteration
>>> timeit.Timer('nary_outer_ix([1,2], [3,4,5], [6,7,8,9])', globals=globals()).autorange()
(10000, 0.30978472300921567) # 31 µs / iteration
有26个载体:
>>> timeit.Timer('nary_outer_einsum_52(*([[1]] * 26))', globals=globals()).autorange()
(10000, 0.6589978060073918) # 66 µs / iteration
>>> timeit.Timer('nary_outer_einsum_26(*([[1]] * 26))', globals=globals()).autorange()
(10000, 0.7502327310066903) # 75 µs / iteration
>>> timeit.Timer('nary_outer_ix(*([[1]] * 26))', globals=globals()).autorange()
(1000, 0.2026848039968172) # 203 µs / iteration
有33个载体:
>>> nary_outer_einsum_52(*([[1]] * 33))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in nary_outer_einsum_52
File "/usr/local/lib/python3.6/site-packages/numpy/core/einsumfunc.py", line 948, in einsum
return c_einsum(*operands, **kwargs)
ValueError: too many operands
>>> nary_outer_ix(*([[1]] * 33))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in nary_outer_ix
File "/usr/local/lib/python3.6/site-packages/numpy/lib/index_tricks.py", line 83, in ix_
new = new.reshape((1,)*k + (new.size,) + (1,)*(nd-k-1))
ValueError: sequence too large; cannot be greater than 32
答案 2 :(得分:2)
您可以使用numpy.ix_
;它为其操作数添加了轴,使它们形成一个开放的网格。在A,B,C的形状下面是(2, 1, 1), (1, 3, 1)
和(1, 1, 4)
,所以只需将它们相乘就可以得到外部产品。
a = np.arange(1, 3)
b = np.arange(1, 4)
c = np.arange(1, 5)
A,B,C = np.ix_(a,b,c)
A*B*C
# array([[[ 1, 2, 3, 4],
# [ 2, 4, 6, 8],
# [ 3, 6, 9, 12]],
#
# [[ 2, 4, 6, 8],
# [ 4, 8, 12, 16],
# [ 6, 12, 18, 24]]])