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.
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))) }()
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
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