27
$\begingroup$

Suppose I have an array, not necessarily square:

a = $\left( \begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{array} \right)$

I want to rotate it like one of these:

$\left( \begin{array}{ccc} 3 & 6 & 9 \\ 2 & 5 & 8 \\ 1 & 4 & 7 \end{array} \right)$     $\left( \begin{array}{ccc} 7 & 4 & 1 \\ 8 & 5 & 2 \\ 9 & 6 & 3 \end{array} \right)$     $\left( \begin{array}{ccc} 9 & 8 & 7 \\ 6 & 5 & 4 \\ 3 & 2 & 1 \end{array} \right)$

I have been using:

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

Reverse[a, {2}]\[Transpose]

Reverse[a\[Transpose], {2}]

Reverse[a, {1, 2}]

Is there a cleaner, faster, or otherwise better way to do it?

I am more interested in a general solution rather than a compiled solution for an integer array, but if you have a nice compiled approach please make it work on version 7.

$\endgroup$
12
  • 4
    $\begingroup$ I know an uglier, slower or otherwise poorer solution: ImageRotate[Image[m], \[Pi]/2] // ImageData $\endgroup$ Mar 3, 2012 at 0:09
  • $\begingroup$ @Sjoerd you should have saved that for the an obfuscated code contest. :-) $\endgroup$
    – Mr.Wizard
    Mar 3, 2012 at 0:23
  • $\begingroup$ Table[Rotate[a // MatrixForm, k Degree], {k, {90, -90, 180}}] $\endgroup$
    – DavidC
    Mar 3, 2012 at 0:40
  • $\begingroup$ @David If you want to get away with that you'll need to counter-rotate the individual elements. :^) $\endgroup$
    – Mr.Wizard
    Mar 3, 2012 at 0:46
  • 3
    $\begingroup$ Like this? g[list_, theta_] := Rotate[#, -theta Degree] & /@ list h[m_, theta_] := Rotate[MatrixForm[g[#, theta] & /@ m], theta Degree] h[a, 90] h[a, -90] h[a, 180] $\endgroup$
    – DavidC
    Mar 3, 2012 at 1:10

5 Answers 5

13
$\begingroup$

I realise that this doesn't fully answer the question, but for the special case of square matrices, there's already a suitable function: Image`MorphologicalOperationsDump`SquareMatrixRotate (which, no doubt, is how Sjoerd's suggestion works internally). This is undocumented, of course!

The implementation is the following (modulo some bugs I've fixed--i and j were not localized in the original, leading to problems if you want to rotate a matrix containing these symbols, there were no conditions on the arguments, and [admittedly, a minor point] the rotation matrix was numericized only after inversion, which is inefficient):

SquareMatrixRotate[mat_?MatrixQ, angle_?NumericQ] /; Equal @@ Dimensions[mat] :=
 Module[{
   inv, dim, ct,
   i, j,
   ii, jj
  },
  inv = Inverse@RotationMatrix@N[angle];
  dim = First@Dimensions[mat];
  ct = (dim + 1)/2;
  Table[
   {ii, jj} = MapThread[
     Clip[#1, {1, #2}, {1, 1}] &,
     {Round[inv.{i - ct, j - ct} + {ct, ct}], {dim, dim}}
   ];
   mat[[ii, jj]],
   {i, dim}, {j, dim}
  ]
 ]

Is it in any sense better than your existing methods? Well, you can rotate by an arbitrary angle, although the results may not make much sense for angles for which no "natural" rotation is possible. Other than that, I would say no: personally, I would have used Reverse as you did.

$\endgroup$
0
7
$\begingroup$

FindPermutation may be useful as part of a yet-to-be-found general method as follows (using your examples as templates for the three rotations):

  a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
  b = (Reverse[a, {2}] // Transpose );
  c = (Reverse[a // Transpose, {2}]);
  d = Reverse[a, {1, 2}];
  cntrclckws = FindPermutation[Flatten@a, Flatten@b];
  clckws = Cycles[Reverse /@ RotateLeft /@ cntrclckws[[1]]];
  flp = PermutationProduct[clckws, clckws]

With

  m = {{r, s, t}, {u, v, w}, {x, y, z}};

and

  {Permute[Flatten@m, cntrclckws] // Partition[#, 3] &,
   Permute[Flatten@m, clckws] // Partition[#, 3] &,
   Permute[Flatten@m, flp] // Partition[#, 3] &}

one can "rotate" m to :

 {{{t, w, z}, {s, v, y}, {r, u, x}}, 
   {{x, u, r}, {y, v, s}, {z, w, t}}, 
   {{z, y, x}, {w, v, u}, {t, s, r}}}
$\endgroup$
2
  • $\begingroup$ BLeerghahahrhhggghghghhhh puajjj (+1 due to something) $\endgroup$
    – Rojo
    Mar 3, 2012 at 2:57
  • $\begingroup$ @Rojo um... WHAT? kguler I actually was looking for a "better" method as there are so many functions in Mathematica that it seemed reasonable I have overlooked one that does exactly this, perhaps with some obscure option or by clever usage. $\endgroup$
    – Mr.Wizard
    Mar 3, 2012 at 6:57
6
$\begingroup$

I don't think of this as rotating in the sense of an angle, rather, the sequence of positions around the matrix. So I came up with a rather different solution.

First let's create the simple case:

test[3, 3] = Array[a, {3, 3}]

{{a[1, 1], a[1, 2], a[1, 3]}, {a[2, 1], a[2, 2], a[2, 3]}, {a[3, 1], a[3, 2], a[3, 3]}}

This is the list of positions that rotates

snake = Join[Table[{1, i}, {i, 3}], {{2, 3}}, 
  Table[{3, i}, {i, 3, 1, -1}], {{2, 1}}]

{{1, 1}, {1, 2}, {1, 3}, {2, 3}, {3, 3}, {3, 2}, {3, 1}, {2, 1}}

RotateLeft[snake, 1]

{{1, 2}, {1, 3}, {2, 3}, {3, 3}, {3, 2}, {3, 1}, {2, 1}, {1, 1}}

output = test[3, 3];

ReplacePart[output, 
  Thread[RotateLeft[snake, 
     1] -> (test[3, 3][[Sequence @@ #]] & /@ snake)] ] // MatrixForm

$$\left( \begin{array}{ccc} a[2,1] & a[1,1] & a[1,2] \\ a[3,1] & a[2,2] & a[1,3] \\ a[3,2] & a[3,3] & a[2,3] \\ \end{array} \right)$$

Demonstrating the cases asked for in the question:

aa = Partition[Range[9], 3]; 

rotateMatrix[aa, -2] // MatrixForm

$$\left( \begin{array}{ccc} 3 & 6 & 9 \\ 2 & 5 & 8 \\ 1 & 4 & 7 \\ \end{array} \right)$$

rotateMatrix[aa, 2] // MatrixForm

$$\left( \begin{array}{ccc} 7 & 4 & 1 \\ 8 & 5 & 2 \\ 9 & 6 & 3 \\ \end{array} \right)$$

rotateMatrix[aa, 4] // MatrixForm

$$\left( \begin{array}{ccc} 9 & 8 & 7 \\ 6 & 5 & 4 \\ 3 & 2 & 1 \\ \end{array} \right)$$

We can generalise this to the case where the matrix has either three rows or three columns pretty easily.

rotateMatrix[a_?MatrixQ, j_Integer] /; Length[a] == 3 || Length[a[[1]]] == 3 :=
  Module[{output = a, m = Length[a], n = Length[a[[1]]], snake},
  snake = Join[Table[{1, i}, {i, n}], Table[{i, n}, {i, 2, m - 1}], 
    Table[{m, i}, {i, n, 1, -1}], Table[{i, 1}, {i, m - 1, 2, -1}]];
  ReplacePart[output, 
   Thread[RotateLeft[snake, j] -> (a[[Sequence @@ #]] & /@ snake)] ]
  ]

rotateMatrix[test[3, 3], 1] // MatrixForm

$$\left( \begin{array}{ccc} a[2,1] & a[1,1] & a[1,2] \\ a[3,1] & a[2,2] & a[1,3] \\ a[3,2] & a[3,3] & a[2,3] \\ \end{array} \right) $$

test[3, 4] = Array[b, {3, 4}]

{{b[1, 1], b[1, 2], b[1, 3], b[1, 4]}, {b[2, 1], b[2, 2], b[2, 3], b[2, 4]}, {b[3, 1], b[3, 2], b[3, 3], b[3, 4]}}

It works for both positive (rotate clockwise) and negative (rotate anti-clockwise) integers as the second argument.

rotateMatrix[test[3, 4], -2] // MatrixForm

$$\left( \begin{array}{cccc} b[1,3] & b[1,4] & b[2,4] & b[3,4] \\ b[1,2] & b[2,2] & b[2,3] & b[3,3] \\ b[1,1] & b[2,1] & b[3,1] & b[3,2] \\ \end{array} \right) $$

It would be possible to generalise this further to cases where neither the number of rows nor the number of columns is 3. One would have to specify what happens to the "inner rings" of positions that rotate. For example, in a 5 by 5 matrix, does the inner ring rotate three-fifths of the number of positions that the outer ring does?

$\endgroup$
1
  • $\begingroup$ This is not really what I had in mind, but it is an interesting interpretation of my question. +1 $\endgroup$
    – Mr.Wizard
    Mar 4, 2012 at 2:20
5
$\begingroup$

Quarter turn, where m any matrix.

Counterclockwise (positive direction):

Transpose @ (Reverse /@ m)

Clockwise (negative direction):

Reverse /@ (Transpose @ m)
$\endgroup$
2
  • $\begingroup$ Nice & simple answer. For some humor, your first entry is equivalent to: Reverse@Transpose@a or Transpose@a // Reverse. And your second entry is equivalent to: Transpose@Reverse@a or Reverse@a // Transpose ;D $\endgroup$ Feb 27, 2020 at 20:09
  • $\begingroup$ We can do the complete D4 group! Module[{i=Identity, r=Reverse, s=Map[Reverse], t=Transpose}, MatrixForm /@ Through[{i, r@*t, r@*s, t@*r, r, s, t, r@*t@*r} @ {{a, b}, {c, d}}]] $\endgroup$ May 28, 2020 at 5:15
4
$\begingroup$

I liked David's humorous method enough that I wondered what it would take to make it convincing. This is what I came up with:

a = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

Table[
  DisplayForm @
   RowBox[{"(", "\[NoBreak]", 
     Rotate[Map[Rotate[#, -k] &, a, {2}] // Grid, k], 
       "\[NoBreak]", ")"}],
  {k, {90 Degree, -90 Degree, 180 Degree}}
] // Row

Mathematica graphics

$\endgroup$
3
  • $\begingroup$ Much better than mine. You even got the braces right! $\endgroup$
    – DavidC
    Mar 3, 2012 at 1:14
  • $\begingroup$ @David hide this one behind ReadProtected and befuddle novice users to no end. $\endgroup$
    – Mr.Wizard
    Mar 3, 2012 at 1:15
  • $\begingroup$ Good idea. I assume that ReadProtected hides the source code from prying eyes. $\endgroup$
    – DavidC
    Mar 3, 2012 at 1:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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