Find intervals of true values in vector

Question:

I am looking for a quick way to find the start and end indexes of each "block" of consecutive trues in a Vector.
Both julia or python would do the job for me. I’ll write my example in julia syntax:

Say I have a vector

a = [false, true, true, true, false, true, false, true, true, false]

what I want to get is something like this (with 1-based indexing):

[[2, 4], [6, 6], [8, 9]]

The exact form/type of the returned value does not matter, I am mostly looking for a quick and syntactically easy solution. Single trues surrounded by falses should also be detected, as given in my example.

My use-case with this is that I want to find intervals in a Vector of data where the values are below a certain threshold. So I get a boolean array from my data where this is true. Ultimately I want to shade these intervals in a plot, for which I need the start and end indeces of each interval.

Asked By: flurble

||

Answers:

try this:

a = [False, True, True, True, False, True, False, True, True, False]

index = 0
foundTrue = False
booleanList = []
sublist = []

for i in a:
    index += 1
    
    if foundTrue:
        if i == False:
            foundTrue = False
            sublist.append(index-1)
            booleanList.append(sublist)
            sublist = []
    else:
        if i == True:
            foundTrue = True
            sublist.append(index)
   

print(booleanList)

output should be: [[2, 4], [6, 6], [8, 9]]

This iterates in the a list and when it finds a True it marks a flag (foundTrue) and stores its index on sublist. Now with the maked flag (foundTrue), if it finds a False, then we store the previous index from that False into sublist, appends it to the booleanList and resets sublist.

Answered By: Alberto Cambronero

My use-case with this is that I want to find intervals in a Vector of data where the values are below a certain threshold.

Let’s say your vector is v and your threshold is 7:

julia> println(v); threshold
[9, 6, 1, 9, 5, 9, 4, 5, 6, 1]
7

You can use findall to get the indices where the value is below the threshold, and get the boundaries from that:

julia> let start = 1, f = findall(<(threshold), v), intervals = Tuple{Int, Int}[]
         for i in Iterators.drop(eachindex(f), 1)
           if f[i] - f[i - 1] > 1
             push!(intervals, (f[start], f[i - 1]))
             start = i
           end
         end
         push!(intervals, (f[start], last(f)))
       end
3-element Vector{Tuple{Int64, Int64}}:
 (2, 3)
 (5, 5)
 (7, 10)
Answered By: Sundar R

Here’s a version that avoids running findall first, and is a bit faster as a consequence:

function intervals(v)
    ints = UnitRange{Int}[]
    i = firstindex(v)
    while i <= lastindex(v)
        j = findnext(v, i)  # find next true
        isnothing(j) && break
        k = findnext(!, v, j+1)  # find next false
        isnothing(k) && (k = lastindex(v)+1)
        push!(ints, j:k-1)
        i = k+1
    end
    return ints
end

It also returns a vector of UnitRanges, since that seemed a bit more natural to me.

Answered By: DNF
function intervals(a)
    jumps = diff([false; a; false])
    zip(findall(jumps .== 1), findall(jumps .== -1) .- 1)
end

Quick in terms of keystrokes, maybe not in performance or readability 🙂

Answered By: August
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.