Date: 2023-11-12

# Divvy up events with partitions

#### 12 November 2023

This vignette will introduce you to `partitions()`. `partitions()` provides an alternative approach to implementing case definitions. In summary, it uses specific temporal boundaries as the window of occurrence. This differs from `episodes()` where the boundaries are calculated as durations relative to index events. `partitions()` produces a similar `S4` class identifier (`pane`) referred to as panes and share similar arguments with `episodes()`.

To demonstrate this difference, let’s review the `homes` dataset below. It has data on household members including their ages. We’ll attempt to apply a case definition to identify a three-generation home, where each generation includes individuals aged not more than 16 years apart.

    homes <- data.frame(member = c("son_1", "son_2", "daughter_1",
                                   "father", "mother", "grand_father", "grand_mother"),
                        age = c(4, 6, 17, 43, 40, 74, 69))
    homes
    #>         member age
    #> 1        son_1   4
    #> 2        son_2   6
    #> 3   daughter_1  17
    #> 4       father  43
    #> 5       mother  40
    #> 6 grand_father  74
    #> 7 grand_mother  69

The simplest approach would be to specify the age bands for each generation. In this context, these are the temporal boundaries.

    age_bands <- seq(0, 69, by =17)
    age_bands <- number_line(age_bands, age_bands + 16)
    age_bands
    #> [1] "0 -> 16"  "17 -> 33" "34 -> 50" "51 -> 67" "68 -> 84"

    homes$grp_1 <- partitions(homes$age, window = list(age_bands), separate = TRUE)
    homes
    #>         member age     grp_1
    #> 1        son_1   4 PN.08 (I)
    #> 2        son_2   6 PN.08 (D)
    #> 3   daughter_1  17 PN.09 (I)
    #> 4       father  43 PN.10 (D)
    #> 5       mother  40 PN.10 (I)
    #> 6 grand_father  74 PN.12 (D)
    #> 7 grand_mother  69 PN.12 (I)

    schema(homes$grp_1, seed = 4,
           custom_label = paste0(homes$member, " \n(", homes$age, " yrs)"))

However, we can make the case that the children are all part of the same generation since no two are older than 16 years apart. This presents the main difference between `partitions()` and `episodes()`. Unlike `episodes()`, the duration (age gaps) between records is not a factor. Here records or events are linked together simply because they exist within the same interval (age gap).

To correct this, we can start the age band from age 6 but this becomes difficult to manage when analysing multiple homes. Instead, we can use the `by` or `lenght.out` argument to create windows (`window`) relative to the first event (or `custom_sort`) only. Although this makes it more like `episodes()`, it is still different since all age gaps are relative to only one reference event `(I)`.

    homes$grp_2 <- partitions(homes$age, by = 16, separate = TRUE)
    schema(homes$grp_2, seed = 4,
           custom_label = paste0(homes$member, " \n(", homes$age, " yrs)"))

Now that we have identified the generations, we can build on this by linking every record on the conditions that there’s a specified number of generations (windows). Below we ask for three to four generations.

    homes$grp_3 <- partitions(homes$age, by = 16,
                              separate = FALSE,
                              windows_total = number_line(3, 4))
    homes
    #>         member age     grp_1     grp_2    grp_3
    #> 1        son_1   4 PN.08 (I) PN.08 (I) PN.1 (I)
    #> 2        son_2   6 PN.08 (D) PN.08 (D) PN.2 (I)
    #> 3   daughter_1  17 PN.09 (I) PN.08 (D) PN.3 (I)
    #> 4       father  43 PN.10 (D) PN.10 (D) PN.4 (I)
    #> 5       mother  40 PN.10 (I) PN.10 (I) PN.5 (I)
    #> 6 grand_father  74 PN.12 (D) PN.06 (I) PN.6 (I)
    #> 7 grand_mother  69 PN.12 (I) PN.07 (I) PN.7 (I)

    schema(homes$grp_3, seed = 4,
           custom_label = paste0(homes$member, " \n(", homes$age, " yrs)"))

