2

Suppose

A = np.array([1,0,2,0,0,0,0],[1,2,0,3,5,0,0],[5,6,7,0,9,5,10]) 

From A I'm trying to create B such that:

B = np.array([2,0,1,0,0,0,0],[5,3,0,2,1,0,0],[10,5,9,0,7,6,5])

In other words B is A reversed with the leading zeros rotated to the back. Is there a vectorized approach to accomplish this?

(B is created so that I can vectorize the summation of the integer values in A with their reflection across the zero off diagonal) e.g. A[1][0]+A[1][4], A[1][1]+A[1][3] = (B+A)[0:2]

The only thing I think of is to try something like this.

First I reversed A

B = A.copy()
B = B[...,::-1]

Which yields

B
Out[185]: 
array([[ 0,  0,  0,  0,  2,  0,  1],
       [ 0,  0,  5,  3,  0,  2,  1],
       [10,  5,  9,  0,  7,  6,  5]])

then I looped through each row with the following.

for i in range(0,B.shape[0]):
    a=np.trim_zeros(B[i])
    B[i]=np.pad(a,(0,B.shape[1]-a.shape[0])

B
Out[]: 
array([[ 2,  0,  1,  0,  0,  0,  0],
       [ 5,  3,  0,  2,  1,  0,  0],
       [10,  5,  9,  0,  7,  6,  5]])

But the actual data set may have rows and columns that are orders of magnitude larger. Is there a vectorized solution to this problem.

phntm
  • 511
  • 2
  • 11

1 Answers1

2

Here's a vectorized one with masking -

# nonzeros mask
nnz = A!=0

# Mask surrounded by nonzeros
max_accum = np.maximum.accumulate
island = max_accum(nnz,axis=1) & max_accum(nnz[:,::-1],axis=1)[:,::-1]

# Setup o/p array. Extract flipped A elements within each island per row and 
# assign into sorted mask i.e. brought to front mask places in o/p
out = np.zeros_like(A)
out[np.sort(island, axis=1)[:,::-1]] = A[:,::-1][island[:,::-1]]

Explanation

Input array (for ref and a different one to handle corner case and better to explain) :

In [147]: A
Out[147]: 
array([[ 1,  0,  2,  0,  0,  0,  0],
       [ 0,  0,  0,  2,  4,  0,  6],
       [ 5,  6,  7,  0,  9,  5, 10]])

Island mask of nonzeros-surrounded :

Forward accumulated mask

In [148]: max_accum(nnz,axis=1)
Out[148]: 
array([[ True,  True,  True,  True,  True,  True,  True],
       [False, False, False,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True]])

Backward accumulated mask with flipping nnz mask, performing forward accumulation, flipping back

In [149]: max_accum(nnz[:,::-1],axis=1)[:,::-1]
Out[149]: 
array([[ True,  True,  True, False, False, False, False],
       [ True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True]])

Final combined mask to give us island

In [150]: max_accum(nnz,axis=1) & max_accum(nnz[:,::-1],axis=1)[:,::-1]
Out[150]: 
array([[ True,  True,  True, False, False, False, False],
       [False, False, False,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True]])

Selection and assignment :

In [159]: A
Out[159]: 
array([[ 1,  0,  2,  0,  0,  0,  0],
       [ 0,  0,  0,  2,  4,  0,  6],
       [ 5,  6,  7,  0,  9,  5, 10]])

In [160]: island
Out[160]: 
array([[ True,  True,  True, False, False, False, False],
       [False, False, False,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True]])

In [161]: A[:,::-1]
Out[161]: 
array([[ 0,  0,  0,  0,  2,  0,  1],
       [ 6,  0,  4,  2,  0,  0,  0],
       [10,  5,  9,  0,  7,  6,  5]])

In [162]: island[:,::-1]
Out[162]: 
array([[False, False, False, False,  True,  True,  True],
       [ True,  True,  True,  True, False, False, False],
       [ True,  True,  True,  True,  True,  True,  True]])

Hence, select :

In [165]: A[:,::-1][island[:,::-1]]
Out[165]: array([ 2,  0,  1,  6,  0,  4,  2, 10,  5,  9,  0,  7,  6,  5])

To select brought-to-front mask, sort island mask, which selects o/p places to be assigned into :

In [163]: np.sort(island, axis=1)[:,::-1]
Out[163]: 
array([[ True,  True,  True, False, False, False, False],
       [ True,  True,  True,  True, False, False, False],
       [ True,  True,  True,  True,  True,  True,  True]])

Then, assign those A selected ones into output with sorted mask :

In [166]: out
Out[166]: 
array([[ 2,  0,  1,  0,  0,  0,  0],
       [ 6,  0,  4,  2,  0,  0,  0],
       [10,  5,  9,  0,  7,  6,  5]])
Divakar
  • 218,885
  • 19
  • 262
  • 358