It uses broadcasting and advanced-indexing -
def preceedingN(a, N): # mask of value (minus 1 here) to be found mask = a==-1 # Get the first index with the value along the last axis. # In case its not found, choose the last index idx = np.where(mask.any(-1), mask.argmax(-1), mask.shape[-1]) # Get N ranged indices along the last axis ind = idx[...,None] + np.arange(-N,0) # Finally advanced-index and get the ranged indexed elements as the o/p m,n,r = a.shape return a[np.arange(m)[:,None,None], np.arange(n)[:,None], ind]
Run Example -
Setting for playable input:
import numpy as np # Setup sample input array np.random.seed(0) m,n,r = 2,4,10 a = np.random.randint(11,99,(m,n,r)) # Select N elements off each row N = 3 idx = np.random.randint(N,a.shape[-1]-1,(m,n)) a[idx[...,None] < np.arange(a.shape[-1])] = -1 a[0,0] = range(r) # set first row of first 2D slice to range (no -1s there)
Enter exit:
>>> a array([[[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [81, 23, 69, 76, 50, 98, 57, -1, -1, -1], [88, 83, 20, 31, 91, 80, 90, 58, -1, -1], [60, 40, 30, 30, 25, 50, 43, 76, -1, -1]], [[43, 42, 85, 34, 46, 86, 66, 39, -1, -1], [11, 47, 64, 16, -1, -1, -1, -1, -1, -1], [42, 12, 76, 52, 68, 46, 22, 57, -1, -1], [25, 64, 23, 53, 95, 86, 79, -1, -1, -1]]]) >>> preceedingN(a, N=3) array([[[ 7, 8, 9], [50, 98, 57], [80, 90, 58], [50, 43, 76]], [[86, 66, 39], [47, 64, 16], [46, 22, 57], [95, 86, 79]]])