Home > Software engineering >  Numpy - get "corner" of nd array
Numpy - get "corner" of nd array

Time:02-04

I want to get the upper "corner" or hypertriangle of a fully symmetric numpy nd array. In other words, i want to slice the array along all its diagonals (with optional offset) and take only the remaining triangle.

# with a 3d array and 0 offset, 
a = np.arange(5)
arr=a[:,None,None]   a[None,:,None]   a[None,None,:] 

upper_corner(arr,offset=0)

Expected output:

array([[[ 0,  1,  2,  3,  4],
        [ 0,  2,  3,  4,  5],
        [ 0,  0,  4,  5,  6],
        [ 0,  0,  0,  6,  7],
        [ 0,  0,  0,  0,  8]],

       [[ 0,  2,  3,  4,  5],
        [ 0,  0,  4,  5,  6],
        [ 0,  0,  0,  6,  7],
        [ 0,  0,  0,  0,  8],
        [ 0,  0,  0,  0,  0]],

       [[ 0,  0,  4,  5,  6],
        [ 0,  0,  0,  6,  7],
        [ 0,  0,  0,  0,  8],
        [ 0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0]],

       [[ 0,  0,  0,  6,  7],
        [ 0,  0,  0,  0,  8],
        [ 0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0]],

       [[ 0,  0,  0,  0,  8],
        [ 0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0],
        [ 0,  0,  0,  0,  0]]])

Is there an indexing or slicing solution for this in n-dimensions?

CodePudding user response:

np.triu (triangle upper) can almost do this in one go:

>>> np.triu(arr)
array([[[ 0,  1,  2,  3,  4],
        [ 0,  2,  3,  4,  5],
        [ 0,  0,  4,  5,  6],
        [ 0,  0,  0,  6,  7],
        [ 0,  0,  0,  0,  8]],

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

       [[ 2,  3,  4,  5,  6],
        [ 0,  4,  5,  6,  7],
        [ 0,  0,  6,  7,  8],
        [ 0,  0,  0,  8,  9],
        [ 0,  0,  0,  0, 10]],

       [[ 3,  4,  5,  6,  7],
        [ 0,  5,  6,  7,  8],
        [ 0,  0,  7,  8,  9],
        [ 0,  0,  0,  9, 10],
        [ 0,  0,  0,  0, 11]],

       [[ 4,  5,  6,  7,  8],
        [ 0,  6,  7,  8,  9],
        [ 0,  0,  8,  9, 10],
        [ 0,  0,  0, 10, 11],
        [ 0,  0,  0,  0, 12]]])

But since you seem to want the triangle to move incrementally each time, you can generate an appropriate mask like this:

mask = np.array([np.triu(np.ones(arr.shape[1:], dtype=int), i) for i in range(arr.shape[0])])

and then multiply the original array by the mask:

>>> corners = arr * mask
>>> corners
array([[[0, 1, 2, 3, 4],
        [0, 2, 3, 4, 5],
        [0, 0, 4, 5, 6],
        [0, 0, 0, 6, 7],
        [0, 0, 0, 0, 8]],

       [[0, 2, 3, 4, 5],
        [0, 0, 4, 5, 6],
        [0, 0, 0, 6, 7],
        [0, 0, 0, 0, 8],
        [0, 0, 0, 0, 0]],

       [[0, 0, 4, 5, 6],
        [0, 0, 0, 6, 7],
        [0, 0, 0, 0, 8],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0]],

       [[0, 0, 0, 6, 7],
        [0, 0, 0, 0, 8],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 8],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0]]])

CodePudding user response:

You can try the following:

import numpy as np
 
arr = np.arange(4**3).reshape(4, 4, 4)
print(arr)

It gives:

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

 [[16 17 18 19]
  [20 21 22 23]
  [24 25 26 27]
  [28 29 30 31]]

 [[32 33 34 35]
  [36 37 38 39]
  [40 41 42 43]
  [44 45 46 47]]

 [[48 49 50 51]
  [52 53 54 55]
  [56 57 58 59]
  [60 61 62 63]]]

Get the corner:

x, y, z = np.ogrid[0:arr.shape[0], 0:arr.shape[1], 0:arr.shape[2]]
arr[x - z   y > 0] = 0
print(arr)

Result:

[[[ 0  1  2  3]
  [ 0  5  6  7]
  [ 0  0 10 11]
  [ 0  0  0 15]]

 [[ 0 17 18 19]
  [ 0  0 22 23]
  [ 0  0  0 27]
  [ 0  0  0  0]]

 [[ 0  0 34 35]
  [ 0  0  0 39]
  [ 0  0  0  0]
  [ 0  0  0  0]]

 [[ 0  0  0 51]
  [ 0  0  0  0]
  [ 0  0  0  0]
  [ 0  0  0  0]]]
  •  Tags:  
  • Related