Skip to content

Commit 266a3d7

Browse files
committed
#92 first pass at adding PK TLGs
1 parent a6a6bc4 commit 266a3d7

File tree

3 files changed

+373
-0
lines changed

3 files changed

+373
-0
lines changed

DESCRIPTION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ Imports:
2626
magrittr,
2727
metacore,
2828
metatools,
29+
nestcolor,
2930
pharmaversesdtm,
3031
pharmaverseadam,
3132
reactable,
3233
reactablefmtr,
34+
rlistings,
3335
sparkline,
3436
stringr,
3537
shinylive,

tlg/pharmacokinetic.R

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
## ----r setup, message=FALSE, warning=FALSE, results='hold'--------------------
2+
library(pharmaverseadam)
3+
library(tern)
4+
library(dplyr)
5+
library(ggplot2)
6+
library(nestcolor)
7+
library(rlistings)
8+
9+
10+
adsl <- pharmaverseadam::adsl %>%
11+
df_explicit_na()
12+
13+
# Keep only treated subjects for graph
14+
adsl_f <- adsl %>%
15+
filter(SAFFL == "Y" & TRT01A != "Placebo")
16+
17+
adpc <- pharmaverseadam::adpc %>%
18+
df_explicit_na()
19+
20+
# For ADPC keep only concentration records and treated subjects
21+
# Keep only plasma records for this example
22+
adpc <- adpc %>%
23+
filter(PARAMCD != "DOSE" & TRT01A != "Placebo" & PCSPEC == "PLASMA" & ANL02FL == "Y")
24+
25+
# Setting up the data
26+
adpc_1 <- adpc %>%
27+
mutate(
28+
NFRLT = as.factor(NFRLT),
29+
AVALCAT1 = as.factor(AVALCAT1),
30+
NOMTPT = as.factor(paste(NFRLT, "/", PCTPT))
31+
) %>%
32+
select(NOMTPT, ACTARM, VISIT, AVAL, PARAM, AVALCAT1)
33+
34+
adpc_1$NOMTPT <- factor(
35+
adpc_1$NOMTPT,
36+
levels = levels(adpc_1$NOMTPT)[order(as.numeric(gsub(".*?([0-9\\.]+).*", "\\1", levels(adpc_1$NOMTPT))))]
37+
)
38+
39+
# Row structure
40+
lyt_rows <- basic_table() %>%
41+
split_rows_by(
42+
var = "ACTARM",
43+
split_fun = drop_split_levels,
44+
split_label = "Treatment Group",
45+
label_pos = "topleft"
46+
) %>%
47+
add_rowcounts(alt_counts = TRUE) %>%
48+
split_rows_by(
49+
var = "VISIT",
50+
split_fun = drop_split_levels,
51+
split_label = "Visit",
52+
label_pos = "topleft"
53+
) %>%
54+
split_rows_by(
55+
var = "NOMTPT",
56+
split_fun = drop_split_levels,
57+
split_label = "Nominal Time (hr) / Timepoint",
58+
label_pos = "topleft",
59+
child_labels = "hidden"
60+
)
61+
62+
## ----r table------------------------------------------------------------------
63+
lyt <- lyt_rows %>%
64+
analyze_vars_in_cols(
65+
vars = c("AVAL", "AVALCAT1", rep("AVAL", 8)),
66+
.stats = c("n", "n_blq", "mean", "sd", "cv", "geom_mean", "geom_cv", "median", "min", "max"),
67+
.formats = c(
68+
n = "xx.", n_blq = "xx.", mean = format_sigfig(3), sd = format_sigfig(3), cv = "xx.x", median = format_sigfig(3),
69+
geom_mean = format_sigfig(3), geom_cv = "xx.x", min = format_sigfig(3), max = format_sigfig(3)
70+
),
71+
.labels = c(
72+
n = "n", n_blq = "Number\nof\nLTRs/BLQs", mean = "Mean", sd = "SD", cv = "CV (%) Mean",
73+
geom_mean = "Geometric Mean", geom_cv = "CV % Geometric Mean", median = "Median", min = "Minimum", max = "Maximum"
74+
),
75+
na_str = "NE",
76+
.aligns = "decimal"
77+
)
78+
79+
result <- build_table(lyt, df = adpc_1, alt_counts_df = adsl) %>% prune_table()
80+
81+
# Decorating
82+
main_title(result) <- "Summary of PK Concentrations by Nominal Time and Treatment: PK Evaluable"
83+
subtitles(result) <- c(
84+
"Protocol: xxxxx",
85+
paste("Analyte: ", unique(adpc_1$PARAM)),
86+
paste("Treatment:", unique(adpc_1$ACTARM))
87+
)
88+
main_footer(result) <- "NE: Not Estimable"
89+
90+
result
91+
92+
## ----r graph------------------------------------------------------------------
93+
use_title <- "Plot of Mean (+/- SD) Plasma Concentrations Over Time by Treatment, \nPK Evaluable Patients"
94+
use_subtitle <- "Analyte:"
95+
use_footnote <- "Program: \nOutput:"
96+
97+
result <- g_lineplot(
98+
df = adpc,
99+
variables = control_lineplot_vars(
100+
x = "NFRLT",
101+
y = "AVAL",
102+
group_var = "ARM",
103+
paramcd = "PARAM",
104+
y_unit = "AVALU",
105+
subject_var = "USUBJID"
106+
),
107+
alt_counts_df = adsl_f,
108+
position = ggplot2::position_dodge2(width = 0.5),
109+
y_lab = "Concentration",
110+
y_lab_add_paramcd = FALSE,
111+
y_lab_add_unit = TRUE,
112+
interval = "mean_sdi",
113+
whiskers = c("mean_sdi_lwr", "mean_sdi_upr"),
114+
title = use_title,
115+
subtitle = use_subtitle,
116+
caption = use_footnote,
117+
ggtheme = theme_nest()
118+
)
119+
120+
plot <- result + theme(plot.caption = element_text(hjust = 0))
121+
plot
122+
123+
## ----r listing----------------------------------------------------------------
124+
analyte <- unique(adpc$PARAM)
125+
126+
out <- adpc %>%
127+
select(ARM, USUBJID, VISIT, NFRLT, AFRLT, AVALCAT1)
128+
129+
130+
var_labels(out) <- c(
131+
ARM = "Treatment Group",
132+
USUBJID = "Subject ID",
133+
VISIT = "Visit",
134+
NFRLT = paste0("Nominal\nSampling\nTime (", adpc$RRLTU[1], ")"),
135+
AFRLT = paste0("Actual Time\nFrom First\nDose (", adpc$RRLTU[1], ")"),
136+
AVALCAT1 = paste0("Concentration\n(", adpc$AVALU[1], ")")
137+
)
138+
139+
lsting <- as_listing(
140+
out,
141+
key_cols = c("ARM", "USUBJID", "VISIT"),
142+
disp_cols = names(out),
143+
default_formatting = list(
144+
all = fmt_config(align = "left"),
145+
numeric = fmt_config(
146+
format = "xx.xx",
147+
na_str = " ",
148+
align = "right"
149+
)
150+
),
151+
main_title = paste(
152+
"Listing of",
153+
analyte,
154+
"Concentration by Treatment Group, Subject and Nominal Time, PK Population\nProtocol: xxnnnnn"
155+
),
156+
subtitles = paste("Analyte:", analyte)
157+
)
158+
159+
head(lsting, 28)
160+

