Skip to contents

This document is for developers who would like to understand the internal of the forestly package.

Overview

The forestly package is developed to generate interactive forest plot for the safety analysis in clinical trials.

To generate an interactive forest plot, we develop three main functions as listed below. And the details of these three main functions will be introduced in the next three sections separately.

  • tidy_ae_table: This function is used to obtain adverse event (AE) information ready for visualization.
  • download_ae_reports: This function is used to generate a download button, so users can download the AE summary tables by clicking this button.
  • forestly: This function is used to generate an interactive forest plot.

A minimal example summarized the major steps in using the forestly package is

mydata <- tidy_ae_table(...)    # Step 1: create tidy AE data
download_ae_reports(mydata)     # Step 2: generate download button
forestly(mydata)                # Step 3: generate interactive forest plot
                                # (the order of Step 2 and Step 3 can be reversed)

Main function 1: tidy_ae_table()

The main function tidy_ae_table() is used to obtain AE information ready for visualization. Specifically speaking, it merges standardized adsl and adae tables together, and outputs a AE summary table. The AE summary table summarizes the number/ratio of subjects experiencing a certain AE for both treatment arm and control arm.

There are some utility functions developed inside of tidy_ae_table() and we list them as follows.

  • tidy_population(): This function is used to prepare the observation datasets for the use in function tidy_ae_table(). The function filter the adsl dataset, and define the proper treatment order in the dataset.

  • tidy_observation(): This function is used to prepare the observation datasets for the use in function tidy_ae_table(). The function filter the adae dataset, and define the proper treatment order in the dataset.

  • `prop_test_mn(): This function tests equal proportion using Miettinen and Nurminen method. In the forestly package, it generates the confidence interval (CI) for the risk difference between treatment arm and control arm.

  • define_ae_selectList(): This function is to specify the AE labels to be displayed in the select list of the interactive forestly plot. It has two arguments, (1) ae_criterion and (2) ae_label. The first argument ae_criterion is a (vector of) string, which defines the criterion to select a class of AE to display in the interactive forest plot. For example, if ae_criterion = 'AESER == "Y"', then it will select the serious AE. The second argument ae_label is a (vector of) string, which defines the labels to be displayed in the select list of interactive forest plot. If these two arguments are both string vectors, then they must be a 1:1 map. This function is used in the arguments ae_interested = in function tidy_ae_table() (see examples below). It throws errors if the length of the ae_criterion doesn’t match that of ae_label.

# Example 1: if you would like to display the serious AE
tidy_ae_table(...,
              ae_interested = define_ae_selectList(ae_criterion = 'AESER == "Y"',
                                                   ae_label = "serious AE"),
              ...).

# Example 2: if you would like to display both serious AE and drug-related AE
tidy_ae_table(...,
              ae_interested = define_ae_selectList(ae_criterion = c('AESER != "N"', 'AEREL != "None"'),
                                                   ae_label = c("serious AE", "drug-related AE")
              ...).
  • tidy_multi_ae_label(): This a function is design to rbind data together according to the interested AE labels defined in ae_interested =. For example, if you use Example 1 above, tidy_multi_ae_label() will generate an AE summary table as rbind(summary table of all AE, summary of serious AE).

  • tidy_ae_listing(): This function generates a data frame which serves as the listed details in the the reactable.

Main function 2: tlf_download()

This function generates a download button for users to download AE tables.

The output of the tlf_xxx functions are r2rtf objects created by rtf_encode

  • tlf_download: a function to download multiple TLFs in Rmarkdown. Rewrite ae_download_report to allow it use tlf_xxx functions as input.
tlf_ae_summary()   # generate the AE summary table
tlf_ae_specific()  # generate the specific AE table
# tlf[[4]] <- tlf_ae_listing()   # As needed ????

tlf_download()    # download the above tables

Main function 3: ae_forestly()

This function generates an interactive forest plot for the object returned by format_ae_forestly(). The interactive forest plot is built on the lt package: the main table and the inline figures are a single lt table made interactive with lt::lt_interactive(), surrounded by a few forestly-owned HTML controls.

The output has two kinds of pieces.

The first is the forestly controls (an ordinary <div> of HTML widgets): a parameter dropdown, an incidence range slider, and an optional CSV download button. They are plain DOM elements, not part of the table. On change they drive the table through the controller lt exposes on the rendered table element as el._lt, calling el._lt.filter(id, fn) to install a row predicate. The slider and dropdown filter on hidden helper columns (hide_prop/hide_n and parameter) that stay in the table spec but are hidden from view. This logic lives in inst/js/forestly-widgets.js and needs no crosstalk.

The second is the lt table itself, which summarizes the AE information:

  • Per-arm n/(%) columns grouped under treatment-group spanners.
  • An inline proportion dot plot drawn by lt::lt_dotplot() (one staggered dot per arm on a shared scale, with a footer legend).
  • An inline risk-difference error bar drawn by lt::lt_errorbar() (one point and 95% CI per comparison, with a zero reference line and a favor-direction axis).
  • Expandable row detail (lt_interactive(detail=)): clicking a row’s caret renders that term’s AE listing as its own small interactive lt table, assembled on demand from a listing shipped once (see below).

There are some utility functions used to develop ae_forestly():

  • format_lt_forestly(): Turns format_ae_forestly()’s outdata into the structural lt table (columns, labels, spanners, the dot plot and error bar, column widths, hidden helper columns). It is deliberately interaction-free so it can be tested in isolation; ae_forestly() layers lt_interactive() and the controls on top. The figure design is driven by metadata that format_ae_forestly() computes once across all rows:

    • color: fig_prop_color / fig_diff_color, passed to lt_dotplot(color=) / lt_errorbar(color=);
    • label: the arm names and the diff_label favor-direction axis;
    • column width: the per-column widths in outdata$widths, passed to lt::lt_width();
    • range: fig_prop_range / fig_diff_range, passed to lt_dotplot(limits=) / lt_errorbar(limits=).
  • The drill-down listing is embedded once as a shared record store captured in the detail callback’s closure; each table row carries only the record indices it needs (run-length or delta encoded), so the listing is never duplicated per row. The lt interactivity (lt-interactive.js) and inline-plot (lt-plot.js) runtimes are bundled as HTML dependencies via lt::lt_dependency(); only the numbers travel to the client and the SVG is drawn in the browser.

Other utility files/functions

  • important_functions.R: This is used to declare the R-package dependency of the functions we used to develop the above functions.