Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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.

share|improve this question
2  
I know an uglier, slower or otherwise poorer solution: ImageRotate[Image[m], \[Pi]/2] // ImageData –  Sjoerd C. de Vries Mar 3 '12 at 0:09
    
@Sjoerd you should have saved that for the an obfuscated code contest. :-) –  Mr.Wizard Mar 3 '12 at 0:23
    
Table[Rotate[a // MatrixForm, k Degree], {k, {90, -90, 180}}] –  David Carraher Mar 3 '12 at 0:40
    
@David If you want to get away with that you'll need to counter-rotate the individual elements. :^) –  Mr.Wizard Mar 3 '12 at 0:46
3  
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] –  David Carraher Mar 3 '12 at 1:10
show 7 more comments

4 Answers 4

up vote 11 down vote accepted

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.

share|improve this answer
3  
+1 for Mathematica spelunking. –  Mr.Wizard Mar 3 '12 at 1:01
add comment

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}}}
share|improve this answer
    
BLeerghahahrhhggghghghhhh puajjj (+1 due to something) –  Rojo Mar 3 '12 at 2:57
    
@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. –  Mr.Wizard Mar 3 '12 at 6:57
add comment

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?

share|improve this answer
    
This is not really what I had in mind, but it is an interesting interpretation of my question. +1 –  Mr.Wizard Mar 4 '12 at 2:20
add comment

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

share|improve this answer
    
Much better than mine. You even got the braces right! –  David Carraher Mar 3 '12 at 1:14
    
@David hide this one behind ReadProtected and befuddle novice users to no end. –  Mr.Wizard Mar 3 '12 at 1:15
    
Good idea. I assume that ReadProtected hides the source code from prying eyes. –  David Carraher Mar 3 '12 at 1:35
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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