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
3 changes: 3 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ Imports:
grid,
methods
Suggests:
dplyr,
flextable (>= 0.8.0),
ggplot2,
gridGraphics,
gt (>= 0.11.0),
gtable,
knitr,
magrittr,
random.cdisc.data,
rmarkdown,
scales,
spelling,
testthat (>= 3.0.0)
Collate:
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# gridify 0.8.1.9000

## Documentation

* Added the "Posit Table Contest 2025: The Best Pharma Table" vignette
(`vignette("table_contest")`) showcasing the winning entry of the pharma
table category, which combines `gt` styling with `gridify` framing (#27).

# gridify 0.8.1

## New features
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ For more information please visit the following vignettes:
- **Transparency of gridify**
`vignette("transparency", package = "gridify")` - How to extract the
raw grid code to reproduce a `gridify` object.
- **Posit Table Contest 2025: The Best Pharma Table**
`vignette("table_contest", package = "gridify")` - The winning entry of the
pharma table category, combining `gt` styling with `gridify` regulatory framing.

## Posit Table Contest 2025

A `gt` table framed with `gridify` won **The Best Pharma Table** category of the
[2025 Posit Table Contest](https://posit.co/blog/2025-table-contest-winners/).
See the `table_contest` vignette or the original code at
[Polkas/tgcontest2025](https://github.com/Polkas/tgcontest2025).

## Non Editable Outputs

Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ articles:
- multi_page_examples
- create_custom_layout
- transparency
- table_contest

reference:
- title: gridify Users
Expand Down
3 changes: 3 additions & 0 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Abdy
Acknowledgments
Bleier
CDISC
CMD
Codecov
Dallimore
Expand All @@ -19,6 +20,7 @@ Pharmaverse
Qmd
RStudio
Rmd
TLGs
TransactionID
Vicencio
centered
Expand Down Expand Up @@ -48,5 +50,6 @@ px
rd
rtables
sprintf
tgcontest
unitType
vjust
206 changes: 206 additions & 0 deletions vignettes/table_contest.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
---
title: "Posit Table Contest 2025: The Best Pharma Table"
output:
rmarkdown::html_vignette:
toc: true
toc_depth: 4
vignette: >
%\VignetteIndexEntry{Posit Table Contest 2025: The Best Pharma Table}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
editor_options:
markdown:
wrap: 72
---

```{r, include = FALSE}
required_pkgs <- c("gt", "gtable", "dplyr", "scales", "random.cdisc.data")
has_pkgs <- all(vapply(required_pkgs, requireNamespace, logical(1), quietly = TRUE))

knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.width = 10,
fig.height = 8.7,
eval = has_pkgs
)
```

```{r, echo = FALSE, results = "asis", eval = !has_pkgs}
cat(
"> **Note:** The code in this vignette was not evaluated because one or more",
"of the following packages are not installed:",
paste0("`", required_pkgs, "`", collapse = ", "), "\n"
)
```

## Introduction

This example won **The Best Pharma Table** category of the
[2025 Posit Table Contest](https://posit.co/blog/2025-table-contest-winners/).

- Contest submission:
[Submission-Ready and Elegant Clinical Tables: Combining {gt} Styling with {gridify} Layout Control](https://github.com/rich-iannone/table-contest/discussions/18)
- Original code: [Polkas/tgcontest2025](https://github.com/Polkas/tgcontest2025)

Clinical reporting requires tables that meet both statistical rigour and
regulatory standards. While pharmaverse packages handle the analysis well,
a gap remains in the consistent and reproducible framing of Tables, Listings,
and Graphs (TLGs) with the headers, footers, compliance notices, and metadata
required for submissions.

This example combines `gt` for table styling with `gridify` for
regulatory-compliant framing. The table shows a CDISC ADSL baseline
characteristic, Biomarker 1 (`BMRKR1`), by treatment arm and geographic
region with:

- summary statistics: n and Mean (SD), with gradient colour encoding,
- complete regulatory framing: protocol information, data cut-off,
confidentiality notice, and traceability.

All data are synthetic, generated by the `random.cdisc.data` package.
No real patient data are used, and the company, study, and drug
information are fictional.

## Setup

```{r setup, message = FALSE}
library(gridify)
library(gt)
library(dplyr)

primary_color <- "#08306b"
secondary_color <- "#6baed6"
```

## Data Preparation

The ADSL dataset is summarised by treatment arm and region.
The `BMRKR1` column is stored as a factor ordered by the mean value,
so that the colour gradient applied later follows the mean.

```{r data}
adsl <- random.cdisc.data::cadsl |>
mutate(
ARM = factor(ARM),
REGION1 = factor(REGION1)
)

summary_tbl <- adsl |>
group_by(ARM, REGION1) |>
summarise(
N = n(),
MEAN_BMRKR1 = round(mean(BMRKR1, na.rm = TRUE), 2),
SD_BMRKR1 = round(sd(BMRKR1, na.rm = TRUE), 2),
.groups = "drop"
) |>
mutate(
BMRKR1 = sprintf("%0.2f (%0.2f)", MEAN_BMRKR1, SD_BMRKR1),
BMRKR1 = factor(BMRKR1, levels = unique(BMRKR1[order(MEAN_BMRKR1)]))
) |>
select(ARM, REGION1, N, BMRKR1)

summary_tbl
```

## The gt Table

```{r gt_table}
color_palette <- scales::col_factor(
palette = c("white", secondary_color, primary_color),
domain = NULL
)

gt_tbl <- summary_tbl |>
gt(rowname_col = NULL, groupname_col = "ARM") |>
cols_label(
REGION1 = "Region",
N = "n",
BMRKR1 = "BMRKR1"
) |>
cols_width(
ARM ~ pct(30),
REGION1 ~ pct(30),
N ~ pct(10),
BMRKR1 ~ pct(30)
) |>
cols_align(
align = "center",
columns = N
) |>
tab_options(
table.width = pct(70),
table.font.size = 16,
data_row.padding = px(6),
table.font.color = "black",
table.font.weight = "400",
heading.align = "left",
table.background.color = "gray98",
heading.background.color = "gray98",
column_labels.background.color = "gray95",
row_group.font.weight = "bold",
row_group.as_column = TRUE
) |>
opt_row_striping() |>
data_color(columns = BMRKR1, fn = color_palette) |>
data_color(columns = N, fn = color_palette) |>
tab_style(
style = cell_text(weight = "bold", color = primary_color),
locations = cells_column_labels()
)
```

## Framing with gridify

The `gt` table is passed to `gridify()` with the predefined
`pharma_layout_base()` layout. Custom margins and a monospaced font are
used, and the cells are filled with `set_cell()`.

```{r gridify}
pharma_gt <- gridify(
gt_tbl,
layout = pharma_layout_base(
margin = grid::unit(c(t = 0.5, r = 0.5, b = 0.5, l = 0.5), "inch"),
global_gpar = list(
fontfamily = "mono",
fontsize = 14,
col = "black"
)
)
) |>
set_cell("header_left_1", "PharmaCorp International") |>
set_cell("header_left_2", "ONCOLOGY / Advanced NSCLC") |>
set_cell("header_left_3", "Study PC-2025-001") |>
set_cell("header_right_1", "CONFIDENTIAL") |>
set_cell("header_right_2", "Final") |>
set_cell("header_right_3", "Data Cut-off: 2025-09-30") |>
set_cell("output_num", "Table 14.1.1") |>
set_cell("title_1", "Summary of Demographics and Baseline Characteristics") |>
set_cell("title_2", "Baseline Biomarker 1 (BMRKR1) by Geographic Region") |>
set_cell("title_3", "Safety Analysis Set") |>
set_cell("note", "Note: BMRKR1 values shown as Mean (SD).") |>
set_cell("references", "Source: ADSL dataset random.cdisc.data") |>
set_cell("footer_left", "Program: table_posit.R, 2025-10-01 12:00 UTC") |>
set_cell("footer_right", "Page 1 of 1")

pharma_gt
```

In a production program, the `footer_left` cell would typically contain
the run time, e.g.
`sprintf("Program: table_posit.R, %s", format(Sys.time(), "%Y-%m-%d %H:%M %Z"))`.

## Export

The final output can be exported to PNG or PDF with `export_to()`.

```{r export, eval = FALSE}
pharma_gt |>
export_to("polkas_table_contest_2025.png", width = 3000, height = 2600, res = 300)

pharma_gt |>
export_to("polkas_table_contest_2025.pdf")
```

To retrieve the raw `grid` code behind the output, see
`vignette("transparency", package = "gridify")`.
Loading