Despite the use of `by` and `length.out`, if the configurations of records relative to the index record changes, the resulting identifier can change as well. For example, if the `"mother"` and `"father"` were five years younger, this would place them in two different age gaps, resulting in a total of four generations.

    homes$alt_age <- homes$age
    lgk <- homes$member %in% c("father", "mother")
    homes$alt_age[lgk] <- homes$alt_age[lgk] - 5
    homes$grp_4 <- partitions(homes$alt_age, by = 16,
                              separate = TRUE,
                              windows_total = number_line(3, 4))
    homes
    #>         member age     grp_1     grp_2    grp_3 alt_age    grp_4
    #> 1        son_1   4 PN.08 (I) PN.08 (I) PN.1 (I)       4 PN.1 (I)
    #> 2        son_2   6 PN.08 (D) PN.08 (D) PN.2 (I)       6 PN.2 (I)
    #> 3   daughter_1  17 PN.09 (I) PN.08 (D) PN.3 (I)      17 PN.3 (I)
    #> 4       father  43 PN.10 (D) PN.10 (D) PN.4 (I)      38 PN.4 (I)
    #> 5       mother  40 PN.10 (I) PN.10 (I) PN.5 (I)      35 PN.5 (I)
    #> 6 grand_father  74 PN.12 (D) PN.06 (I) PN.6 (I)      74 PN.6 (I)
    #> 7 grand_mother  69 PN.12 (I) PN.07 (I) PN.7 (I)      69 PN.7 (I)

    schema(homes$grp_4, seed = 4,
           custom_label = paste0(homes$member, " \n(", homes$alt_age, " yrs)"))

This makes a difference if our conditions changes to only three generations as the condition for our three-generation households.

    homes$grp_5 <- partitions(homes$alt_age, by = 16,
                              separate = FALSE,
                              windows_total = number_line(3, 3))

    homes
    #>         member age     grp_1     grp_2    grp_3 alt_age    grp_4    grp_5
    #> 1        son_1   4 PN.08 (I) PN.08 (I) PN.1 (I)       4 PN.1 (I) PN.1 (I)
    #> 2        son_2   6 PN.08 (D) PN.08 (D) PN.2 (I)       6 PN.2 (I) PN.2 (I)
    #> 3   daughter_1  17 PN.09 (I) PN.08 (D) PN.3 (I)      17 PN.3 (I) PN.3 (I)
    #> 4       father  43 PN.10 (D) PN.10 (D) PN.4 (I)      38 PN.4 (I) PN.4 (I)
    #> 5       mother  40 PN.10 (I) PN.10 (I) PN.5 (I)      35 PN.5 (I) PN.5 (I)
    #> 6 grand_father  74 PN.12 (D) PN.06 (I) PN.6 (I)      74 PN.6 (I) PN.6 (I)
    #> 7 grand_mother  69 PN.12 (I) PN.07 (I) PN.7 (I)      69 PN.7 (I) PN.7 (I)
    schema(homes$grp_5, seed = 4,
           custom_label = paste0(homes$member, " \n(", homes$alt_age, " yrs)"))

We see that the household no longer has a common identifier that would identify it as a three-generation household. If we wish to address this, then `episodes()` would be the better option.

    homes$grp_6 <- episodes(homes$alt_age, case_length = 16)
    homes
    #>         member age     grp_1     grp_2    grp_3 alt_age    grp_4    grp_5
    #> 1        son_1   4 PN.08 (I) PN.08 (I) PN.1 (I)       4 PN.1 (I) PN.1 (I)
    #> 2        son_2   6 PN.08 (D) PN.08 (D) PN.2 (I)       6 PN.2 (I) PN.2 (I)
    #> 3   daughter_1  17 PN.09 (I) PN.08 (D) PN.3 (I)      17 PN.3 (I) PN.3 (I)
    #> 4       father  43 PN.10 (D) PN.10 (D) PN.4 (I)      38 PN.4 (I) PN.4 (I)
    #> 5       mother  40 PN.10 (I) PN.10 (I) PN.5 (I)      35 PN.5 (I) PN.5 (I)
    #> 6 grand_father  74 PN.12 (D) PN.06 (I) PN.6 (I)      74 PN.6 (I) PN.6 (I)
    #> 7 grand_mother  69 PN.12 (I) PN.07 (I) PN.7 (I)      69 PN.7 (I) PN.7 (I)
    #>              grp_6
    #> 1  E.1 4 -> 17 (C)
    #> 2  E.1 4 -> 17 (D)
    #> 3  E.1 4 -> 17 (D)
    #> 4 E.5 35 -> 38 (D)
    #> 5 E.5 35 -> 38 (C)
    #> 6 E.7 69 -> 74 (D)
    #> 7 E.7 69 -> 74 (C)

    schema(homes$grp_6, seed = 4,
           show_labels = c("length_arrow", "length"),
           custom_label = paste0(homes$member, " \n(", homes$alt_age, " yrs)"))

