Using numpy.vectorize () to rotate all elements of a numPy array

I am in the early stages of learning NumPy. I have a Numpy array of 3x3 matrices. I would like to create a new array where each of these matrices will be rotated 90 degrees. I have looked into this answer , but I still cannot 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 rot90() function.

      

Note. I understand that I can do the following, but I would like to understand the option with the vectorized version.

t = np.array([ np.rot90(x, k=-1) for x in a])

      

+3


source to share


2 answers


No need to do rotations separately: it numpy

has a built-in function . By default, the matrix is ​​rotated in the first and second dimensions. numpy.rot90(m, k=1, axes=(0, 1))

If you want to rotate one level deeper, you just need to set the axes along which the rotation occurs one level deeper (and, if necessary, change them if you want to rotate in a different direction). Or, as the documentation states:

axes: (2,) array_like

    The array rotates in the plane defined by the Axis. The axes must be different.

So we are rotating in the y and z plane (if we denote the x, y and z dimensions), and therefore we will either indicate (2,1)

or (1,2)

.

All you have to do is set it axes

correctly if you want to rotate to the right / left :

np.rot90(a,axes=(2,1)) # right
np.rot90(a,axes=(1,2)) # left
      



This will rotate all matrices, for example:

>>> np.rot90(a,axes=(2,1))
array([[[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]],

       [[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]],

       [[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]],

       [[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]]])

      

Or, if you want to rotate to the left :

>>> np.rot90(a,axes=(1,2))
array([[[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]],

       [[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]],

       [[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]],

       [[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]]])

      

Note that you can only specify axes

from numpy 1.12 and (possibly) future versions .

+5


source


Typically np.vectorize

used to apply a scalar (Python, not numpy) function to all elements of an array or a set of arrays. There's a side note that is often ignored:

The function is vectorize

provided primarily for convenience, not for presentation. The implementation is essentially a for loop.

In [278]: m = np.array([[1,2,3],[4,5,6]])
In [279]: np.vectorize(lambda x:2*x)(m)
Out[279]: 
array([[ 2,  4,  6],
       [ 8, 10, 12]])

      

This multiplies each element m

by 2, taking care of the paperwork for us.

Better yet, when given multiple arrays, it broadcasts (generalization of "external product").

In [280]: np.vectorize(lambda x,y:2*x+y)(np.arange(3), np.arange(2)[:,None])
Out[280]: 
array([[0, 2, 4],
       [1, 3, 5]])

      

This causes (x,y)

scalar tuples to be loaded into the lambda for all combinations of array (3,) passed over array (2,1), resulting in array (2,3). It can be thought of as a broadcast extension map

.

The problem with np.vectorize(np.rot90)

is that it rot90

accepts a 2d array, but vectorize

will feed it scalars.

However, in the docs, I see that v1.12

they added a signature parameter for. This is the first time I have used it.

Your problem - apply np.rot90

to 2d elements of a 3D array:

In [266]: m = np.array([[1,2,3],[4,5,6]])
In [267]: a = np.stack([m,m])
In [268]: a
Out[268]: 
array([[[1, 2, 3],
        [4, 5, 6]],

       [[1, 2, 3],
        [4, 5, 6]]])

      

As long as you can describe this a

as an array of 2d arrays, it is better to think of it as a 3D array of integers. The way he sees np.vectorize(myfun)(a)

, giving myfun

each number.

Applies to 2d m

:

In [269]: np.rot90(m)
Out[269]: 
array([[3, 6],
       [2, 5],
       [1, 4]])

      

With a Python workhorse, a list is understood:

In [270]: [np.rot90(i) for i in a]
Out[270]: 
[array([[3, 6],
        [2, 5],
        [1, 4]]), array([[3, 6],
        [2, 5],
        [1, 4]])]

      

The result is a list, but we can wrap that in np.array

.

Python map

does the same.



In [271]: list(map(np.rot90, a))
Out[271]: 
[array([[3, 6],
        [2, 5],
        [1, 4]]), array([[3, 6],
        [2, 5],
        [1, 4]])]

      

Understanding and displaying iteration over the 1st dimension a, actions on the resulting 2d element.

vectorize

with signature

:

In [272]: f = np.vectorize(np.rot90, signature='(n,m)->(k,l)')
In [273]: f(a)
Out[273]: 
array([[[3, 6],
        [2, 5],
        [1, 4]],

       [[3, 6],
        [2, 5],
        [1, 4]]])

      

signature

tells it to pass a 2d array and expects a 2d array. (I have to learn how to signature

play with the parameter otypes

.)

Some quick time comparisons:

In [287]: timeit np.array([np.rot90(i) for i in a])
10000 loops, best of 3: 40 µs per loop
In [288]: timeit np.array(list(map(np.rot90, a)))
10000 loops, best of 3: 41.1 µs per loop
In [289]: timeit np.vectorize(np.rot90, signature='(n,m)->(k,l)')(a)
1000 loops, best of 3: 234 µs per loop
In [290]: %%timeit f=np.vectorize(np.rot90, signature='(n,m)->(k,l)')
     ...: f(a)
     ...: 
1000 loops, best of 3: 196 µs per loop

      

So, for a small array, Python list methods are faster, quite a lot. Sometimes approaches are numpy

better for large arrays, although I doubt it.

rot90

with the axes parameter is even better and will work well with large arrays:

In [292]: timeit np.rot90(a,axes=(1,2))
100000 loops, best of 3: 15.7 µs per loop

      

Looking at the code np.rot90

, I can see that it simply does np.flip

(reverse) and np.transpose

in various combinations depending on k

. In fact, for this case, it does:

In [295]: a.transpose(0,2,1)[:,::-1,:]
Out[295]: 
array([[[3, 6],
        [2, 5],
        [1, 4]],

       [[3, 6],
        [2, 5],
        [1, 4]]])

      

(it's even faster than rot90

.)


I suspect that vectorize

c signature

does something like:

In [301]: b = np.zeros(2,dtype=object)
In [302]: b[...] = [m,m]
In [303]: f = np.frompyfunc(np.rot90, 1,1)
In [304]: f(b)
Out[304]: 
array([array([[3, 6],
       [2, 5],
       [1, 4]]),
       array([[3, 6],
       [2, 5],
       [1, 4]])], dtype=object)

      

np.stack(f(b))

converts an array of objects to a 3D array like other code.

frompyfunc

is the main function for vectorize

and returns an array of objects. Here I am creating an array similar to yours a

except for 1d containing multiple arrays m

. It is an array of arrays, not a 3D array.

+1


source







All Articles