Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
cfeccec
init commit
shajoezhu Aug 1, 2025
10e9709
update autoslider example
shajoezhu Aug 18, 2025
3657573
update
shajoezhu Aug 18, 2025
6af3487
update
shajoezhu Aug 18, 2025
f415ad7
update dep
shajoezhu Aug 18, 2025
a421933
rm file
shajoezhu Aug 18, 2025
431cf1d
Update DESCRIPTION
shajoezhu Aug 18, 2025
e3549f6
Apply suggestions from code review
shajoezhu Aug 18, 2025
6c63d94
Apply suggestions from code review
shajoezhu Aug 18, 2025
5210c84
update
shajoezhu Aug 18, 2025
18f1c90
init commit
shajoezhu Aug 1, 2025
44b9c68
update autoslider example
shajoezhu Aug 18, 2025
f589f61
update
shajoezhu Aug 18, 2025
c0b6510
update
shajoezhu Aug 18, 2025
b4eafee
update dep
shajoezhu Aug 18, 2025
04acf3f
rm file
shajoezhu Aug 18, 2025
2135a92
Update DESCRIPTION
shajoezhu Aug 18, 2025
6b3310a
Apply suggestions from code review
shajoezhu Aug 18, 2025
fb47cfd
Apply suggestions from code review
shajoezhu Aug 18, 2025
451533f
update
shajoezhu Aug 18, 2025
9d357ea
Merge branch 'main' of github.com:shajoezhu/pharmaverse_examples
shajoezhu Sep 15, 2025
77b6bde
update wordlist
shajoezhu Sep 15, 2025
fa47028
Merge branch 'main' into main
rossfarrugia Sep 15, 2025
b4025d4
update wordlist
shajoezhu Sep 15, 2025
bfe3ad8
Merge branch 'main' of github.com:shajoezhu/pharmaverse_examples
shajoezhu Sep 15, 2025
5572de7
mv documents
shajoezhu Sep 15, 2025
1adbb16
change folder
shajoezhu Sep 15, 2025
3e7808f
update, mv files
shajoezhu Sep 19, 2025
7a8fd77
Update digit_files/docx.qmd
shajoezhu Sep 19, 2025
d6ab6b1
Merge branch 'main' of github.com:shajoezhu/pharmaverse_examples
shajoezhu Sep 19, 2025
9e93d42
update and add R fileS
shajoezhu Sep 20, 2025
58a1e85
change to #
shajoezhu Sep 20, 2025
f1266bd
update stle
shajoezhu Sep 22, 2025
07976b8
updat ewordlist
shajoezhu Sep 22, 2025
3c648fa
update style
shajoezhu Sep 22, 2025
d3be082
udpate style
shajoezhu Sep 22, 2025
a6835ec
sort the wordlist
shajoezhu Sep 23, 2025
b800352
Apply suggestions from code review
shajoezhu Sep 23, 2025
2bd7895
Merge branch 'main' of github.com:shajoezhu/pharmaverse_examples
shajoezhu Sep 23, 2025
237499e
rname file
shajoezhu Sep 23, 2025
0e6d433
update wordlist
shajoezhu Sep 23, 2025
92ad07a
update file names
shajoezhu Sep 24, 2025
29b3166
update and check
shajoezhu Sep 24, 2025
b8f6009
only do tdm
shajoezhu Sep 24, 2025
9331c44
update style
shajoezhu Sep 24, 2025
c11d80f
update style
shajoezhu Sep 25, 2025
00b2da4
minor update
shajoezhu Sep 25, 2025
534b63e
minor update
shajoezhu Sep 25, 2025
05c6de1
rm lines
shajoezhu Sep 25, 2025
ceb463b
restyle“
shajoezhu Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Authors@R:
person("David", "Blair", role = "aut"),
person("Daniel D.", "Sjoberg", , "danield.sjoberg@gmail.com", role = "aut", comment = c(ORCID = "0000-0003-0862-2018")),
person("Fanny", "Gautier", role = "aut", comment = c(ORCID = "0009-0004-3581-0131")),
person("Joe", "Zhu", role = "aut", comment = c(ORCID = "0000-0001-7566-2787")),
person("Aksel", "Thomsen", , "oath@novonordisk.com", role = "aut"),
person("Shiyu", "Chen", , "shiyu.chen@atorusresearch.com", role = "aut"),
person("Rammprasad", "Ganapathy", , "rammprasad.ganapathy@gene.com", role = "aut")
Expand All @@ -21,8 +22,10 @@ URL: https://github.com/pharmaverse/examples
Imports:
admiralonco,
admiral,
autoslider.core,
cards,
dplyr,
filters,
gtsummary,
lubridate,
labelled,
Expand All @@ -36,6 +39,8 @@ Imports:
pkglite,
reactable,
reactablefmtr,
rtables,
rtables.officer,
rlistings,
sdtm.oak,
sparkline,
Expand Down
1 change: 1 addition & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ website:
- auto: sdtm
- auto: adam
- auto: tlg
- auto: digit_files
- auto: interactive
- auto: logging
- auto: esub
Expand Down
54 changes: 54 additions & 0 deletions digit_files/autoslider.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## ----r setup------------------------------------------------------------------
library(autoslider.core)
library(dplyr)

