Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 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 that conform to the CDISC ARD standard. 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_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
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