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.
CodePudding user response:
You can do it using np.apply_along_axis:
Define a function to be applied to each row:
def myFill(row):
n = row[0]
rv = row.copy()
rv[n 2:] = rv[n 1]
return rv
Then apply it
result = np.apply_along_axis(myFill, 1, mat)
To present a more instructive example, I defined the source array (mat) as:
array([[ 0, 2, 3, 4, 5, 6],
[ 1, 8, 9, 10, 11, 12],
[ 3, 14, 15, 16, 17, 18],
[ 2, 20, 21, 22, 23, 24]])
The result, for the above data, is:
array([[ 0, 2, 2, 2, 2, 2],
[ 1, 8, 9, 9, 9, 9],
[ 3, 14, 15, 16, 17, 17],
[ 2, 20, 21, 22, 22, 22]])
CodePudding user response:
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.