# 1. Load ALL necessary packages
library(rtables) # For append_topleft()
library(assertthat) # For assert_that() you had issues with before
library(tern)

# define path to the yml files
spec_file <- file.path("metadata/autoslideR_spec.yml")

filters_file <- file.path("metadata/autoslideR_filters.yml")
# load all filters
filters::load_filters(filters_file, overwrite = TRUE)

## ----r, table-----------------------------------------------------------------
# read data
data <- list(
"adsl" = pharmaverseadam::adsl %>%
mutate(
FASFL = SAFFL, # add FASFL for illustrative purpose for t_pop_slide
# DISTRTFL is needed for t_ds_slide but is missing in example data
DISTRTFL = sample(c("Y", "N"), size = length(TRT01A), replace = TRUE, prob = c(.1, .9))
),
"adae" = pharmaverseadam::adae,
"adtte" = pharmaverseadam::adtte_onco,
"adrs" = pharmaverseadam::adrs_onco,
"adlb" = pharmaverseadam::adlb
)

# create outputs based on the specs and the functions
outputs <- spec_file %>%
read_spec() %>%
# we can also filter for specific programs:
filter_spec(., program %in% c("t_dm_slide")) %>%
# these filtered specs are now piped into the generate_outputs function.
# this function also requires the data
generate_outputs(datasets = data) %>%
# now we decorate based on the specs, i.e. add footnotes and titles
decorate_outputs(
version_label = NULL
)

## ----r------------------------------------------------------------------------
outputs$t_dm_slide_SE

## ----r------------------------------------------------------------------------
outputs %>%
generate_slides(
outfile = "presentation.pptx",
template = file.path(system.file(package = "autoslider.core"), "/theme/basic.pptx"),
table_format = autoslider_format
)
190 changes: 190 additions & 0 deletions digit_files/autoslider.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
title: "Slides"
order: 1
---

```{r setup script, include=FALSE, purl=FALSE}
invisible_hook_purl <- function(before, options, ...) {
knitr::hook_purl(before, options, ...)
NULL
}
knitr::knit_hooks$set(purl = invisible_hook_purl)
```


# Introduction


`AutoslideR` functions that are used for slide rendering and workflow are already open-sourced with the `autoslider.core` package. In this example, we show the general `autoslideR` workflow, how you can create functions from our templates and produce study-specific outputs, and how you can integrate them into the `autoslideR` framework to automate slide generation.


# Background

At its heart, `autoslideR` was created to tackle the inefficiencies and error-proneness of manual slide generation in clinical trials. It automates the creation of slides, significantly reducing the amount of work and time required, minimizing the risk of errors from manual data entry, and alleviating stress during demanding reporting periods.

`autoslideR` works by transforming your analysis data (SDTM, ADaM, raw, etc.) into standardized Table/Listing/Graph (TLG) objects. These objects are then decorated with titles, subtitles, and footnotes, positioning them at desired locations. With the addition of placeholder slides, the resulting PPTX file is ready for use in meetings.


# Prerequisites

First and foremost, you need to have the `autoslider.core` package installed, and you need to have data available. In this example, it uses example data stored in the `autoslider.core` package. The data needs to be stored in a named list (in this particular example, dataset names correspond to ADaM data sets).

## File structure


The folder structure could look something like:

