How do you rotate elements in a 2D numpy array by 'n' units?

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

Rotation by 1 unit should give:

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

Basically, I want to shift each circular layer in the array by 'n' units.

I looked at numpy.roll but couldn't figure out to use it for this use case. I cannot use image rotation routines like scipy.ndimage.interpolation.rotate as they change the shape and not quite achieve the desired result.

Edit:

For the 4 X 4 matrix:

x = [[a, b, c, d],
     [e, f, g, h],
     [i, j, k, l],
     [m, n, o, p]]

Rotation by 1 unit should give:

x = [[e, a, b, c],
     [i, j, f, d],
     [m, k, g, h],
     [n, o, p, l]]

Edit:

Adding some clarifications on how this works for arbitrary sizes.

For a N X N matrix rotated by 1 unit, the outer 'ring' is first shifted by 1. Same logic is followed the remaining 'inner' (N-2) X (N-2) matrix recursively.


Show source
| numpy   | python   | arrays   2017-01-06 10:01 3 Answers

Answers to How do you rotate elements in a 2D numpy array by 'n' units? ( 3 )

  1. 2017-01-06 11:01

    For a 3x3 array, you can accomplish this with np.roll and ndarray.flat:

    >>> x = np.arange(1, 10).reshape((3, 3))
    >>> x
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    >>> i = np.array([0, 1, 2, 5, 8, 7, 6, 3])  # Indices in circular order
    >>> x.flat[i]
    array([1, 2, 3, 6, 9, 8, 7, 4])
    

    Rotate by 1 unit:

    >>> x.flat[i] = np.roll(x.flat[i], 1)  # Rotate indices and reassign
    >>> x
    array([[4, 1, 2],
           [7, 5, 3],
           [8, 9, 6]])
    

    Rotate by 4 units:

    >>> x.flat[i] = np.roll(x.flat[i], 4)
    >>> x
    array([[9, 8, 7],
           [6, 5, 4],
           [3, 2, 1]])
    

    For the 4x4 case, I still need some clarity on whether each "circle" needs to be rotated at different "speeds" according to their length...

  2. 2017-01-06 13:01

    Here's an approach assuming you are looking to rotate such that the amount of shift is constant across slices, where by slice we mean the outermost layer of elements directed outwards from the center -

    def outer_slice(x):
        return np.r_[x[0],x[1:-1,-1],x[-1,:0:-1],x[-1:0:-1,0]]
    
    def rotate_steps(x, shift):
        out = np.empty_like(x)
        N = x.shape[0]
        idx = np.arange(x.size).reshape(x.shape)
        for n in range((N+1)//2):
            sliced_idx = outer_slice(idx[n:N-n,n:N-n])
            out.ravel()[sliced_idx] = np.roll(np.take(x,sliced_idx), shift)
        return out
    

    Sample runs

    Case #1 (3 x 3 array) :

    In [444]: x
    Out[444]: 
    array([[24, 85, 97],
           [51, 33, 11],
           [86, 38, 33]])
    
    In [445]: rotate_steps(x,shift=1)
    Out[445]: 
    array([[51, 24, 85],
           [86, 33, 97],
           [38, 33, 11]])
    

    Case #2 (4 x 4 array) :

    In [447]: x
    Out[447]: 
    array([[11, 70, 28, 13],
           [44, 41, 17, 82],
           [47, 32, 89, 25],
           [32, 20, 67, 98]])
    
    In [448]: rotate_steps(x,shift=1)
    Out[448]: 
    array([[44, 11, 70, 28],
           [47, 32, 41, 13],
           [32, 89, 17, 82],
           [20, 67, 98, 25]])
    
    In [449]: rotate_steps(x,shift=2)
    Out[449]: 
    array([[47, 44, 11, 70],
           [32, 89, 32, 28],
           [20, 17, 41, 13],
           [67, 98, 25, 82]])
    
  3. 2017-01-06 21:01

    You're applying a permutation on the matrix. Permutations are usually represented by vectors, (i-->p[i]), and applied on vectors. You can represent a permutation of a matrix in a matrix if you want, the permutation will be a matrix of pairs, so that the element at (i,j) moves to m[i,j] .

    Once you build the matrix, it's just a matter of a simple loop to apply the permutation.

Leave a reply to - How do you rotate elements in a 2D numpy array by 'n' units?

◀ Go back