Reputation: 145
I have M
vectors with N
entries each (MxN
matrix, if you will):
A = [A1, A2, ... , AN]
B = [B1, B2, ... , BN]
...
Z = [Z1, Z2, ... , ZN]
I want to transform these so that the output is:
[[[A1, 0, ... , 0],
[0, B1, ... , 0],
...
[0, 0, ... , Z1]],
[[A2, 0, ... , 0],
[0, B2, ... , 0],
...
[0, 0, ... , Z2]],
...
[[AN, 0, ... , 0],
[0, BN, ... , 0],
...
[0, 0, ... , ZN]]]
The goal is not to use for loops at all and achieve this solely with numpy
operations. Ideas?
Upvotes: 1
Views: 289
Reputation: 40768
I came upon an example here which uses np.apply_along_axis
to fill multiple diagonals.
A = [11, 12, 13, 14]
B = [21, 22, 23, 24]
C = [31, 32, 33, 34]
D = [41, 42, 43, 44]
E = [51, 52, 53, 54]
Z = np.array([A, B, C, D, E])
Having constructed Z
, you take its transposed and fill an empty diagonal array with its values:
>>> np.apply_along_axis(np.diag, -1, Z.T)
array([[[11, 0, 0, 0, 0],
[ 0, 21, 0, 0, 0],
[ 0, 0, 31, 0, 0],
[ 0, 0, 0, 41, 0],
[ 0, 0, 0, 0, 51]],
[[12, 0, 0, 0, 0],
[ 0, 22, 0, 0, 0],
[ 0, 0, 32, 0, 0],
[ 0, 0, 0, 42, 0],
[ 0, 0, 0, 0, 52]],
[[13, 0, 0, 0, 0],
[ 0, 23, 0, 0, 0],
[ 0, 0, 33, 0, 0],
[ 0, 0, 0, 43, 0],
[ 0, 0, 0, 0, 53]],
[[14, 0, 0, 0, 0],
[ 0, 24, 0, 0, 0],
[ 0, 0, 34, 0, 0],
[ 0, 0, 0, 44, 0],
[ 0, 0, 0, 0, 54]]])
Upvotes: 1
Reputation: 231665
While your question is clear enough, we prefer to see a [MCVE] (that tag expands in a comment, see SO intro). And when asking for no-loop solutions, it's polite to show a working loop solution - that gives us something test against. I at least like to prove that my answer works.
So here my minimal working example - with a loop solution:
In [308]: M = np.stack([np.arange(i,j) for i,j in zip([1,11,21],[5,15,25])])
In [309]: M.shape
Out[309]: (3, 4)
In [310]: M
Out[310]:
array([[ 1, 2, 3, 4],
[11, 12, 13, 14],
[21, 22, 23, 24]])
In [311]: np.stack([np.diag(M[:,i]) for i in range(4)])
Out[311]:
array([[[ 1, 0, 0],
[ 0, 11, 0],
[ 0, 0, 21]],
[[ 2, 0, 0],
[ 0, 12, 0],
[ 0, 0, 22]],
[[ 3, 0, 0],
[ 0, 13, 0],
[ 0, 0, 23]],
[[ 4, 0, 0],
[ 0, 14, 0],
[ 0, 0, 24]]])
np.diagonal
lets us specify axes. So for example we can use it to extract the starting array from Out[311]
:
In [318]: np.diagonal(Out[311],axis1=1, axis2=2)
Out[318]:
array([[ 1, 11, 21],
[ 2, 12, 22],
[ 3, 13, 23],
[ 4, 14, 24]])
I don't (off hand) see a multidimensional building. We could dig in docs some more, or look at the existing code to construct an equivalent. Or just accept the time penalty of that loop :)
The diag(onal)
functions use a flat
indexing for speed, but it's easier to use multidimensional indexing. We can access those same values with
In [319]: Out[311][:,np.arange(3),np.arange(3)]
Out[319]:
array([[ 1, 11, 21],
[ 2, 12, 22],
[ 3, 13, 23],
[ 4, 14, 24]])
We can use the same indexing to assign values.
In [320]: res = np.zeros((4,3,3),int)
In [321]: res[:,np.arange(3), np.arange(3)] = M.T
In [322]: res
Out[322]:
array([[[ 1, 0, 0],
[ 0, 11, 0],
[ 0, 0, 21]],
[[ 2, 0, 0],
[ 0, 12, 0],
[ 0, 0, 22]],
[[ 3, 0, 0],
[ 0, 13, 0],
[ 0, 0, 23]],
[[ 4, 0, 0],
[ 0, 14, 0],
[ 0, 0, 24]]])
If these last steps are confusing, I'd suggest experimenting with creating your own 2d diagonal array. Start small, and build on that knowledge.
Upvotes: 1
Reputation: 1789
You can use the diag numpy function, e.g.
import numpy as np
A = [0, 1, 2]
B = [0, 1, 2]
C = [0, 1, 2]
# Merge lists into matrix
X = np.array([A, B, C])
# Index columns and merge into list of diagonal matrices
print([np.diag(X[:, 0]), np.diag(X[:, 1]), np.diag(X[:, 2])])
To automate the final step (above) you may need to use a for-loop however e.g.
diag_list = []
for n in N:
diag_list.append(np.diag(X[:, n])
Upvotes: 0