1

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.

3
  • I don't think it is specific. To make it specific, please give an input, and the desired output/ Commented Jan 16, 2022 at 18:59
  • 1
    There isn't a "slice" that can do this. So iterating on the rows is a good option; if not the fastest it's the easiest to understand and implement. Using ideas developed for padding arrays of variable length it is possible to create a boolean mask of the slots you want to fill. And then mat[mask] = [4,4,..., 6,... 5,...] Commented Jan 16, 2022 at 19:34
  • Another current questions shows how to set one element per row, stackoverflow.com/questions/70732955/…; but this doesn't work for slices. As you found slices can only be defined with a scalar. Commented Jan 16, 2022 at 19:57

1 Answer 1

1
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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.