7
$\begingroup$

Given a 2D matrix I would like to make a list with elements consisting of positions of the matrix elements and the elements themselves. Let the matrix be

mat = {{a, b, c}, {s, v, t}, {k, l, m}};

then the aim is to obtain this:

(*   {{1, 1, a}, {1, 2, b}, {1, 3, c}, {2, 1, s}, {2, 2, v}, {2, 3, t}, {3,
   1, k}, {3, 2, l}, {3, 3, m}} *)

The element a of the initial matrix has the list coordinates {1,1} and, therefore, is transformed into {1,1,a}, and so on. The solution is easy to obtain with the above matrix:

   Flatten[Map[{Position[mat, #], #} &, 
   mat, {2}] /. {{{a_, b_}}, c_} -> {a, b, c}, 1]

(*  {{1, 1, a}, {1, 2, b}, {1, 3, c}, {2, 1, s}, {2, 2, v}, {2, 3, t}, {3,
   1, k}, {3, 2, l}, {3, 3, m}}  *)

The problem with this solution arises in the case, when not all elements in the initial matrix are different.

Any idea?

PS. One can do, of course, straightforwardly:

Flatten[Table[{i, j, mat[[i, j]]}, {i, 1, Dimensions[mat][[1]]}, {j, 
   1, Dimensions[mat][[1]]}], 1]

but I am looking for a faster approach.

$\endgroup$
  • 5
    $\begingroup$ this is quite short and straightforward: MapIndexed[{Sequence @@ #2, #1} &, mat, {2}]. But what exactly would make you consider an approach "faster"? $\endgroup$ – Albert Retey Dec 8 '15 at 12:54
  • 2
    $\begingroup$ Obfuscation frenzy: Flatten /@ List @@@ ArrayRules@(SparseArray@mat) // Most $\endgroup$ – Yves Klett Dec 8 '15 at 13:07
  • 1
    $\begingroup$ Interesting that you mention that MapIndexed is faster. For me the table approach also seems to be slightly faster, at least if one keeps the Sequence for flattening the indices. For the Table approach I think you'd need Dimensions[mat][[2]] for the j loop counter for nonsquare matrices? $\endgroup$ – Albert Retey Dec 8 '15 at 13:28
  • 1
    $\begingroup$ I added the performance-tuning tag, since you seemed to be asking for speed. Feel free to roll back, although I'm pretty sure some of our most expert Mathematica users monitor performance-tuning, so it's perhaps a good way to attract attention? :) $\endgroup$ – march Dec 8 '15 at 16:50
  • 1
    $\begingroup$ I would have done @Albert's snippet as MapIndexed[Append[#2, #1] &, mat, {2}]. $\endgroup$ – J. M. is away Dec 9 '15 at 8:07
5
$\begingroup$

Optimized Table wins, but in general setting Array is faster.

Array approach is inspired by the @LeonidShifrin vintage comment.

This version is fast and robust for 2D. For more dimentions needs tuning.

Array[{#1, #2, mat[[#1, #2]]} &, Dimensions[mat]]

The following one looks shortest and universal but indeed slow (due to dual Sequence burden; note that use of only one ## in any position does not diminish the speed seriously).

Array[{##, mat[[##]]} &, Dimensions[mat]]

For the small 2D problem at hand it is fine though:

mat = {{a, b, c}, {s, v, t}, {k, l, m}};
Flatten[Array[{##, mat[[##]]} &, {3, 3}], 1]

{{1, 1, a}, {1, 2, b}, {1, 3, c}, {2, 1, s}, {2, 2, v}, {2, 3, t}, {3, 1, k}, {3, 2, l}, {3, 3, m}}}

MapIndexed approaches are also elegant but not that fast for long arrays and should as well be tuned a bit for multi-dimensional arrays.

Addenum Some timing comparisons:

Sample:

Clear[longMat];
longMat = RandomInteger[10, {1000, 1000}];

Optimized conditions (formula has exact size of matrix):

Table[{i, j, longMat[[i, j]]}, {i, 1000}, {j, 1000}]; // Timing
(* {0.078125, Null} *)

Array[{#1, #2, longMat[[#1, #2]]} &, {1000, 1000}]; // Timing
(* {0.09375, Null} *)

-----------------------------------

MapIndexed[Append[#2, #1] &, longMat, {2}]; // Timing
(* {1.0625, Null} *)

Array[{##, longMat[[##]]} &, {1000, 1000}]; // Timing
(* {1.625, Null} *)

MapIndexed[{Sequence @@ #2, #1} &, longMat, {2}]; // Timing
(* {1.96875, Null} *)

Unknown 2D Matrix:

Array[{#1, #2, longMat[[#1, #2]]} &, Dimensions[longMat]]; // Timing
(* {0.109375, Null} *)

Table[{i, j, longMat[[i, j]]}, {i, Dimensions[longMat][[1]]}, {j, 
    Dimensions[longMat][[2]]}]; // Timing
(* {0.96875, Null} *) ----> "much slower"

----------------------------------------

MapIndexed[Append[#2, #1] &, longMat, {Length[Dimensions[longMat]]}]; // Timing
(* {1.09375, Null} *)

Array[{##, longMat[[##]]} &, Dimensions[longMat]]; // Timing
(* {1.64063, Null} *)

MapIndexed[{Sequence @@ #2, #1} &, longMat, {Length[Dimensions[longMat]]}]; // Timing
(* {1.98438, Null} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.