knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(Tplyr)
library(dplyr, warn.conflicts = FALSE)
library(knitr)
library(kableExtra)

In the other vignettes we talk about how to get the most out of Tplyr when it comes to preparing your data. The last step we need to cover is how to get from the data output by Tplyr to a presentation ready table.

There are a few things left to do after a table is built. These steps will vary based on what package you're using for presentation - but within this vignette we will demonstrate how to use 'huxtable' to prepare your table and 'pharmaRTF' to write the output.

After a Tplyr table is built, you will likely have to:

Let's build a demographics table to see how this all works.

Preparing the data

tplyr_adsl <- tplyr_adsl %>% 
  mutate(
    SEX = recode(SEX, M = "Male", F = "Female"), 
    RACE = factor(RACE, c("AMERICAN INDIAN OR ALASKA NATIVE", "ASIAN", "BLACK OR AFRICAN AMERICAN", 
                          "NATIVE HAWAIIN OR OTHER PACIFIC ISLANDER", "WHITE", "MULTIPLE"))
  )

t <- tplyr_table(tplyr_adsl, TRT01P) %>% 
  add_total_group() %>% 
  add_layer(name = 'Sex', 
    group_count(SEX, by = "Sex n (%)") %>% 
      set_missing_count(f_str('xx', n), Missing=NA, denom_ignore=TRUE)
  ) %>% 
  add_layer(name = 'Age',
    group_desc(AGE, by = "Age (Years)")
  ) %>% 
  add_layer(name = 'Age group', 
    group_count(AGEGR1, by = "Age Categories n (%)") %>% 
      set_missing_count(f_str('xx', n), Missing=NA, denom_ignore=TRUE)
  ) %>% 
  add_layer(name = 'Race', 
    group_count(RACE, by = "Race n (%)") %>% 
      set_missing_count(f_str('xx', n), Missing=NA, denom_ignore=TRUE) %>% 
      set_order_count_method("byfactor")
  ) %>% 
  add_layer(name = 'Ethnic', 
    group_count(ETHNIC, by = "Ethnicity n (%)") %>% 
      set_missing_count(f_str('xx', n), Missing=NA, denom_ignore=TRUE)
  )

dat <- build(t)

dat %>% 
  kable()

In the block above, we assembled the count and descriptive statistic summaries one by one. But notice that I did some pre-processing on the dataset. There are some important considerations here:

Data may sometimes need additional post-processing as well. For example, sometimes statisticians may want special formatting to do things like not show a percent when a count is 0. For this the function apply_conditional_formatting() can be used to post process character strings based on the numbers present within the string.

dat %>% 
  mutate(
    across(starts_with('var'),
    ~ if_else(
      ord_layer_index %in% c(1, 3:5),
      apply_conditional_format(
        string = .,
        format_group = 2,
        condition = x == 0,
        replacement = ""
      ),
      .
      )
    )
  )

In this example, we targetted the count layers by their layer index (in ord_layer_index) and used dplyr::across() to target the result columns. Within apply_conditional_format() where saying take the string, look at the second number, and if it's equal to 0 then replace that entire string segment with a blank. For more information on what the apply_conditional_format() function, see the function documentation. For now, we'll leave this data frame unedited for the rest of the example.

Sorting, Column Ordering, Column Headers, and Clean-up

Now that we have our data, let's make sure it's in the right order. Additionally, let's clean the data up so it's ready to present.

dat <- dat %>% 
  arrange(ord_layer_index, ord_layer_1, ord_layer_2) %>% 
  apply_row_masks(row_breaks = TRUE) %>% 
  select(starts_with("row_label"), var1_Placebo, `var1_Xanomeline Low Dose`, `var1_Xanomeline High Dose`, var1_Total) %>%
  add_column_headers(
    paste0(" | | Placebo\\line(N=**Placebo**)| Xanomeline Low Dose\\line(N=**Xanomeline Low Dose**) ", 
           "| Xanomeline High Dose\\line(N=**Xanomeline High Dose**) | Total\\line(N=**Total**)"), 
           header_n = header_n(t))

dat %>% 
  kable()

Now you can see things coming together. In this block, we:

Table Styling

There are a lot of options of where to go next. The gt package is always a good choice, and we've been using kableExtra throughout these vignettes. At the moment, with the tools we've made available in Tplyr, if you're aiming to create RTF outputs (which is still a common requirement in within pharma companies), huxtable and our package pharmaRTF will get you where you need to go.

(Note: We plan to extend pharmaRTF to support GT when it has better RTF support)

Alright - so the table is ready. Let's prepare the huxtable table.

# Make the table
ht <- huxtable::as_hux(dat, add_colnames=FALSE) %>%
  huxtable::set_bold(1, 1:ncol(dat), TRUE) %>% # bold the first row
  huxtable::set_align(1, 1:ncol(dat), 'center') %>% # Center align the first row 
  huxtable::set_align(2:nrow(dat), 3:ncol(dat), 'center') %>% # Center align the results
  huxtable::set_valign(1, 1:ncol(dat), 'bottom') %>% # Bottom align the first row
  huxtable::set_bottom_border(1, 1:ncol(dat), 1) %>% # Put a border under the first row
  huxtable::set_width(1.5) %>% # Set the table width
  huxtable::set_escape_contents(FALSE) %>% # Don't escape RTF syntax
  huxtable::set_col_width(c(.2, .2, .15, .15, .15, .15)) # Set the column widths
ht

Output to File

So now this is starting to look a lot more like what we're going for! The table styling is coming together. The last step is to get it into a final output document. So here we'll jump into pharmaRTF

doc <- pharmaRTF::rtf_doc(ht) %>% 
  pharmaRTF::add_titles(
    pharmaRTF::hf_line("Protocol: CDISCPILOT01", "PAGE_FORMAT: Page %s of %s", align='split', bold=TRUE, italic=TRUE),
    pharmaRTF::hf_line("Table 14-2.01", align='center', bold=TRUE, italic=TRUE),
    pharmaRTF::hf_line("Summary of Demographic and Baseline Characteristics", align='center', bold=TRUE, italic=TRUE)
  ) %>% 
  pharmaRTF::add_footnotes(
    pharmaRTF::hf_line("FILE_PATH: Source: %s", "DATE_FORMAT: %H:%M %A, %B %d, %Y", align='split', bold=FALSE, italic=TRUE)
  ) %>% 
  pharmaRTF::set_font_size(10) %>%
  pharmaRTF::set_ignore_cell_padding(TRUE) %>% 
  pharmaRTF::set_column_header_buffer(top=1)

This may look a little messy, but pharmaRTF syntax can be abbreviated by standardizing your process. Check out this vignette for instructions on how to read titles and footnotes into 'pharmaRTF' from a file.

Our document is now created, all the titles and footnotes are added, and settings are good to go. Last step is to write it out.

pharmaRTF::write_rtf(doc, file='styled_example.rtf')

And here we have it - Our table is styled and ready to go!

knitr::include_graphics("styled_example.png")

If you'd like to learn more about how to use huxtable, be sure to check out the website. For use specifically with pharmaRTF, we prepared a vignette of tips and tricks here.



atorus-research/Tplyr documentation built on Feb. 21, 2024, 7:36 p.m.