Search code examples
pythonnumpymatrixdiagonal

Reverse (flip) right-left(anti-)diagonals of a non-square numpy array


What I am after is Python code able to reverse the order of the values in each of the array anti-diagonals in a numpy array.

I have already tried various combinations of np.rot90, np.fliplr, np.transpose, np.flipud but none is able to give me the original shape of the 5x3 array with all the anti-diagonals reversed.

Any idea how to accomplish this?

Example:

[[ 1  2  4]
 [ 3  5  7]
 [ 6  8 10]
 [ 9 11 13]
 [12 14 15]]

Should become:

[[ 1  3  6]
 [ 2  5  9]
 [ 4  8 12]
 [ 7 11 14]
 [10 13 15]]

I suppose it must be easy, but somehow I have yet failed to find how to do it efficiently on arrays with millions of values.


Inspired by the already provided answers (status 2024-05-23 11:37 CET) and re-thinking what would be the most efficient way of getting the required transformation done it seems that giving a simple function taking two indices : iRow, jColumn of a value in an array and returning the required i,j indices to access the array as if it were flipped/reversed over the diagonals will provide fastest results. With such function for the over the diagonals flipped version of the array would be getting the right values without operating on the array as easy as in a trivial case of one-based and column/row based access to array values demonstrated below:

import numpy as np 
srcArr = np.array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12],
       [13, 14, 15, 16, 17, 18],
       [19, 20, 21, 22, 23, 24]])

def ijOfArrayValueGivenOneBasedColumnRowBasedIndices(i, j):
     return ( j - 1, i - 1 )
 
print( srcArr[
    ijOfArrayValueGivenOneBasedColumnRowBasedIndices(
        3,4)] ) # gives 21
print( srcArr[3,4] ) # gives 23

From this perspective the question comes down to providing a function

ijIndicesToSourceArray_gettingValueOfSourceArrayWithReversedRightLeftAntiDiagonalsAt(i,j,arrShapeRows,arrShapeColumns)


Solution

  • This one seems fairly fast, especially for wide matrices like your width=n=1920, height=m=1080 :

    def mirror(a):
        m, n = a.shape
        if m == n:
            return a.T.copy()
        if m > n:
            return mirror(a.T).T
    
        # Shear
        v = a.flatten()
        w = v[:-m].reshape((m, n-1))
        
        # Flip the parallelogram
        w[:, m-1:] = w[::-1, m-1:]
    
        # Flip the triangles
        t = np.vstack((w[:, :m-1].reshape((m-1, m)), v[-m:]))
        t = t.T
        w[:, :m-1] = t[:-1].reshape((m, m-1))
    
        # Write flipped parts back and unshear
        v[:-m] = w.ravel()
        v[-m:] = t[-1]
        return v.reshape((m, n))
    

    Attempt This Online!

    The idea: Slice/reshape the m×n matrix to an m×(n-1) matrix so that the parallelogram (green part) becomes a rectangle so we can just flip it upside down. For example:

    original

    Now reshape the m×n matrix to an m×(n-1) matrix (omit the black last m cells), which moves the yellow cells to the front of the next row, shifting the next row as needed:

    sheared

    Now we can easily flip the green parallelogram part. For the top-left and bottom-right triangles, reshape/shear the left part of this back and put the omitted cells under it:

    triangles

    This can now simply be transposed and written back.