I know there are lots of similar questions, but this one is sightly different.
Given any row such as row = [1 0 0 1 0 1 0 1] I want to output a row that says output = [0 -1 -1 0 -1 3 -1 5]
and basically this is saying, the first 1, points to itself. The 2nd 1, which has index 3, points to the 1 to its left and since that 1 has an index 0, it is 0. The 3rd 1 points to the 1 to it's left, which has index 3. And lastly, the 4th 1 points to the first one to it's left which has index 5. Lastly, all the 0s are set to -1.
I am able to get the indices of the where all the 1 are using numpy.nonzero(row) but I'm not sure how to space out these indices in the same dimensions as input array.
It's rather easy if you iterate on the list (using enumerate to keep track of the element index too) and store the last index where 1 (below any non-zero value) was seen (except for the first time)
row = [1, 0, 0, 1, 0, 1, 0, 1]
prev_index = None
result = []
for i,v in enumerate(row):
if v:
result.append(i if prev_index is None else prev_index)
prev_index = i
else:
result.append(-1)
>>> result
[0, -1, -1, 0, -1, 3, -1, 5]
This is difficult to achieve with a list comprehension because of the need to store the previous index.
Basically what we want is to replace all 0s with -1, and all non-zeros with the index of the previous zero, if I understood it correctly.
We can thus create an array of -1s with the same length as the given array, and then replace a view of zeros with the result of np.where:
outp = np.full(a.shape, -1)
idxs = a.nonzero()
if len(idxs) > 0:
outp[idxs] = np.concatenate(([0], idxs[0][:-1]))
For example:
>>> a = np.array([1, 0, 0, 1, 0, 1, 0, 1])
>>> outp = np.full(a.shape, -1)
>>> idxs = a.nonzero()
>>> outp[idxs] = np.concatenate(([0], idxs[0][:-1]))
>>> outp
array([ 0, -1, -1, 0, -1, 3, -1, 5])
If the first value is a zero however, it will still have value -1, and thus have an index out of range, but it is not clear, at least to me, what should happen in that case.
We can write it a bit more elegant as:
outp = np.full(a.shape, -1)
idxs, = a.nonzero()
if len(idxs) > 0:
outp[idxs[1:]] = idxs[:-1]
outp[idxs[0]] = idxs[0]
This allows us to fill in a value before the first non-zero:
outp = np.full(a.shape, -1)
idxs, = a.nonzero()
if len(idxs) > 0:
outp[idxs[1:]] = idxs[:-1]
outp[idxs[0]] = idxs[0]
outp[:idxs[0]] = 0 # or another value before the first non-zero
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With