A number_line
object is a range of numeric
values on a number line. In diyar
, how they overlap determine successful matches (see range matching) in record_group()
and overlapping windows (see interval grouping) in fixed_episodes()
, rolling_episodes()
and episode_group()
.
number_line
objects are similar to interval
objects in the lubridate
package but can support any numeric
based object class..
For a given number_line
object e.g. 8 <- 1
, its main components are;
left_point()
- left side of the number_line
i.e.
8
right_point()
- right side of the number_line
i.e.
1
start_point()
- lower end of the range i.e.
1
end_point()
- upper end of the range i.e.
8
direction
- "decreasing"
(<-)
, "increasing"
(->)
or no direction (==)
number_line_width()
- difference between the left and right points i.e.
-7
In "increasing"
number_line
objects, the start_point()
is on the left_point()
while in "decreasing"
number_line
objects, the start_point()
is on the right_point()
.
library(diyar)
nl_a <- number_line(l=c(3,5), r = c(4,1))
# `number_line` objects
nl_a
#> [1] "3 -> 4" "5 <- 1"
# lower end of the range
start_point(nl_a)
#> [1] 3 1
# left part of the range
left_point(nl_a)
#> [1] 3 5
Figure 1: number_line
objects
You can reverse the direction of a number_line
object with reverse_number_line()
.
# Only reverse decreasing `number_line` objects
nl_b <- reverse_number_line(nl_a, direction = "decreasing"); nl_b
#> [1] "3 -> 4" "1 -> 5"
Figure 2: Direction of number_line
objects
number_line
objectsdiyar
has convenience functions to reverse, shrink, expand number_line
objects, or shift the along the number line.
nl_c <- number_line(as.Date("04/04/2019", "%d/%M/%Y"),
as.Date("07/04/2019", "%d/%M/%Y"))
nl_c
#> [1] "2019-06-04 -> 2019-06-07"
# expand the `number_line` object by 2 days from both ends
nl_d <- expand_number_line(nl_c, 2); nl_d
#> [1] "2019-06-02 -> 2019-06-09"
# expand the `number_line` object by 2 days from the lower end
nl_e <- expand_number_line(nl_c, 2, "start"); nl_e
#> [1] "2019-06-02 -> 2019-06-07"
# shrink the `number_line` object by 2 days from the upper end
nl_f <- expand_number_line(nl_c, -2, "end"); nl_f
#> [1] "2019-06-04 -> 2019-06-05"
# shrink the `number_line` object by 2 days from both ends
nl_g <- expand_number_line(nl_c, -2); nl_g
#> [1] "2019-06-06 <- 2019-06-05"
# reverse the direction of the `number_line`
nl_h <- reverse_number_line(nl_c); nl_h
#> [1] "2019-06-07 <- 2019-06-04"
# shift the `number_line` object towards the left of the number line by 2 days
nl_i <- shift_number_line(nl_c, -2); nl_g
#> [1] "2019-06-06 <- 2019-06-05"
# shift the `number_line` object towards the right of the number line by 2 days
nl_j <- shift_number_line(nl_c, 2); nl_i
#> [1] "2019-06-02 -> 2019-06-05"
Figure 3: Manipulated number_line
objects
invert_number_line()
inverts the position of each point on the number line. See below.
inv <- number_line(c(3,-3,3,-3), c(-6,6,6,-6)); inv
#> [1] "3 <- -6" "-3 -> 6" "3 -> 6" "-3 <- -6"
inv
#> [1] "3 <- -6" "-3 -> 6" "3 -> 6" "-3 <- -6"
invert_number_line(inv)
#> [1] "-3 -> 6" "3 <- -6" "-3 <- -6" "3 -> 6"
invert_number_line(inv, "left")
#> [1] "-3 <- -6" "3 -> 6" "-3 -> 6" "3 <- -6"
invert_number_line(inv, "start")
#> [1] "3 -> 6" "3 -> 6" "-3 -> 6" "-3 -> 6"
invert_number_line(inv, "right")
#> [1] "3 -> 6" "-3 <- -6" "3 <- -6" "-3 -> 6"
invert_number_line(inv, "end")
#> [1] "-3 <- -6" "-3 <- -6" "3 <- -6" "3 <- -6"
You can also convert number_line
objects to a sequence of numeric
based values
nls <- c(nl_c, nl_d, nl_e, nl_f, nl_g, nl_h, nl_i, nl_j)
nls
#> [1] "2019-06-04 -> 2019-06-07" "2019-06-02 -> 2019-06-09"
#> [3] "2019-06-02 -> 2019-06-07" "2019-06-04 -> 2019-06-05"
#> [5] "2019-06-06 <- 2019-06-05" "2019-06-07 <- 2019-06-04"
#> [7] "2019-06-02 -> 2019-06-05" "2019-06-06 -> 2019-06-09"
number_line_sequence(nls, by=2)
#> [[1]]
#> [1] "2019-06-04" "2019-06-06"
#>
#> [[2]]
#> [1] "2019-06-02" "2019-06-04" "2019-06-06" "2019-06-08"
#>
#> [[3]]
#> [1] "2019-06-02" "2019-06-04" "2019-06-06"
#>
#> [[4]]
#> [1] "2019-06-04"
#>
#> [[5]]
#> [1] "2019-06-06"
#>
#> [[6]]
#> [1] "2019-06-07" "2019-06-05"
#>
#> [[7]]
#> [1] "2019-06-02" "2019-06-04"
#>
#> [[8]]
#> [1] "2019-06-06" "2019-06-08"
number_line_sequence(nls, length.out = 3)
#> [[1]]
#> [1] "2019-06-04" "2019-06-05" "2019-06-07"
#>
#> [[2]]
#> [1] "2019-06-02" "2019-06-05" "2019-06-09"
#>
#> [[3]]
#> [1] "2019-06-02" "2019-06-04" "2019-06-07"
#>
#> [[4]]
#> [1] "2019-06-04" "2019-06-04" "2019-06-05"
#>
#> [[5]]
#> [1] "2019-06-06" "2019-06-05" "2019-06-05"
#>
#> [[6]]
#> [1] "2019-06-07" "2019-06-05" "2019-06-04"
#>
#> [[7]]
#> [1] "2019-06-02" "2019-06-03" "2019-06-05"
#>
#> [[8]]
#> [1] "2019-06-06" "2019-06-07" "2019-06-09"
number_line
objects can overlap with each other and do so in six ways - "exact"
, "inbetween"
, "across"
, "chain"
, "aligns_start"
and "aligns_end"
. Each pair of number_line
objects can only overlap by one these methods. For example, a number_line
object can not have aligned start and end points, instead that’ll be considered an "exact"
overlap.
The 6 overlap_methods
are mutually exclusive.
There are 2 convenience methods - "none"
and "overlap"
. These are mutually inclusive with the other 6.
Figure 4a: Example of exact
overlaps
exact <- c(number_line(1,2), number_line(1,2),
# negative test - start_point() and end_point()
# for both must be identical to be an exact mactch
number_line(3,4), number_line(4,3),
number_line(2.5,2.5), number_line(2.5,2.5))
# positive logical test
exact(exact[c(1,3,5)], exact[c(2,4,6)])
#> [1] TRUE FALSE TRUE
# negtaive logical test
across(exact[c(1,3,5)], exact[c(2,4,6)])
#> [1] FALSE FALSE FALSE
# check overlap methods as defined in `diyar`
overlap_method(exact[c(1,3,5)], exact[c(2,4,6)])
#> [1] "exact" "none" "exact"
Figure 4b: Example of inbetween
overlaps
inbetween <- c(number_line(5, 8), number_line(6, 7),
number_line(11, 10), number_line(9, 12),
number_line(13, 15), as.number_line(14))
# positive logical test
inbetween(inbetween[c(1,3,5)], inbetween[c(2,4,6)])
#> [1] TRUE TRUE TRUE
# negtaive logical test
across(inbetween[c(1,3,5)], inbetween[c(2,4,6)])
#> [1] FALSE FALSE FALSE
# check overlap methods as defined in `diyar`
overlap_method(inbetween[c(1,3,5)], inbetween[c(2,4,6)])
#> [1] "inbetween" "inbetween" "inbetween"
Figure 4c: Example of across
overlaps
across <- c(number_line(16, 19), number_line(18, 21),
number_line(22, 25), number_line(23, 26),
number_line(27, 30), number_line(31, 29))
# positive logical test
across(across[c(1,3,5)], across[c(2,4,6)])
#> [1] TRUE TRUE TRUE
# negtaive logical test
inbetween(across[c(1,3,5)], across[c(2,4,5)])
#> [1] FALSE FALSE FALSE
# check overlap methods as defined in `diyar`
overlap_method(across[c(1,3,5)], across[c(2,4,6)])
#> [1] "across" "across" "across"
Figure 4d: Example of chain
overlaps
chain <- c(number_line(32, 34), number_line(34, 36),
number_line(39, 37), number_line(41, 39),
# negative test - end_point() of one must lead to the start_point()
# to be considered a chain overlap
number_line(42, 44), number_line(47, 44))
# positive logical test
chain(chain[c(1,3,5)], chain[c(2,4,6)])
#> [1] TRUE TRUE FALSE
# negtaive logical test
across(chain[c(1,3,5)], chain[c(2,4,6)])
#> [1] FALSE FALSE FALSE
# check overlap methods as defined in `diyar`
overlap_method(chain[c(1,3,5)], chain[c(2,4,6)])
#> [1] "chain" "chain" "aligns_end"
Figure 4e: Example of aligns_start
overlaps
aligns_start <- c(number_line(45, 46), number_line(45, 47),
number_line(48, 49.5), number_line(48, 48),
number_line(51, 50), number_line(51, 52))
# positive logical test
aligns_start(aligns_start[c(1,3,5)], aligns_start[c(2,4,6)])
#> [1] TRUE TRUE TRUE
# negtaive logical test
across(aligns_start[c(1,3,5)], aligns_start[c(2,4,6)])
#> [1] FALSE FALSE FALSE
# check overlap methods as defined in `diyar`
overlap_method(aligns_start[c(1,3,5)], aligns_start[c(2,4,6)])
#> [1] "aligns_start" "aligns_start" "aligns_start"
Figure 4f: Example of aligns_end
overlaps
aligns_end <- c(number_line(54, 55), number_line(53, 55),
number_line(56, 57.5), number_line(57.5, 57.5),
number_line(58, 59), number_line(60, 59))
# positive logical test
aligns_end(aligns_end[c(1,3,5)], aligns_end[c(2,4,6)])
#> [1] TRUE TRUE TRUE
# negtaive logical test
across(aligns_end[c(1,3,5)], aligns_end[c(2,4,6)])
#> [1] FALSE FALSE FALSE
# check overlap methods as defined in `diyar`
overlap_method(aligns_end[c(1,3,5)], aligns_end[c(2,4,6)])
#> [1] "aligns_end" "aligns_end" "aligns_end"
Set operations can be performed on number_line
objects.
a <- number_line(1,10); b <- number_line(5, 15)
c <- union_number_lines(a, b)
a; b; c
#> [1] "1 -> 10"
#> [1] "5 -> 15"
#> [1] "1 -> 15"
Figure 5a: Union
c <- intersect_number_lines(a, b)
a; b; c
#> [1] "1 -> 10"
#> [1] "5 -> 15"
#> [1] "5 -> 10"
Figure 5b: Intersection
c <- subtract_number_lines(a, b)
a; b; c
#> [1] "1 -> 10"
#> [1] "5 -> 15"
#> $n1
#> [1] "1 -> 5"
#>
#> $n2
#> [1] "10 -> 15"
Figure 5c: Subtraction
number_line
objectsYou can compress a pair of overlapping number_line
objects into one by using compress_number_line()
. You can also choose to compress only those that overlap in a certain way.
When overlapping number_line
objects have different directions, the resulting compressed number_line
object inherits the direction of the widest among them. If they have the same width, the compressed number_line
object takes an "increasing"
direction.
Figure 6. Pairs of number_line
objects overlapping by "inbetween"
and "exact"
methods compressed into one
cnl_a <- c(inbetween, exact); cnl_a
#> [1] "5 -> 8" "6 -> 7" "11 <- 10" "9 -> 12" "13 -> 15"
#> [6] "14 == 14" "1 -> 2" "1 -> 2" "3 -> 4" "4 <- 3"
#> [11] "2.5 == 2.5" "2.5 == 2.5"
cmp_a <- compress_number_line(cnl_a, deduplicate = T); cmp_a
#> [1] "5 -> 8" "9 -> 12" "13 -> 15" "1 -> 2" "3 -> 4"
#> [6] "2.5 == 2.5"
Figure 7. Pairs of number_line
objects overlapping by "across"
and "chain"
methods compressed into one
cnl_b <- c(across, chain); cnl_b
#> [1] "16 -> 19" "18 -> 21" "22 -> 25" "23 -> 26" "27 -> 30" "31 <- 29"
#> [7] "32 -> 34" "34 -> 36" "39 <- 37" "41 <- 39" "42 -> 44" "47 <- 44"
cmp_b <- compress_number_line(cnl_b, deduplicate = T); cmp_b
#> [1] "16 -> 21" "22 -> 26" "27 -> 31" "32 -> 36" "41 <- 37" "47 <- 42"
Compress number_line
objects that overlap in a particular way by using the methods
argument.
Figure 8. Pairs of number_line
objects overlapping by "aligns_start"
and "exact"
methods compressed into one
cnl_c <- c(aligns_start, aligns_end); cnl_c
#> [1] "45 -> 46" "45 -> 47" "48 -> 49.5" "48 == 48"
#> [5] "51 <- 50" "51 -> 52" "54 -> 55" "53 -> 55"
#> [9] "56 -> 57.5" "57.5 == 57.5" "58 -> 59" "60 <- 59"
cmp_c <- compress_number_line(cnl_c, deduplicate = T, methods ="aligns_start|exact"); cmp_c
#> [1] "45 -> 47" "48 -> 49.5" "50 -> 52" "54 -> 55"
#> [5] "53 -> 55" "56 -> 57.5" "57.5 == 57.5" "58 -> 59"
#> [9] "60 <- 59"
number_line
objectsThe resulting compressed number_line
object can yet again overlap with others, especially when not every method
is used. This doesn’t happen by default but you can do so by changing collapse
to TRUE
.
It’s worth mentioning that this process starts from the first number_line
object in the set and then proceeds to the last. Therefore, changing their position in the set can lead to different results.
Figure 9a: Compressing pairs of number_line
objects by "across"
, "chain"
, "inbetween"
and "aligns_end"
methods
nl_z <- number_line(l= c(1,2,4,5,6), r = c(3,4,8,8,7))
nl_z
#> [1] "1 -> 3" "2 -> 4" "4 -> 8" "5 -> 8" "6 -> 7"
In the figure above, there are "across"
, "chain"
, "inbetween"
and "aligns_end"
overlaps but we’ve chosen to only compress pairs that overlap by "chain"
and "aligns_end"
methods. The resulting number_line
object from compressing 1 -> 3 and 2 -> 4
now overlaps with the resulting number_line
objects from 4 -> 8 and 5 -> 8
.
Figure 9b: Compressing pairs of number_line
objects by "chain"
and "aligns_end"
methods compressed into one
cmp_z <- compress_number_line(nl_z, methods = "across|chain")
cmp_z
#> [1] "1 -> 4" "4 -> 8" "5 -> 8" "6 -> 7"
As mentioned, if each number_line
object’s position in the set changes, the result could also change. See below.
Figure 9c: Rearranged the pairs of number_line
objects before compressing by "across"
, "chain"
, "inbetween"
and "aligns_end"
methods
nl_zb <- nl_z[c(2:5,1)]; nl_zb
#> [1] "2 -> 4" "4 -> 8" "5 -> 8" "6 -> 7" "1 -> 3"
cmp_zb <- compress_number_line(nl_zb, methods = "across|chain"); cmp_zb
#> [1] "1 -> 8" "5 -> 8" "6 -> 7"
In Fig. 8b, 1 -> 3
came first, so only number_line
objects that overlapped with 1 -> 3
were compressed. However in Fig. 8c, because of the new positions, 2 -> 4
came first, so only those that overlapped with 2 -> 4
were compressed.
In the example below, we change collapse
to TRUE
to collapse (rather than compress) the initial set (nl_z
) which gives the same result as compressing the rearranged version (nl_zb
).
Figure 9d: pairs of number_line
objects overlapping by "chain"
and `“aligns_end” methods collapsed into one
cmp_zba <- compress_number_line(nl_z, methods = "across|chain", collapse = T); cmp_zba
#> [1] "1 -> 8" "5 -> 8" "6 -> 7"
fixed_episodes()
, rolling_episodes()
and episode_group()
are more advanced implementations of compress_number_line()
. They can compress ("fixed_episodes"
) and collapse ("rolling_episodes"
) overlapping number_line
objects. They have additional useful features such as controlling the order in which pairs are compressed i.e. chronological order (from_last
) or a user defined order (custom_sort
), and using additional periods (case_length
and recurrence_length
) before and/or (bi_direction
) after the reference number_line
object. See episode grouping for further details.