Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 806
jsavn Avatar asked Oct 11 '25 16:10

jsavn


2 Answers

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))) }()
like image 113
Friede Avatar answered Oct 14 '25 06:10

Friede


On top of @Frieke's base R solution, I'd like to bring up a tidyverse approach using the recent ivs package, which conveniently deals with intervals:

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

#Create the intervals (iv) object
iv1 = iv(intervals$start, intervals$end)
iv2 = iv(1, 20)

#Compute the difference
iv_set_difference(iv2, iv1)
# <iv<double>[3]>
# [1] [1, 3)   [6, 10)  [11, 19)

#You can easily transform this object back to a `normal` data.frame/tibble:
iv_diff <- iv_set_difference(iv2, iv1)
tibble(start = iv_start(iv_diff),
       end = iv_end(iv_diff))

# # A tibble: 3 × 2
#   start   end
#   <dbl> <dbl>
# 1     1     3
# 2     6    10
# 3    11    19
like image 36
Maël Avatar answered Oct 14 '25 05:10

Maël



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!