I am in the beginning phases of learning NumPy. I have a Numpy array of 3x3 matrices. I would like to create a new array where each of those matrices is rotated 90 degrees. I've studied this answer but I still can't figure out what I am doing wrong.
import numpy as np
# 3x3
m = np.array([[1,2,3], [4,5,6], [7,8,9]])
# array of 3x3
a = np.array([m,m,m,m])
# rotate a single matrix counter-clockwise
def rotate90(x):
return np.rot90(x)
# function that can be called on all elements of an np.array
# Note: I've tried different values for otypes= without success
f = np.vectorize(rotate90)
result = f(a)
# ValueError: Axes=(0, 1) out of range for array of ndim=0.
# The error occurs in NumPy's rot90() function.
Note: I realize I could do the following but I'd like to understand the vectorized option.
t = np.array([ np.rot90(x, k=-1) for x in a])
Normally
np.vectorize
is used to apply a scalar (Python, non-numpy) function to all elements of an array, or set of arrays. There's a note that's often overlooked:This multiplies each element of
m
by 2, taking care of the looping paper-work for us.Better yet, when given several arrays, it broadcasts (a generalization of 'outer product').
This feeds
(x,y)
scalar tuples to the lambda for all combinations of a (3,) array broadcasted against a (2,1) array, resulting in a (2,3) array. It can be viewed as a broadcasted extension ofmap
.The problem with
np.vectorize(np.rot90)
is thatrot90
takes a 2d array, butvectorize
will feed it scalars.However I see in the docs that for
v1.12
they've added a signature parameter. This is the first time I used it.Your problem - apply
np.rot90
to 2d elements of a 3d array:While you could describe this
a
as an array of 2d arrays, it's better to think of it as a 3d array of integers. That's how thenp.vectorize(myfun)(a)
sees it, givingmyfun
each number.Applied to a 2d
m
:With the Python work horse, the list comprehension:
The result is a list, but we could wrap that in
np.array
.Python
map
does the same thing.The comprehension and map both iterate on the 1st dimension of a, action on the resulting 2d element.
vectorize
withsignature
:The
signature
tells it to pass a 2d array and expect back a 2d array. (I should explore howsignature
plays with theotypes
parameter.)Some quick time comparisons:
So for a small array, the Python list methods are faster, by quite a bit. Sometimes,
numpy
approaches do better with larger arrays, though I doubt in this case.rot90
with the axes parameter is even better, and will do well with larger arrays:Looking at the
np.rot90
code, I see that it is just doingnp.flip
(reverse) andnp.transpose
, in various combinations depending on thek
. In effect for this case it is doing:(this is even faster than
rot90
.)I suspect
vectorize
with thesignature
is doing something like:np.stack(f(b))
will convert the object array into a 3d array like the other code.frompyfunc
is the underlying function forvectorize
, and returns an array of objects. Here I create an array like youra
except it is 1d, containing multiplem
arrays. It is an array of arrays, as opposed to a 3d array.No need to do the rotations individually:
numpy
has a builtinnumpy.rot90(m, k=1, axes=(0, 1))
function. By default the matrix is thus rotate over the first and second dimension.If you want to rotate one level deeper, you simply have to set the axes over which rotation happens, one level deeper (and optionally swap them if you want to rotate in a different direction). Or as the documentation specifies:
So we rotate over the y and z plane (if we label the dimensions x, y and z) and thus we either specify
(2,1)
or(1,2)
.All you have to do is set the
axes
correctly, when you want to rotate to the right/left:This will rotate all matrices, like:
Or if you want to rotate to the left:
Note that you can only specify the
axes
from numpy 1.12 and (probably) future versions.