Search code examples
rtidyverseintervals

How to find complementary, contiguous integer intervals in R?


I have a set of integer intervals in some range, e.g. 1-20, and I would like to generate complementary, contiguous intervals to them, like so:

intervals <- list(3:6, 10:11, 19:20)

complementary_intervals <- list(1:2, 7:9, 12:18)

Alternatively, I'll have a dataframe/tibble of start and end integers:

library(tidyverse)

intervals <- tribble(
 ~start, ~end,
 3, 6,
 10, 11,
 19, 20
)

How can I find the complementary intervals? I've tried using set_diff to generate the integers not included in the intervals, but then I'm stuck finding contiguous intervals from those.


Solution

  • Following @Maël's suggestion in the comments, I post my comment as an answer.

    Try

    # data 
    intervals = list(3:6, 10:11, 19:20)
    complementary_intervals = list(1:2, 7:9, 12:18)
    # task 
    x = setdiff(1L:20L, unlist(intervals)) 
    split(x, cumsum(c(1L, diff(x) != 1L)))
    
    #> $`1`
    #> [1] 1 2
    #> 
    #> $`2`
    #> [1] 7 8 9
    #> 
    #> $`3`
    #> [1] 12 13 14 15 16 17 18
    

    To check the result:

    y = split(x, cumsum(c(1L, diff(x) != 1L)))
    mapply(FUN = "%in%", complementary_intervals, y)
    

    The two lines could be rewritten as with the base pipe operator, nothing added to the environment.

    list(3:6, 10:11, 19:20) |> 
      { \(.) setdiff(1L:20L, unlist(.)) }() |>
      { \(.) split(., cumsum(x = c(1L, diff(.) != 1L))) }()