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.