Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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: cards
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should name the folder ARD instead of the package name?

- auto: digit_files
- auto: interactive
- auto: logging
Expand Down
56 changes: 56 additions & 0 deletions cards/cards_demographic.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## ----r preproc----------------------------------------------------------------
library(dplyr)

# Create categorical variables, remove screen failures, and assign column labels
adsl <- pharmaverseadam::adsl |>
filter(!ACTARM %in% "Screen Failure") |>
mutate(
SEX = case_match(SEX, "M" ~ "MALE", "F" ~ "FEMALE"),
AGEGR1 =
case_when(
between(AGE, 18, 40) ~ "18-40",
between(AGE, 41, 64) ~ "41-64",
AGE > 64 ~ ">=65"
) |>
factor(levels = c("18-40", "41-64", ">=65"))
) |>
labelled::set_variable_labels(
AGE = "Age (yr)",
AGEGR1 = "Age group",
SEX = "Sex",
RACE = "Race"
)

## ----r gtsummary-table--------------------------------------------------------
library(cards)
library(gtsummary)
theme_gtsummary_compact() # reduce default padding and font size for a gt table

# build the ARD with the needed summary statistics using {cards}
ard <-
ard_stack(
adsl,
ard_continuous(variables = AGE),
ard_categorical(variables = c(AGEGR1, SEX, RACE)),
.by = ACTARM, # split results by treatment arm
.attributes = TRUE # optionally include column labels in the ARD
)

# use the ARD to create a demographics table using {gtsummary}
tbl_ard_summary(
cards = ard,
by = ACTARM,
include = c(AGE, AGEGR1, SEX, RACE),
type = AGE ~ "continuous2",
statistic = AGE ~ c("{N}", "{mean} ({sd})", "{median} ({p25}, {p75})", "{min}, {max}")
) |>
bold_labels() |>
modify_header(all_stat_cols() ~ "**{level}** \nN = {n}") |> # add Ns to header
modify_footnote(everything() ~ NA) # remove default footnote

## ----r gtsummary-ard----------------------------------------------------------
# build demographics table directly from a data frame
tbl <- adsl |> tbl_summary(by = ACTARM, include = c(AGE, AGEGR1, SEX, RACE))

# extract ARD from table object
gather_ard(tbl)[[1]] |> select(-gts_column) # removing column so ARD fits on page
120 changes: 120 additions & 0 deletions cards/cards_demographic.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
title: "Demographic Table"
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)
knitr::opts_chunk$set(echo = TRUE)
```

## Introduction

This guide will show you how pharmaverse packages, along with some from tidyverse, can be used to create a Demographic table, using the `{pharmaverseadam}` `ADSL` data as an input.

### About CDISC Analysis Results Data (ARD)

Analysis Results Datasets (ARDs) are a core component of the emerging [CDISC Analysis Results Standard](https://www.cdisc.org/standards/foundational/analysis-results-standard). ARDs provide a standardized, machine-readable format for representing statistical analysis results, enabling:

- **Reproducibility**: ARDs capture the complete analysis metadata, making results reproducible and traceable
- **Automation**: Machine-readable formats allow automated generation of tables, listings, and graphs
- **Interoperability**: Standardized structures facilitate data exchange between systems and organizations
- **Regulatory Submissions**: ARDs support modern regulatory requirements for transparent and auditable analyses

### About the {cards} Package

The [{cards}](https://insightsengineering.github.io/cards/) package is a powerful R package designed to create Analysis Results Datasets (ARDs). Key features include:

- Creates ARDs with summary statistics for continuous and categorical variables
- Supports stratified analyses (e.g., by treatment arm)
- Integrates seamlessly with table-making packages like {gtsummary}
- Captures metadata such as variable labels and analysis context
- Provides a consistent data structure for downstream reporting

In the examples below, we illustrate how to create demographics tables using ARDs generated with {cards}, demonstrating the modern workflow for creating analysis results that are both human-readable and machine-actionable.

## Data preprocessing

Now we will add some pre-processing to create some extra formatted variables ready for display in the table.

```{r preproc}
#| message: false
library(dplyr)

