How to get MATLAB data imported with the same dimensions?
Some theory
This is not completely trivial, and the reason is in the differences between how Matlab and Mathematica represent tensors (multi-dimensional arrays), of which I will stress three:
Matrices in Matlab are stored in the column-major order (like in Fortran and R), while in Mathematica they are stored in the row-major order (like in C). This is also true for sparse matrices. This has a number of implications for things like data transfer between Matlab and Mathematica (when Matlab engine C API is used), but also for linear indexing. For example,
mlbmat = [[1 2 3]; [4 5 6]] mmamat = {{1,2,3},{4,5,6}} mlbmat(4) --> 5 Flatten[mmamat][[4]] --> 4
So called trailing (thanks for the correction, @R.M) singleton dimensions - trailing meaning dimension - 1 at the start or end of the dimensions list, but not in the middle - are automatically removed by Matlab, while they are kept in Mathematica. So, for example,
mlbsingletons = [[[[1],[2],[3]];[[4],[5],[6]]]]
is equivalent to
[[1 2 3];[4 5 6]]
while in Mathematica this would be
{{{{1},{2},{3}},{{4},{5},{6}}}}
and these "singleton" dimensions will be kept by the system.
Arrays of higher dimensionality are treated also differently. Higher dimensions are added in Matlab via the pointer mechanism, so they are prepended to the list of dimensions, rather than appended to it.
Translation
I happened to have developed the translation functions in the past, so here I will post and illustrate what I was using. I won't discuss the singleton dimension, just drop it. Here are the conversion functions:
ClearAll[newDims, fromMmaToMtlb, fromMtlbToMma];
newDims[tensor_] :=
Join[Take[#, -2], Drop[#, -2]] &@Dimensions[tensor];
fromMmaToMtlb[tensor_] := Map[Transpose, tensor, {-3}];
fromMtlbToMma[tensor_] :=
With[{values = Flatten[tensor], dims = Reverse[Dimensions[tensor]]},
Map[Transpose, First@Fold[Partition, values, dims], {-3}]];
I used these to communicate with Matlab via its C engine API. If you start e.g. with the following array:
tst = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}},
{{13,14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}};
which has dimensions
Dimensions@tst
(* {4,3,2} *)
then here is what is the equivalent Matlab array:
mlbtst = fromMmaToMtlb[tst]
(*
{{{1,3,5},{2,4,6}},{{7,9,11},{8,10,12}},
{{13,15,17},{14,16,18}},{{19,21,23},{20,22,24}}}
*)
with (Matlab) dimensions
newDims[tst]
(* {3,2,4} *)
which are the reverse of Dimensions[mlbtst]
, due to the column-major order vs row-major order difference. Now, the reverse would be:
fromMtlbToMma[mlbtst]
(*
{{{1,2},{3,4},{5,6}},{{7,8},{9,10},{11,12}},
{{13,14},{15,16},{17,18}},{{19,20},{21,22},{23,24}}}
*)
so we get back our original array.
Leonid has given you the theory behind why the dimensions get flipped — it's because of how arrays are indexed. However, I offer a much simpler way of doing the transformation using the powerful second argument of Flatten
. First, let's create an example in MATLAB:
mat = reshape(magic(32),[1,2,4,8,16]);
size(mat)
% ans = 1 2 4 8 16
save('~/test.mat','mat','-v7.3')
Now we import this in Mathematica
mma = Import["~/test.mat", {"HDF5", "Datasets", "mat"}];
Dimensions@mma
(* {16, 8, 4, 2, 1} *)
Ok, so to convert this to MATLAB, the transformation is as simple as the following:
mma2mat = Flatten[mma, Table[{i}, {i, Depth[mma] - 1, 1, -1}]];
The above is a generalized transpose of the list and see Leonid's excellent answer for an understanding of the second argument of Flatten
.
Verification:
You can check that the results are the same by comparing slices of the array in both MATLAB and Mathematica:
In MATLAB:
squeeze(mat(1,1,:,1,1))'
% ans = 1024 65 896 193
In Mathematica:
mma2mat[[1, 1, ;; , 1, 1]]
(* {1024., 65., 896., 193.} *)