Asked 1 month ago by VenusianNomad941
How can I determine the start and end indices of a counter run in a NumPy array?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by VenusianNomad941
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I have a NumPy array that represents counters which reset (i.e. they start over at 0). Given an array like this:
PYTHONvalues = [0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3] index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
The goal is to identify the contiguous block (run) of nonzero values for any queried index before the counter resets. For example:
I've seen some suggestions using Boolean masks, cumulative sums, and indexing methods in NumPy. What is an efficient way to compute these run boundaries using NumPy? Please provide a detailed explanation along with code examples that preserve the original array and code snippet formatting.
Comments 0
•Answers 5
•Views 91
Answer by TychoAI Agent • 1 month ago
Below is one way to do it. The idea is to “detect” the contiguous group of nonzero entries (i.e. the counter that is “running” until it resets to zero) and then, given a queried index, find which run it belongs to. For example:
One solution is as follows:
PYTHONimport numpy as np values = np.array([0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3]) # Create a boolean mask of non-zero (i.e. “running”) values. mask = values != 0 # Find the start indices of the runs. # A run starts when an element is nonzero but the previous element is zero (or it is the first element). run_start = np.where(mask & np.r_[True, ~mask[:-1]])[0] # Find the end indices of the runs. # A run ends when an element is nonzero but the next element is zero (or it is the last element). run_end = np.where(mask & np.r_[~mask[1:], True])[0] print("run_start:", run_start) # For this array: [ 2 8 16] print("run_end: ", run_end) # For this array: [ 6 13 18] def get_run_boundaries(query_index): # find which run contains the query_index # (you could also check that values[query_index] is nonzero) in_run = (run_start <= query_index) & (run_end >= query_index) if not in_run.any(): raise ValueError("The query index is not inside any run.") i = np.nonzero(in_run)[0][0] return run_start[i], run_end[i] # Examples: print(get_run_boundaries(3)) # prints (2, 6) print(get_run_boundaries(10)) # prints (8, 13) print(get_run_boundaries(16)) # prints (16, 18)
We first create a Boolean mask (mask = values != 0
) that tells us which entries are part of a run (nonzero).
A run starts where an element is nonzero but the previous element was zero (or it is at the beginning of the array). This is found with:
PYTHONrun_start = np.where(mask & np.r_[True, ~mask[:-1]])[0]
Here the np.r_[True, ~mask[:-1]]
pads the left so that the very first element is treated as a start if it is nonzero.
Similarly, a run ends when an element is nonzero and its following element is zero (or it is at the end of the array). This is computed much the same way:
PYTHONrun_end = np.where(mask & np.r_[~mask[1:], True])[0]
Finally, given any query index you check which run it belongs to by finding the run where the start is ≤ query index ≤ end.
This is one acceptable solution using NumPy indexing techniques.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by CosmicPioneer798 • 1 month ago
You can get the start/end coordinates of the non-null stretches with something like:
PYTHONidx = np.nonzero(values == 0)[0] start = idx+1 end = np.r_[idx[1:]-1, len(values)-1] m = start<end indices = np.c_[start, end][m]
indices
:
PYTHONarray([[ 2, 6], [ 8, 13], [16, 18]])
Then get the position with searchsorted
(assuming you only pass non-zeros indices, else you need an additional check (e.g. is values[position] != 0
) and explanation of what should be the output):
PYTHONindices[np.searchsorted(indices[:, 1], 2)] # [ 2, 6] indices[np.searchsorted(indices[:, 1], 10)] # [ 8, 13] indices[np.searchsorted(indices[:, 1], 16)] # [16, 18]
And you can get multiple targets at once:
PYTHONtarget = [2, 6, 10, 16] indices[np.searchsorted(indices[:, 1], target)] array([[ 2, 6], [ 2, 6], [ 8, 13], [16, 18]])
And if you have indices of zero-values you could mask them in the output:
PYTHONtarget = [1, 2, 6, 7, 10, 16] out = np.ma.masked_array(indices[np.searchsorted(indices[:, 1], target)], np.broadcast_to(values[target, None]==0, (len(target), 2)) ) [[-- --] [ 2 6] [ 2 6] [-- --] [ 8 13] [16 18]]
Used input:
PYTHONvalues = np.array([0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3])
No comments yet.
Answer by StarlitRanger968 • 1 month ago
PYTHONimport numpy as np values = np.array([0, 1, 2, 3, 4, 5, 0, 7, 8, 9, 10, 11, 12, 0, 0, 15, 16, 17]) m = values == 0 idx = np.where(m)[0] aa = np.where(m, np.arange(len(values)), -1) print(aa) #[ 0 -1 -1 -1 -1 -1 6 -1 -1 -1 -1 -1 -1 13 14 -1 -1 -1] startIdx = np.maximum.accumulate(aa, -1) +1 print(startIdx) '''[ 1 1 1 1 1 1 7 7 7 7 7 7 7 14 15 15 15 15]''' bb = np.where(m[::-1], np.arange(len(values))[::-1], len(values) ) print(bb) '''[18 18 18 14 13 18 18 18 18 18 18 6 18 18 18 18 18 0]''' endIdx = np.minimum.accumulate(bb)[::-1] -1 print(endIdx) '''[-1 5 5 5 5 5 5 12 12 12 12 12 12 12 13 17 17 17]''' def getRange(idx): if values[idx] == 0 : return None aa = int(startIdx[idx]) bb = int(endIdx[idx]) return (aa,bb) print(getRange(3)) # Output: (1, 5) print(getRange(10)) # Output: (7, 12) print(getRange(16)) # Output: (15, 17)
No comments yet.
Answer by StellarSatellite858 • 1 month ago
PYTHONimport numpy as np arr = np.array([0, 0, 1, 2, 3, 4,5, 0, 1, 2, 3, 4, 5, 6, 0, 0,1, 2, 3]) resets = np.where(arr == 0)[0] # Create an array of boundaries boundaries = np.concatenate(([ -1 ], resets, [ len(arr) ])) def get_run_bounds(arr, boundaries, i): if arr[i] == 0: return None pos= np.searchsorted(boundaries, i, side='right') start = boundaries[pos- 1] + 1 end = boundaries[pos] - 1 return (start, end) print("Index 3→",get_run_bounds(arr, boundaries, 3)) # waiting for(2, 6) print("Index 10 →", get_run_bounds(arr, boundaries, 10)) # (8, 13) print("Index 16 →", get_run_bounds(arr, boundaries, 16))# (16, 18)
No comments yet.
Answer by QuantumAstronaut174 • 1 month ago
For example, if you care about index==10
, you can try
PYTHONgrp = np.cumsum(np.append(1,np.diff(values)<0)) np.argwhere(grp ==grp[index==10])[1:].T
which gives
PYTHONarray([[ 8, 9, 10, 11, 12, 13]])
PYTHONvalues = np.array([0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3]) index = np.arange(values.size)
No comments yet.
No comments yet.