# Create categorical variables, remove screen failures, and assign column labels
adsl <- pharmaverseadam::adsl |>
filter(!ACTARM %in% "Screen Failure") |>
mutate(
SEX = case_match(SEX, "M" ~ "MALE", "F" ~ "FEMALE"),
AGEGR1 =
case_when(
between(AGE, 18, 40) ~ "18-40",
between(AGE, 41, 64) ~ "41-64",
AGE > 64 ~ ">=65"
) |>
factor(levels = c("18-40", "41-64", ">=65"))
) |>
labelled::set_variable_labels(
AGE = "Age (yr)",
AGEGR1 = "Age group",
SEX = "Sex",
RACE = "Race"
)
```

## {gtsummary} & {cards}

In the example below, we will use the [{gtsummary}](https://www.danieldsjoberg.com/gtsummary/) and [{cards}](https://insightsengineering.github.io/cards/) packages to create a demographics tables.

- The {cards} package creates Analysis Results Datasets (ARDs, which are a part of the [CDISC Analysis Results Standard](https://www.cdisc.org/standards/foundational/analysis-results-standard)).
- The {gtsummary} utilizes ARDs to create tables.

#### ARD ➡ Table

In the example below, we first build an ARD with the needed summary statistics using {cards}.
Then, we use the ARD to build the demographics table with {gtsummary}.

```{r gtsummary-table}
#| message: false
library(cards)
library(gtsummary)
theme_gtsummary_compact() # reduce default padding and font size for a gt table

# build the ARD with the needed summary statistics using {cards}
ard <-
ard_stack(
adsl,
ard_summary(variables = AGE),
ard_tabulate(variables = c(AGEGR1, SEX, RACE)),
.by = ACTARM, # split results by treatment arm
.attributes = TRUE # optionally include column labels in the ARD
)

# use the ARD to create a demographics table using {gtsummary}
tbl_ard_summary(
cards = ard,
by = ACTARM,
include = c(AGE, AGEGR1, SEX, RACE),
type = AGE ~ "continuous2",
statistic = AGE ~ c("{N}", "{mean} ({sd})", "{median} ({p25}, {p75})", "{min}, {max}")
) |>
bold_labels() |>
modify_header(all_stat_cols() ~ "**{level}** \nN = {n}") |> # add Ns to header
modify_footnote(everything() ~ NA) # remove default footnote
```

#### Table ➡ ARD

One may also build the demographics in the classic way using `gtsummary::tbl_summary()` from a data frame, then extract the ARD from the table object.

```{r gtsummary-ard}
# build demographics table directly from a data frame
tbl <- adsl |> tbl_summary(by = ACTARM, include = c(AGE, AGEGR1, SEX, RACE))

# extract ARD from table object
gather_ard(tbl)[[1]] |> select(-gts_column) # removing column so ARD fits on page
Copy link
Collaborator

@ddsjoberg ddsjoberg Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had added [[1]] |> select(-gts_column) because the ARD print was running into multiple chunks. I think a better solution would be to increase the width of the print for this chunk. You can add this chunk just above the gtsummary-ard.

```{r}
#| include: false
options(width=120)
```

