amirhe
amirhe

Reputation: 2341

Fill non rectangular slice of numpy array

Is it possible to do any operation for example fill in a non-rectangular slice of the NumPy array with NumPy itself?

For example imaging, we have a NumPy array in which the first index in each row represents the limit. and we want to fill out of limit items with the latest element inside the limit.

mat = np.array([
    [0, 4, 4, 4, 4],
    [1, 4, 4, 4, 4],
    [3, 4, 5, 6, 6],
    [2, 4, 5, 5, 5],
])

index = mat[:, 0] # array([0, 1, 3, 2])

Is it possible to fill each row from index+1 to its end with the corresponding index+1 in its row?

mat[0, 1:] = mat[0, 1] # 4
mat[1, 2:] = mat[1, 2] # 4
mat[2, 4:] = mat[2, 4] # 6
mat[3, 3:] = mat[3, 3] # 5
mat[: index+1:] = mat[: index+1]
TypeError: only integer scalar arrays can be converted to a scalar index

NOTE: I know it seems too specific, but I can not explain what is in mind easier without the example.

Upvotes: 1

Views: 430

Answers (1)

hpaulj
hpaulj

Reputation: 231385

In [134]: mat = np.array([
     ...:     [0, 4, 4, 4, 4],
     ...:     [1, 4, 4, 4, 4],
     ...:     [3, 4, 5, 6, 6],
     ...:     [2, 4, 5, 5, 5],
     ...: ])
     ...: 
In [135]: index = mat[:, 0]+1          # add the 1 here
In [136]: for i,v in enumerate(index):
     ...:     mat[i,v:] = mat[i,v]
     ...: 
     ...: 
In [137]: mat
Out[137]: 
array([[0, 4, 4, 4, 4],
       [1, 4, 4, 4, 4],
       [3, 4, 5, 6, 6],
       [2, 4, 5, 5, 5]])

construct a boolean mask (as suggested for padding problems)

In [141]: np.arange(5)>=index[:,None]
Out[141]: 
array([[False,  True,  True,  True,  True],
       [False, False,  True,  True,  True],
       [False, False, False, False,  True],
       [False, False, False,  True,  True]])

This is True where we want to fill in values. But the next trick is to create an array of values like this:

In [142]: mat[_]
Out[142]: array([4, 4, 4, 4, 4, 4, 4, 6, 5, 5])

We get the number of fills per row with a sum:

In [143]: mask = np.arange(5)>=index[:,None]
In [144]: mask.sum(axis=1)
Out[144]: array([4, 3, 1, 2])

And get the fill array by using those to repeat the selected row value:

In [148]: mat[np.arange(4),index].repeat(mask.sum(axis=1))
Out[148]: array([4, 4, 4, 4, 4, 4, 4, 6, 5, 5])
In [149]: mat[mask] = _

Decide for yourself whether that "vectorized" approach is worth your time.

Upvotes: 1

Related Questions