```
├── programs
│ ├── run_script.R
│ ├── R
| | ├── template_functions.R
| | ├── output_functions.R
├── outputs
├── spec.yml
├── filters.yml
```

The `autoslideR` workflow would be implemented in the `run_script.R` file.
This workflow does not require the files in `programs/R/`.
However, if custom `output_functions.R` are implemented, `programs/R/` would be the place to put them.

The `autoslideR` workflow has four main aspects:

## The specifications `specs.yml`

This file contains the specifications of all outputs you would like to create.

For each output we define specific information, namely the program name, the footnotes & titles, the paper (this indicates the orientation, P for portrait and L for landscape, the number indicates the font size), the suffix and `args`.

It could look something like that:

```
- program: t_dm_slide
titles: Patient Demographics and Baseline Characteristics
footnotes: 't_dm_slide footnote'
paper: L6
suffix: SE
args:
arm: "TRT01A"
vars: ["SEX", "AGE"]

```

The program name refers to a function that produces an output.
This could be one of the template functions provided in `autoslider.core` or a custom function. See vignette [`adding_templates`](https://insightsengineering.github.io/autoslider.core/latest-tag/articles/adding_templates.html) for a detailed guide on using templates.

Titles and footnotes are added once the outputs are created.
We refer to that as decorating the outputs.

The suffix specifies the name of the filters that are applied to the data, before the data is funneled into the function (program).
The filters themselves are specified in the `filters.yml` file.

## The filters `filters.yml`

In `filters.yml` we specify the names of the filters used across the outputs.
Each filter has a name (e.g. `FAS`), a title (`Full Analysis Set`), and then the filtering condition on a target dataset.
The filter title may be appended to the output title. For the `t_dm_slide` slide above all filter titles that target the `adsl` dataset would be included in the brackets.
We would thus expect the title to read: "Patient Demographics and Baseline Characteristics (Full Analysis Set)".


As you can see, we don't just have population filters, but also filters on serious adverse events.
We can thus produce SAE tables by just supplying the serious adverse events to the AE table function.
This concept generalizes also to `PARAMCD` values.


```
ITT:
title: Intent to Treat Population
condition: ITTFL == "Y"
target: adsl
type: slref
SAS:
title: Secondary Analysis Set
condition: SASFL == "Y"
target: adsl
type: slref
SE:
title: Safety Evaluable Population
condition: SAFFL == "Y"
target: adsl
type: slref
SER:
title: Serious Adverse Events
condition: AESER == "Y"
target: adae
type: anl

```

# AutoslideR typical workflow example

A typical workflow could look something like this:

```{r setup}
library(autoslider.core)
library(dplyr)

# 1. Load ALL necessary packages
library(rtables) # For append_topleft()
library(assertthat) # For assert_that() you had issues with before
library(tern)

# define path to the yml files
spec_file <- file.path("metadata/autoslideR_spec.yml")

filters_file <- file.path("metadata/autoslideR_filters.yml")
# load all filters
filters::load_filters(filters_file, overwrite = TRUE)
```



```{r, table}
# read data
data <- list(
"adsl" = pharmaverseadam::adsl %>%
mutate(
FASFL = SAFFL, # add FASFL for illustrative purpose for t_pop_slide
# DISTRTFL is needed for t_ds_slide but is missing in example data
DISTRTFL = sample(c("Y", "N"), size = length(TRT01A), replace = TRUE, prob = c(.1, .9))
),
"adae" = pharmaverseadam::adae,
"adtte" = pharmaverseadam::adtte_onco,
"adrs" = pharmaverseadam::adrs_onco,
"adlb" = pharmaverseadam::adlb
)

# create outputs based on the specs and the functions
outputs <- spec_file %>%
read_spec() %>%
# we can also filter for specific programs:
filter_spec(., program %in% c("t_dm_slide")) %>%
# these filtered specs are now piped into the generate_outputs function.
# this function also requires the data
generate_outputs(datasets = data) %>%
# now we decorate based on the specs, i.e. add footnotes and titles
decorate_outputs(
version_label = NULL
)
```


We can have a look at one of the outputs stored in the outputs file:
```{r}
outputs$t_dm_slide_SE
```


```{r}
outputs %>%
generate_slides(
outfile = "presentation.pptx",
template = file.path(system.file(package = "autoslider.core"), "/theme/basic.pptx"),
table_format = autoslider_format
)
```

For the final product, when it includes more output, it may look like the following

![Example Reviewer's Guide Table](eg_slides.png){fig-align="center"}
96 changes: 96 additions & 0 deletions digit_files/docx.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
## ----r------------------------------------------------------------------------
library(tern)
library(dplyr)
library(rtables.officer)

# Load example datasets
adsl <- pharmaverseadam::adsl
adlb <- pharmaverseadam::adlb

# Convert character variables to factors and handle missing levels
adsl <- df_explicit_na(adsl)
adlb <- df_explicit_na(adlb)

# Create a temporary file for the output
tf <- tempfile(fileext = ".docx")

## ----r------------------------------------------------------------------------
adlb_f <- adlb %>%
dplyr::filter(
PARAM %in% c("Alanine Aminotransferase (U/L)", "Creatinine Kinase (U/L)") &
!(ACTARM == "B: Placebo" & AVISIT == "Week 2")
)

## ----r------------------------------------------------------------------------
afun <- function(x, .var, .spl_context, ...) {
n_fun <- sum(!is.na(x), na.rm = TRUE)
mean_sd_fun <- if (n_fun == 0) c(NA, NA) else c(mean(x, na.rm = TRUE), sd(x, na.rm = TRUE))
median_fun <- if (n_fun == 0) NA else median(x, na.rm = TRUE)
min_max_fun <- if (n_fun == 0) c(NA, NA) else c(min(x), max(x))

is_chg <- .var == "CHG"
is_baseline <- .spl_context$value[which(.spl_context$split == "AVISIT")] == "Baseline"
if (is_baseline && is_chg) n_fun <- mean_sd_fun <- median_fun <- min_max_fun <- NULL

in_rows(
"n" = n_fun,
"Mean (SD)" = mean_sd_fun,
"Median" = median_fun,
"Min - Max" = min_max_fun,
.formats = list("n" = "xx", "Mean (SD)" = "xx.xx (xx.xx)", "Median" = "xx.xx", "Min - Max" = "xx.xx - xx.xx"),
.format_na_strs = list("n" = "NE", "Mean (SD)" = "NE (NE)", "Median" = "NE", "Min - Max" = "NE - NE")
)
}

## ----r------------------------------------------------------------------------
lyt <- basic_table() %>%
split_cols_by("ACTARM", show_colcounts = TRUE, split_fun = keep_split_levels(levels(adlb_f$ACTARM)[c(1, 2)])) %>%
split_rows_by("PARAM",
split_fun = drop_split_levels, label_pos = "topleft",
split_label = obj_label(adlb_f$PARAM), page_by = TRUE
) %>%
split_rows_by("AVISIT",
split_fun = drop_split_levels, label_pos = "topleft",
split_label = obj_label(adlb_f$AVISIT)
) %>%
split_cols_by_multivar(
vars = c("AVAL", "CHG"),
varlabels = c("Value at Visit", "Change from Baseline")
) %>%
analyze_colvars(afun = afun)

## ----r------------------------------------------------------------------------
result <- build_table(lyt, adlb_f)
result

## ----r------------------------------------------------------------------------
main_title(result) <- "Alanine Aminotransferase Measurement"
subtitles(result) <- c("This is a subtitle.", "This is another subtitle.")
main_footer(result) <- "This is a demo table for illustration purpose."
prov_footer(result) <- "Program: demo_poc_docx.R\nDate: 2024-11-06\nVersion: 0.0.1\n"

## ----r------------------------------------------------------------------------
flx_res <- tt_to_flextable(result)
export_as_docx(flx_res,
file = tf,
section_properties = section_properties_default(orientation = "landscape")
)
flx_res

## ----r------------------------------------------------------------------------
cw <- propose_column_widths(result)
cw <- cw / sum(cw)
cw <- c(0.6, 0.1, 0.1, 0.1, 0.1)
spd <- section_properties_default(orientation = "landscape")
fin_cw <- cw * spd$page_size$width / 2 / sum(cw)

flex_tbl <- tt_to_flextable(result,
total_page_width = spd$page_size$width / 2,
counts_in_newline = TRUE,
autofit_to_page = FALSE,
bold_titles = TRUE,
colwidths = cw
)

export_as_docx(flex_tbl, file = tf)
flex_tbl
Loading