Structured lists

Structured lists are a simple(r) and lighter weight alternative to vctrs from the tidyverse package. In pkgutils a struct is defined as a S3 class based on a named list of values that is managed as a single entity. E.g. a value and confidence limits. All structs live in struct_lists allowing them to be part of data frames. This list of S3 classes/lists approach means that structs need infrastructure to make sure they stay in the same format.

library(ggrrr)
# devtools::load_all()

The aim of struct_list is to be transparent to the user and created in a package, but removing the overhead of managing data as lists of lists.

Constructor

As with vctrs the first step is to define a constructor function that performs any error checking, and handles any vectorisation needed


new_HR = function(mid, lower, upper, p.value) {
  return(struct(
    mid = mid,
    lower = lower,
    upper = upper,
    p.value = p.value,
    .class = "HR"))
}

format.HR = function(x, ...) {
  sprintf("%1.2f [%1.2f \u2014 %1.2f] (P:%s)",x$mid,x$lower,x$upper,scales::pvalue(x$p.value))
}

new_HR(1.2,0.8,1.6,0.8534)
#> HR(mid, lower, upper, p.value)
#> 1.20 [0.80 — 1.60] (P:0.853)

Printing as a list of custom S3 object is handled by default using tibble and pillar packages.


df = tibble::tibble(
  index = 1:5,
  HR = rep(new_HR(1.2,0.8,1.6,0.8534),5)
) 

format(df)
#> [1] "# A tibble: 5 × 2"                   
#> [2] "  index                           HR"
#> [3] "  <int>                       <HR[]>"
#> [4] "1     1 1.20 [0.80 — 1.60] (P:0.853)"
#> [5] "2     2 1.20 [0.80 — 1.60] (P:0.853)"
#> [6] "3     3 1.20 [0.80 — 1.60] (P:0.853)"
#> [7] "4     4 1.20 [0.80 — 1.60] (P:0.853)"
#> [8] "5     5 1.20 [0.80 — 1.60] (P:0.853)"

and it will also print correctly using knitrs default

knitr::kable(df)
index HR
1 1.20 [0.80 — 1.60] (P:0.853)
2 1.20 [0.80 — 1.60] (P:0.853)
3 1.20 [0.80 — 1.60] (P:0.853)
4 1.20 [0.80 — 1.60] (P:0.853)
5 1.20 [0.80 — 1.60] (P:0.853)
# but not directly with huxtable
# huxtable::hux(df %>% dplyr::mutate(HR = format(HR)))

As first class lists they can be manipulated with purrr functions, for extracting or manipulating values. If the value we expect back from a purrr::map is a struct just like purrr::map_dbl and similar functions we use a pkgutils::map_struct function.

x = new_HR(1.2,0.8,1.6,0.8534)

x$mid
#> [1] 1.2
df$HR$mid
#> [1] 1.2 1.2 1.2 1.2 1.2