And we could reset after back to the default (which I am not sure what it is in quarto websites, but I think it's 90 in quarto slides...we could print getOption("width") to get the answer)

```
Copy link

Copilot AI Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider adding a "See also" section at the end of this file to provide a reciprocal link back to the traditional approach in the tlg section. For example:

## See also

For a traditional approach using `{rtables}` and `{tern}` without ARDs, see the [Demographic Table example](../tlg/demographic.qmd).

This would improve navigation and help users discover both approaches.

Suggested change
```

See also

For a traditional approach using {rtables} and {tern} without ARDs, see the Demographic Table example.

Copilot uses AI. Check for mistakes.
3 changes: 3 additions & 0 deletions cards/index.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
title: "CARDs"
---
3 changes: 3 additions & 0 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ ATPT
ATPTN
ATPTREF
attr
auditable
autofit
autolog
autoslider
Expand Down Expand Up @@ -185,6 +186,8 @@ cachem
cairo
cairoFT
callr
cards
CARDs
categorizationvars
cdisc
CDISC
Expand Down
61 changes: 5 additions & 56 deletions tlg/demographic.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ knitr::opts_chunk$set(echo = TRUE)

This guide will show you how pharmaverse packages, along with some from tidyverse, can be used to create a Demographic table, using the `{pharmaverseadam}` `ADSL` data as an input.

In the examples below, we illustrate two general approaches for creating a demographics table.
The first utilizes Analysis Results Datasets---part of the emerging [CDISC Analysis Results Standard](https://www.cdisc.org/standards/foundational/analysis-results-standard).
The second is the classic method of creating summary tables directly from a data set.
In the example below, we illustrate creating summary tables directly from a data set.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to also include the gtsummary solution here too?

tbl_summary(
  data = pharmaverseadam::adsl,
  by = ACTARM,
  include = c(AGE, AGEGR1, SEX, RACE),
  type = AGE ~ "continuous2",
  statistic = AGE ~ c("{mean} ({sd})", "{median} ({p25}, {p75})", "{min}, {max}")
) |>
  bold_labels() |>
  remove_footnote_header() # remove default footnote


## Data preprocessing

Expand Down Expand Up @@ -49,59 +47,6 @@ adsl <- pharmaverseadam::adsl |>
)
```

## {gtsummary} & {cards}

In the example below, we will use the [{gtsummary}](https://www.danieldsjoberg.com/gtsummary/) and [{cards}](https://insightsengineering.github.io/cards/) packages to create a demographics tables.

- The {cards} package creates Analysis Results Datasets (ARDs, which are a part of the [CDISC Analysis Results Standard](https://www.cdisc.org/standards/foundational/analysis-results-standard)).
- The {gtsummary} utilizes ARDs to create tables.

#### ARD ➡ Table

In the example below, we first build an ARD with the needed summary statistics using {cards}.
Then, we use the ARD to build the demographics table with {gtsummary}.

```{r gtsummary-table}
#| message: false
library(cards)
library(gtsummary)
theme_gtsummary_compact() # reduce default padding and font size for a gt table

# build the ARD with the needed summary statistics using {cards}
ard <-
ard_stack(
adsl,
ard_continuous(variables = AGE),
ard_categorical(variables = c(AGEGR1, SEX, RACE)),
.by = ACTARM, # split results by treatment arm
.attributes = TRUE # optionally include column labels in the ARD
)

# use the ARD to create a demographics table using {gtsummary}
tbl_ard_summary(
cards = ard,
by = ACTARM,
include = c(AGE, AGEGR1, SEX, RACE),
type = AGE ~ "continuous2",
statistic = AGE ~ c("{N}", "{mean} ({sd})", "{median} ({p25}, {p75})", "{min}, {max}")
) |>
bold_labels() |>
modify_header(all_stat_cols() ~ "**{level}** \nN = {n}") |> # add Ns to header
modify_footnote(everything() ~ NA) # remove default footnote
```

#### Table ➡ ARD

One may also build the demographics in the classic way using `gtsummary::tbl_summary()` from a data frame, then extract the ARD from the table object.

```{r gtsummary-ard}
# build demographics table directly from a data frame
tbl <- adsl |> tbl_summary(by = ACTARM, include = c(AGE, AGEGR1, SEX, RACE))

# extract ARD from table object
gather_ard(tbl)[[1]] |> select(-gts_column) # removing column so ARD fits on page
```

## {rtables} & {tern}

The packages used with a brief description of their purpose are as follows:
Expand Down Expand Up @@ -143,3 +88,7 @@ result <- build_table(lyt, adsl2)

result
```

## See also

For an alternative approach using Analysis Results Datasets (ARDs) and the [{cards}](https://insightsengineering.github.io/cards/) package, see the [CARDs Demographic Table example](../cards/cards_demographic.qmd).
Loading