tlg/pharmacokinetic.qmd

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
---
2+
title: "Pharmacokinetic"
3+
order: 3
4+
---
5+
6+
7+
```{r setup script, include=FALSE, purl=FALSE}
8+
invisible_hook_purl <- function(before, options, ...) {
9+
knitr::hook_purl(before, options, ...)
10+
NULL
11+
}
12+
knitr::knit_hooks$set(purl = invisible_hook_purl)
13+
```
14+
15+
## Introduction
16+
17+
This guide will show you how pharmaverse packages, along with some from tidyverse, can be used to create pharmacokinetic (PK) tables, listings and graphs, using the `{pharmaverseadam}` `ADSL` and `ADPC` data as an input.
18+
19+
The packages used with a brief description of their purpose are as follows:
20+
21+
* [`{rtables}`](https://insightsengineering.github.io/rtables/): designed to create and display complex tables with R.
22+
* [`{tern}`](https://insightsengineering.github.io/tern/): contains analysis functions to create tables and graphs used for clinical trial reporting.
23+
* [`{rlistings}`](https://insightsengineering.github.io/rlistings/): contains framework for creating listings for clinical reporting.
24+
25+
See catalog for PK TLGs here [PK TLG catalog](https://insightsengineering.github.io/tlg-catalog/stable/)
26+
27+
## Load Data and Required pharmaverse Package
28+
29+
After installation of packages, the first step is to load our pharmaverse packages and input data. Here, we are going to encode missing entries in a data frame `adsl` and `adpc`.
30+
31+
Note that `{tern}` depends on `{rtables}` so the latter is automatically attached.
32+
33+
```{r setup, message=FALSE, warning=FALSE, results='hold'}
34+
library(pharmaverseadam)
35+
library(tern)
36+
library(dplyr)
37+
library(ggplot2)
38+
library(nestcolor)
39+
library(rlistings)
40+
41+
42+
adsl <- pharmaverseadam::adsl %>%
43+
df_explicit_na()
44+
45+
# Keep only treated subjects for graph
46+
adsl_f <- adsl %>%
47+
filter(SAFFL == "Y" & TRT01A != "Placebo")
48+
49+
adpc <- pharmaverseadam::adpc %>%
50+
df_explicit_na()
51+
52+
# For ADPC keep only concentration records and treated subjects
53+
# Keep only plasma records for this example
54+
adpc <- adpc %>%
55+
filter(PARAMCD != "DOSE" & TRT01A != "Placebo" & PCSPEC == "PLASMA" & ANL02FL == "Y")
56+
57+
# Setting up the data
58+
adpc_1 <- adpc %>%
59+
mutate(
60+
NFRLT = as.factor(NFRLT),
61+
AVALCAT1 = as.factor(AVALCAT1),
62+
NOMTPT = as.factor(paste(NFRLT, "/", PCTPT))
63+
) %>%
64+
select(NOMTPT, ACTARM, VISIT, AVAL, PARAM, AVALCAT1)
65+
66+
adpc_1$NOMTPT <- factor(
67+
adpc_1$NOMTPT,
68+
levels = levels(adpc_1$NOMTPT)[order(as.numeric(gsub(".*?([0-9\\.]+).*", "\\1", levels(adpc_1$NOMTPT))))]
69+
)
70+
71+
# Row structure
72+
lyt_rows <- basic_table() %>%
73+
split_rows_by(
74+
var = "ACTARM",
75+
split_fun = drop_split_levels,
76+
split_label = "Treatment Group",
77+
label_pos = "topleft"
78+
) %>%
79+
add_rowcounts(alt_counts = TRUE) %>%
80+
split_rows_by(
81+
var = "VISIT",
82+
split_fun = drop_split_levels,
83+
split_label = "Visit",
84+
label_pos = "topleft"
85+
) %>%
86+
split_rows_by(
87+
var = "NOMTPT",
88+
split_fun = drop_split_levels,
89+
split_label = "Nominal Time (hr) / Timepoint",
90+
label_pos = "topleft",
91+
child_labels = "hidden"
92+
)
93+
```
94+
95+
## PK Table
96+
97+
Now we create the PK table.
98+
99+
```{r table}
100+
lyt <- lyt_rows %>%
101+
analyze_vars_in_cols(
102+
vars = c("AVAL", "AVALCAT1", rep("AVAL", 8)),
103+
.stats = c("n", "n_blq", "mean", "sd", "cv", "geom_mean", "geom_cv", "median", "min", "max"),
104+
.formats = c(
105+
n = "xx.", n_blq = "xx.", mean = format_sigfig(3), sd = format_sigfig(3), cv = "xx.x", median = format_sigfig(3),
106+
geom_mean = format_sigfig(3), geom_cv = "xx.x", min = format_sigfig(3), max = format_sigfig(3)
107+
),
108+
.labels = c(
109+
n = "n", n_blq = "Number\nof\nLTRs/BLQs", mean = "Mean", sd = "SD", cv = "CV (%) Mean",
110+
geom_mean = "Geometric Mean", geom_cv = "CV % Geometric Mean", median = "Median", min = "Minimum", max = "Maximum"
111+
),
112+
na_str = "NE",
113+
.aligns = "decimal"
114+
)
115+
116+
result <- build_table(lyt, df = adpc_1, alt_counts_df = adsl) %>% prune_table()
117+
118+
# Decorating
119+
main_title(result) <- "Summary of PK Concentrations by Nominal Time and Treatment: PK Evaluable"
120+
subtitles(result) <- c(
121+
"Protocol: xxxxx",
122+
paste("Analyte: ", unique(adpc_1$PARAM)),
123+
paste("Treatment:", unique(adpc_1$ACTARM))
124+
)
125+
main_footer(result) <- "NE: Not Estimable"
126+
127+
result
128+
```
129+
130+
131+
## PK Graph
132+
133+
Now we create the PK graph.
134+
135+
```{r graph}
136+
use_title <- "Plot of Mean (+/- SD) Plasma Concentrations Over Time by Treatment, \nPK Evaluable Patients"
137+
use_subtitle <- "Analyte:"
138+
use_footnote <- "Program: \nOutput:"
139+
140+
result <- g_lineplot(
141+
df = adpc,
142+
variables = control_lineplot_vars(
143+
x = "NFRLT",
144+
y = "AVAL",
145+
group_var = "ARM",
146+
paramcd = "PARAM",
147+
y_unit = "AVALU",
148+
subject_var = "USUBJID"
149+
),
150+
alt_counts_df = adsl_f,
151+
position = ggplot2::position_dodge2(width = 0.5),
152+
y_lab = "Concentration",
153+
y_lab_add_paramcd = FALSE,
154+
y_lab_add_unit = TRUE,
155+
interval = "mean_sdi",
156+
whiskers = c("mean_sdi_lwr", "mean_sdi_upr"),
157+
title = use_title,
158+
subtitle = use_subtitle,
159+
caption = use_footnote,
160+
ggtheme = theme_nest()
161+
)
162+
163+
plot <- result + theme(plot.caption = element_text(hjust = 0))
164+
plot
165+
```
166+
167+
## PK Listing
168+
169+
Now we create an example PK listing.
170+
171+
```{r listing}
172+
analyte <- unique(adpc$PARAM)
173+
174+
out <- adpc %>%
175+
select(ARM, USUBJID, VISIT, NFRLT, AFRLT, AVALCAT1)
176+
177+
178+
var_labels(out) <- c(
179+
ARM = "Treatment Group",
180+
USUBJID = "Subject ID",
181+
VISIT = "Visit",
182+
NFRLT = paste0("Nominal\nSampling\nTime (", adpc$RRLTU[1], ")"),
183+
AFRLT = paste0("Actual Time\nFrom First\nDose (", adpc$RRLTU[1], ")"),
184+
AVALCAT1 = paste0("Concentration\n(", adpc$AVALU[1], ")")
185+
)
186+
187+
lsting <- as_listing(
188+
out,
189+
key_cols = c("ARM", "USUBJID", "VISIT"),
190+
disp_cols = names(out),
191+
default_formatting = list(
192+
all = fmt_config(align = "left"),
193+
numeric = fmt_config(
194+
format = "xx.xx",
195+
na_str = " ",
196+
align = "right"
197+
)
198+
),
199+
main_title = paste(
200+
"Listing of",
201+
analyte,
202+
"Concentration by Treatment Group, Subject and Nominal Time, PK Population\nProtocol: xxnnnnn"
203+
),
204+
subtitles = paste("Analyte:", analyte)
205+
)
206+
207+
head(lsting, 28)
208+
```
209+
210+
211+

0 commit comments

Comments
 (0)