updated:
added another method to the methods I tried below for completion. This is new way of doing it, but most likely not very efficient, did not time it
I can only visualize 3D matrices in my mind as a book made up of pages. Each page is a 2D matrix. The third dimension of the matrix is the number of pages in it. This can be easily carried over to higher dimensions. 4D matrix, is a volume of books, and so on.
Suppose I have a 3D matrix in Mathematica like this:
a = {{1, 2, 3, 4}, {5, 6, 7, 8}};
b = {{9, 10, 11, 12}, {13, 14, 15, 16}};
c = {{17, 18, 19, 20}, {21, 22, 23, 24}};
m = {a, b, c}
(* { {{1, 2, 3, 4}, {5, 6, 7, 8}},
{{9, 10, 11, 12}, {13, 14, 15,16}},
{{17, 18, 19, 20}, {21, 22, 23, 24}}
}
*)
and want to map f
on all elements in each row/col but going down across the pages. From first page to last page of the book, then come back and do the same for next entry and so on. So that the result will be
{
{f[{1, 9, 17}], f[{2, 10, 18}], f[{3, 11, 19}], f[{4, 12, 20}]},
{f[{5, 13, 21}], f[{6, 14, 22}], f[{7, 15, 23}], f[{8, 16, 24}]}
}
This is explained in this diagram, which I very skillfully just made using Microsoft Visio ;)
There are the two methods I came up with after a bit of struggle. I'd like to find if there is a better or more elegant method. For example, I could not figure how to use Map[f,mat, ??]
of the above to produce this result in one step.
Method 1
Transpose[m];
f /@ Transpose[#] & /@ %
Method 2
Map[f[#] &, Table[m[[All, i, j]], {i, 2}, {j, 4}], {2}]
Method 3 (added 8/20/13)
Map[f, Partition[Flatten[m, {2, 3}], 4], {2}]
Summary and conclusions
This problem can be broken up to 2 parts. The first part is how transform the 3D matrix with the correct list in each entry. Once this is done, the rest is easy, since now one can just do Map[f,mat,{2}]
on the resulting matrix since this maps f
to each entry.
So the trick was is how to do the first part, as the second part I knew. All the answers are very good and learned from them, I selected Leonid's answer since it showed me more general way do this. I did not realized that Transpose
can be used as a general transformation using its second arguments. Here is the possible transformations showing the one I wanted. This is useful, since now if I want different transformation, I can look this up, and just use Transpose
again with different permutation. I am adding this to my Mathematica sheet cheat.
Appendix
On a side note. The main difference for me when working with Mathematica 3D matrices, is to remember that the first index is the page number and not the row number as the normal case with 2D matrices.
In Matlab, in 3D, the page number is the third index (which is a little bit more natural to me). So in Mathematica, m[[1,2,3]]
is the second row, third column in the first page.
While in Matab to reference the same entry, one writes m(2,3,1)
. Notice the 3rd dimension index location difference. So in Matlab, the first index is always the row and the second index is the column number, regardless if the matrix is 2D or 3D.
Here is the same matrix in Matlab:
a = [1 2 3 4 ; 5 6 7 8];
b = [9 10 11 12; 13 14 15 16];
c = [17 18 19 20; 21 22 23 24];
m = zeros(2,4,3);
m(:,:,1)=a;m(:,:,2)=b;m(:,:,3)=c;
m
gives
m(:,:,1) =
1 2 3 4
5 6 7 8
m(:,:,2) =
9 10 11 12
13 14 15 16
m(:,:,3) =
17 18 19 20
21 22 23 24
It is not a big deal, just something to remember that is all. Ofcourse Matlab does not have a nice to way to use Map's and such so easily as in Mathematica, so it all evens out at the end :)