Unlike `partitions()`, additional analyses is required to flag the whole household as a three-generation household. For example, we can count the number of “occurrences” ( age gaps in `epid` talk).

    as.data.frame(homes$grp_6)
    #>   epid sn     case_nm dist_wind_index dist_epid_index epid_length epid_total
    #> 1    1  1        Case               0               0          13          3
    #> 2    1  2 Duplicate_C               2               2          13          3
    #> 3    1  3 Duplicate_C              13              13          13          3
    #> 4    5  4 Duplicate_C               3               3           3          2
    #> 5    5  5        Case               0               0           3          2
    #> 6    7  6 Duplicate_C               5               5           5          2
    #> 7    7  7        Case               0               0           5          2
    #>   iteration wind_id1 wind_nm1 epid_start epid_end
    #> 1         1        1     Case          4       17
    #> 2         1        1     Case          4       17
    #> 3         1        1     Case          4       17
    #> 4         2        5     Case         35       38
    #> 5         2        5     Case         35       38
    #> 6         3        7     Case         69       74
    #> 7         3        7     Case         69       74

    homes$t3_home <- length(unique(homes$grp_6@wind_id[[1]])) == 3
    homes
    #>         member age     grp_1     grp_2    grp_3 alt_age    grp_4    grp_5
    #> 1        son_1   4 PN.08 (I) PN.08 (I) PN.1 (I)       4 PN.1 (I) PN.1 (I)
    #> 2        son_2   6 PN.08 (D) PN.08 (D) PN.2 (I)       6 PN.2 (I) PN.2 (I)
    #> 3   daughter_1  17 PN.09 (I) PN.08 (D) PN.3 (I)      17 PN.3 (I) PN.3 (I)
    #> 4       father  43 PN.10 (D) PN.10 (D) PN.4 (I)      38 PN.4 (I) PN.4 (I)
    #> 5       mother  40 PN.10 (I) PN.10 (I) PN.5 (I)      35 PN.5 (I) PN.5 (I)
    #> 6 grand_father  74 PN.12 (D) PN.06 (I) PN.6 (I)      74 PN.6 (I) PN.6 (I)
    #> 7 grand_mother  69 PN.12 (I) PN.07 (I) PN.7 (I)      69 PN.7 (I) PN.7 (I)
    #>              grp_6 t3_home
    #> 1  E.1 4 -> 17 (C)    TRUE
    #> 2  E.1 4 -> 17 (D)    TRUE
    #> 3  E.1 4 -> 17 (D)    TRUE
    #> 4 E.5 35 -> 38 (D)    TRUE
    #> 5 E.5 35 -> 38 (C)    TRUE
    #> 6 E.7 69 -> 74 (D)    TRUE
    #> 7 E.7 69 -> 74 (C)    TRUE

Similar to `episodes()`, everything we’ve discussed above can be done separately for different subsets of the dataset by using the `strata` argument. For example, different households.

    duplicate <- rbind(homes[1:2], homes[1:2])
    duplicate$house_hold <- c(rep("london", 7), rep("hull", 7))

    duplicate$grp_1 <- partitions(duplicate$age, by = 16,
                                   separate = FALSE,
                                   windows_total = number_line(3, 4),
                                   strata = duplicate$house_hold)
    duplicate$grp_2 <- episodes(duplicate$age,
                                 case_length = 16,
                                 strata = duplicate$house_hold)

    duplicate
    #>          member age house_hold     grp_1             grp_2
    #> 1         son_1   4     london PN.01 (I)  E.01 4 -> 17 (C)
    #> 2         son_2   6     london PN.02 (I)  E.01 4 -> 17 (D)
    #> 3    daughter_1  17     london PN.03 (I)  E.01 4 -> 17 (D)
    #> 4        father  43     london PN.04 (I) E.05 40 -> 43 (D)
    #> 5        mother  40     london PN.05 (I) E.05 40 -> 43 (C)
    #> 6  grand_father  74     london PN.06 (I) E.07 69 -> 74 (D)
    #> 7  grand_mother  69     london PN.07 (I) E.07 69 -> 74 (C)
    #> 8         son_1   4       hull PN.08 (I)  E.08 4 -> 17 (C)
    #> 9         son_2   6       hull PN.09 (I)  E.08 4 -> 17 (D)
    #> 10   daughter_1  17       hull PN.10 (I)  E.08 4 -> 17 (D)
    #> 11       father  43       hull PN.11 (I) E.12 40 -> 43 (D)
    #> 12       mother  40       hull PN.12 (I) E.12 40 -> 43 (C)
    #> 13 grand_father  74       hull PN.13 (I) E.14 69 -> 74 (D)
    #> 14 grand_mother  69       hull PN.14 (I) E.14 69 -> 74 (C)
    schema(duplicate$grp_1, seed = 5,
           custom_label = paste0(duplicate$member, " (", duplicate$age, " yrs) in \n", duplicate$house_hold))

    schema(duplicate$grp_2, seed = 4,
           show_labels = c("length_arrow", "length"),
           custom_label = paste0(duplicate$member, " (", duplicate$age, " yrs) in \n", duplicate$house_hold))