This example produces a subject disposition table. The report features extensive data preparation using the dplyr package. The example also shows how to create a stub column with summary rows.
library(tidyverse)
library(sassy)
# Prepare Log -------------------------------------------------------------
options("logr.autolog" = TRUE,
"logr.notes" = FALSE)
# Get path to temp directory
<- tempdir()
tmp
# Get sample data directory
<- system.file("extdata", package = "sassy")
dir
# Open log
<- log_open(file.path(tmp, "example10.log"))
lgpth
# Load and Prepare Data ---------------------------------------------------
sep("Prepare Data")
# Define data library
libname(sdtm, dir, "csv")
# Loads data into workspace
lib_load(sdtm)
# Prepare data
<- sdtm.DM |>
dm_mod select(USUBJID, ARM) |>
filter(ARM != "SCREEN FAILURE") |> put()
<- sdtm.DS |>
ds_mod select(USUBJID, DSTERM, DSDECOD, DSCAT) |>
filter(DSCAT != "PROTOCOL MILESTONE")
put("Join DS to DM")
<- inner_join(dm_mod, ds_mod)
dmds
put("Get ARM population counts")
<- count(dm_mod, ARM) |> deframe() |> put()
arm_pop
# Prepare formats ---------------------------------------------------------
# Completed Study
<- value(condition(x == "SUBJECT COMPLETED ALL VISITS AND PROTOCOL REQUIREMENTS",
complete_fmt str_to_title("SUBJECT COMPLETED ALL VISITS AND PROTOCOL REQUIREMENTS")),
condition(x == "SUBJECT COMPLETED ALL VISITS BUT WITH MAJOR PROTOCOL VIOLATIONS",
str_to_title("SUBJECT COMPLETED ALL VISITS BUT WITH MAJOR PROTOCOL VIOLATIONS")))
# Subject Non-compliance
<- value(condition(x == "NON-COMPLIANCE WITH STUDY DRUG",
noncomp_fmt str_to_title("NON-COMPLIANCE WITH STUDY DRUG")))
# Early Termination
<- value(condition(x == "LACK OF EFFICACY",
term_fmt str_to_title("LACK OF EFFICACY")),
condition(str_detect(x, "LOST"),
str_to_title("LOST TO FOLLOW UP")),
condition(TRUE, str_to_title("LACK OF EFFICACY")))
# Group labels
<- value(condition(x == "COMPLETED", "Subjects who Completed Study"),
group_fmt condition(x == "NONCOMPLIANCE", "Subjects terminated due to non-compliance"),
condition(x == "OTHER", "Subjects who terminated early"))
# Disposition Groups ------------------------------------------------------
put("Get group counts")
<- dmds |> select(ARM, DSDECOD) |>
grps group_by(ARM, DSDECOD) |>
summarize(n = n()) |>
ungroup() |>
pivot_wider(names_from = ARM,
values_from = n,
values_fill = 0) |>
transmute(group = ifelse(DSDECOD == "NON-COMPLIANCE WITH STUDY DRUG", "NONCOMPLIANCE", DSDECOD),
cat = NA,
catseq = 1,
`ARM A` = fmt_cnt_pct(`ARM A`, arm_pop["ARM A"]),
`ARM B` = fmt_cnt_pct(`ARM B`, arm_pop["ARM B"]),
`ARM C` = fmt_cnt_pct(`ARM C`, arm_pop["ARM C"]),
`ARM D` = fmt_cnt_pct(`ARM D`, arm_pop["ARM D"])) |> put()
# Disposition Subgroups ----------------------------------------------------
put("Pull out subjects who completed study.")
<- dmds |> filter(DSDECOD == "COMPLETED") |>
cmplt mutate(TERMDECOD = fapply(DSTERM, complete_fmt)) |>
group_by(ARM, DSDECOD, TERMDECOD) |>
summarize(n = n()) |>
ungroup() |>
pivot_wider(names_from = ARM,
values_from = n,
values_fill = 0) |>
transmute(group = "COMPLETED",
cat = TERMDECOD,
catseq = 2,
`ARM A` = fmt_cnt_pct(`ARM A`, arm_pop["ARM A"]),
`ARM B` = fmt_cnt_pct(`ARM B`, arm_pop["ARM B"]),
`ARM C` = fmt_cnt_pct(`ARM C`, arm_pop["ARM C"]),
`ARM D` = fmt_cnt_pct(`ARM D`, arm_pop["ARM D"])) |> put()
put("Pull out subjects who were non-compliant")
<- dmds |> filter(DSDECOD == "NON-COMPLIANCE WITH STUDY DRUG") |>
noncompl1 mutate(TERMDECOD = fapply(DSTERM, noncomp_fmt)) |>
group_by(ARM, DSDECOD, TERMDECOD) |>
summarize(n = n()) |>
ungroup() |>
pivot_wider(names_from = ARM,
values_from = n,
values_fill = 0)
<- names(noncompl1)
nms
<- noncompl1 |>
noncompl2 transmute(group = "NONCOMPLIANCE",
cat = TERMDECOD,
catseq = 2,
`ARM A` = ifelse("ARM A" %in% nms, fmt_cnt_pct(`ARM A`, arm_pop["ARM A"]), "0 ( 0.0%)"),
`ARM B` = ifelse("ARM B" %in% nms, fmt_cnt_pct(`ARM B`, arm_pop["ARM B"]), "0 ( 0.0%)"),
`ARM C` = ifelse("ARM C" %in% nms, fmt_cnt_pct(`ARM C`, arm_pop["ARM C"]), "0 ( 0.0%)"),
`ARM D` = ifelse("ARM D" %in% nms, fmt_cnt_pct(`ARM D`, arm_pop["ARM D"]), "0 ( 0.0%)")) |> put()
put("Pull out subjects who terminated early")
<- dmds |> filter(DSDECOD == "OTHER") |>
earlyterm mutate(TERMDECOD = fapply(DSTERM, term_fmt)) |>
group_by(ARM, DSDECOD, TERMDECOD) |>
summarize(n = n()) |>
ungroup() |>
pivot_wider(names_from = ARM,
values_from = n,
values_fill = 0) |>
transmute(group = "OTHER",
cat = TERMDECOD,
catseq = 2,
`ARM A` = fmt_cnt_pct(`ARM A`, arm_pop["ARM A"]),
`ARM B` = fmt_cnt_pct(`ARM B`, arm_pop["ARM B"]),
`ARM C` = fmt_cnt_pct(`ARM C`, arm_pop["ARM C"]),
`ARM D` = fmt_cnt_pct(`ARM D`, arm_pop["ARM D"])) |> put()
put("Combine blocks into final data frame")
<- bind_rows(grps, cmplt, noncompl2, earlyterm) |>
final arrange(group, catseq, cat) |>
mutate(lblind = ifelse(is.na(cat), TRUE, FALSE)) |> put()
# Report ------------------------------------------------------------------
sep("Create and print report")
# Create Table
<- create_table(final, first_row_blank = TRUE,
tbl borders = "all", width = 8.5, header_bold = TRUE) |>
column_defaults(from = `ARM A`, to = `ARM D`,
align = "center", width = 1) |>
stub(vars = v(group, cat), "Completion Status",
style = cell_style(bold = TRUE, indicator = "lblind")) |>
define(group, blank_after = TRUE, dedupe = TRUE,
format = group_fmt) |>
define(cat, indent = .5) |>
define(catseq, visible = FALSE) |>
define(`ARM A`, label = "Placebo", n = arm_pop["ARM A"]) |>
define(`ARM B`, label = "Drug 50mg", n = arm_pop["ARM B"]) |>
define(`ARM C`, label = "Drug 100mg", n = arm_pop["ARM C"]) |>
define(`ARM D`, label = "Competitor", n = arm_pop["ARM D"]) |>
define(lblind, visible = FALSE) |>
titles("Table 5.2.3", "Subject Disposition by Category and Treatment Group",
"Safety Population", bold = TRUE, font_size = 11,
borders = "outside", blank_row = "none") |>
footnotes("Program: DS_Table.R",
"NOTE: Denominator based on number of non-missing responses.",
borders = "outside", blank_row = "none")
<- file.path(tmp, "example10.pdf")
pth
<- create_report(pth, output_type = "PDF", font = "Arial") |>
rpt set_margins(top = 1, bottom = 1) |>
add_content(tbl)
write_report(rpt)
# Clean Up ----------------------------------------------------------------
# Unload library from workspace
lib_unload(sdtm)
# Close log
log_close()
# Uncomment to view files
# file.show(pth)
# file.show(lgpth)
Here is the output report:
Here is the log:
=========================================================================
Log Path: C:/Users/dbosa/AppData/Local/Temp/RtmpeYEHwE/log/example10.log
Program Path: C:/packages/Testing/Disposition.R
Working Directory: C:/packages/Testing
User Name: dbosa
R Version: 4.2.1 (2022-06-23 ucrt)
Machine: SOCRATES x86-64
Operating System: Windows 10 x64 build 19044
Base Packages: stats graphics grDevices utils datasets methods base Other
Packages: tidylog_1.0.2 reporter_1.3.7 libr_1.2.6 fmtr_1.5.9 logr_1.3.3
sassy_1.0.8 forcats_0.5.2 stringr_1.4.0 dplyr_1.0.10 purrr_0.3.4 readr_2.1.2
tidyr_1.2.0 tibble_3.1.8 ggplot2_3.3.6 tidyverse_1.3.1 cranlogs_2.1.1
common_1.0.5
Log Start Time: 2022-10-09 20:51:10
=========================================================================
=========================================================================
Prepare Data
=========================================================================
# library 'sdtm': 8 items
- attributes: csv not loaded
- path: C:/Users/dbosa/AppData/Local/R/win-library/4.2/sassy/extdata
- items:
Name Extension Rows Cols Size LastModified
1 AE csv 150 27 88.5 Kb 2022-09-05 10:57:47
2 DA csv 3587 18 528.2 Kb 2022-09-05 10:57:47
3 DM csv 87 24 45.5 Kb 2022-09-05 10:57:47
4 DS csv 174 9 34.1 Kb 2022-09-05 10:57:47
5 EX csv 84 11 26.4 Kb 2022-09-05 10:57:47
6 IE csv 2 14 13.4 Kb 2022-09-05 10:57:47
7 SV csv 685 10 70.3 Kb 2022-09-05 10:57:47
8 VS csv 3358 17 467.4 Kb 2022-09-05 10:57:47
lib_load: library 'sdtm' loaded
select: dropped 22 variables (STUDYID, DOMAIN, SUBJID, RFSTDTC, RFENDTC, …)
filter: removed 2 rows (2%), 85 rows remaining
# A tibble: 85 × 2
USUBJID ARM
<chr> <chr>
1 ABC-01-049 ARM D
2 ABC-01-050 ARM B
3 ABC-01-051 ARM A
4 ABC-01-052 ARM C
5 ABC-01-053 ARM B
6 ABC-01-054 ARM D
7 ABC-01-055 ARM C
8 ABC-01-056 ARM A
9 ABC-01-113 ARM D
10 ABC-01-114 ARM B
# … with 75 more rows
# ℹ Use `print(n = ...)` to see more rows
select: dropped 5 variables (STUDYID, DOMAIN, DSSEQ, DSSTDTC, DSSTDY)
filter: removed 87 rows (50%), 87 rows remaining
Join DS to DM
inner_join: added 3 columns (DSTERM, DSDECOD, DSCAT)
> rows only in x ( 0)
> rows only in y ( 2)
> matched rows 85
> ====
> rows total 85
Get ARM population counts
count: now 4 rows and 2 columns, ungrouped
ARM A ARM B ARM C ARM D
20 21 21 23
# A user-defined format: 2 conditions
Name Type Expression
1 obj U x == "SUBJECT COMPLETED ALL VISITS AND PROTOCOL REQUIREMENTS"
2 obj U x == "SUBJECT COMPLETED ALL VISITS BUT WITH MAJOR PROTOCOL VIOLATIONS"
Label Order
1 Subject Completed All Visits And Protocol Requirements NA
2 Subject Completed All Visits But With Major Protocol Violations NA
# A user-defined format: 1 conditions
Name Type Expression Label Order
1 obj U x == "NON-COMPLIANCE WITH STUDY DRUG" Non-Compliance With Study Drug NA
# A user-defined format: 3 conditions
Name Type Expression Label Order
1 obj U x == "LACK OF EFFICACY" Lack Of Efficacy NA
2 obj U str_detect(x, "LOST") Lost To Follow Up NA
3 obj U TRUE Lack Of Efficacy NA
# A user-defined format: 3 conditions
Name Type Expression Label Order
1 obj U x == "COMPLETED" Subjects who Completed Study NA
2 obj U x == "NONCOMPLIANCE" Subjects terminated due to non-compliance NA
3 obj U x == "OTHER" Subjects who terminated early NA
Get group counts
select: dropped 3 variables (USUBJID, DSTERM, DSCAT)
group_by: 2 grouping variables (ARM, DSDECOD)
summarize: now 9 rows and 3 columns, one group variable remaining (ARM)
ungroup: no grouping variables
pivot_wider: reorganized (ARM, n) into (ARM A, ARM B, ARM C, ARM D) [was 9x3, now 3x5]
transmute: dropped one variable (DSDECOD)
new variable 'group' (character) with 3 unique values and 0% NA
new variable 'cat' (logical) with one unique value and 100% NA
new variable 'catseq' (double) with one unique value and 0% NA
converted 'ARM A' from integer to character (0 new NA)
converted 'ARM B' from integer to character (0 new NA)
converted 'ARM C' from integer to character (0 new NA)
converted 'ARM D' from integer to character (0 new NA)
# A tibble: 3 × 7
group cat catseq `ARM A` `ARM B` `ARM C` `ARM D`
<chr> <lgl> <dbl> <chr> <chr> <chr> <chr>
1 COMPLETED NA 1 19 ( 95.0%) 17 ( 81.0%) 16 ( 76.2%) 20 ( 87.0%)
2 OTHER NA 1 1 ( 5.0%) 3 ( 14.3%) 5 ( 23.8%) 3 ( 13.0%)
3 NONCOMPLIANCE NA 1 0 ( 0.0%) 1 ( 4.8%) 0 ( 0.0%) 0 ( 0.0%)
Pull out subjects who completed study.
filter: removed 13 rows (15%), 72 rows remaining
mutate: new variable 'TERMDECOD' (character) with 2 unique values and 0% NA
group_by: 3 grouping variables (ARM, DSDECOD, TERMDECOD)
summarize: now 5 rows and 4 columns, 2 group variables remaining (ARM, DSDECOD)
ungroup: no grouping variables
pivot_wider: reorganized (ARM, n) into (ARM A, ARM B, ARM C, ARM D) [was 5x4, now 2x6]
transmute: dropped 2 variables (DSDECOD, TERMDECOD)
new variable 'group' (character) with one unique value and 0% NA
new variable 'cat' (character) with 2 unique values and 0% NA
new variable 'catseq' (double) with one unique value and 0% NA
converted 'ARM A' from integer to character (0 new NA)
converted 'ARM B' from integer to character (0 new NA)
converted 'ARM C' from integer to character (0 new NA)
converted 'ARM D' from integer to character (0 new NA)
# A tibble: 2 × 7
group cat catseq `ARM A` `ARM B` `ARM C` `ARM D`
<chr> <chr> <dbl> <chr> <chr> <chr> <chr>
1 COMPLETED Subject Completed All Visits And Protocol Requirements 2 19 ( 9… 16 ( 7… 16 ( 7… 20 ( 8…
2 COMPLETED Subject Completed All Visits But With Major Protocol Violati… 2 0 ( 0… 1 ( 4… 0 ( 0… 0 ( 0…
Pull out subjects who were non-compliant
filter: removed 84 rows (99%), one row remaining
mutate: new variable 'TERMDECOD' (character) with one unique value and 0% NA
group_by: 3 grouping variables (ARM, DSDECOD, TERMDECOD)
summarize: now one row and 4 columns, 2 group variables remaining (ARM, DSDECOD)
ungroup: no grouping variables
pivot_wider: reorganized (ARM, n) into (ARM B) [was 1x4, now 1x3]
transmute: dropped 2 variables (DSDECOD, TERMDECOD)
new variable 'group' (character) with one unique value and 0% NA
new variable 'cat' (character) with one unique value and 0% NA
new variable 'catseq' (double) with one unique value and 0% NA
new variable 'ARM A' (character) with one unique value and 0% NA
converted 'ARM B' from integer to character (0 new NA)
new variable 'ARM C' (character) with one unique value and 0% NA
new variable 'ARM D' (character) with one unique value and 0% NA
# A tibble: 1 × 7
group cat catseq `ARM A` `ARM B` `ARM C` `ARM D`
<chr> <chr> <dbl> <chr> <chr> <chr> <chr>
1 NONCOMPLIANCE Non-Compliance With Study Drug 2 0 ( 0.0%) 1 ( 4.8%) 0 ( 0.0%) 0 ( 0.0%)
Pull out subject who terminated early
filter: removed 73 rows (86%), 12 rows remaining
mutate: new variable 'TERMDECOD' (character) with 2 unique values and 0% NA
group_by: 3 grouping variables (ARM, DSDECOD, TERMDECOD)
summarize: now 6 rows and 4 columns, 2 group variables remaining (ARM, DSDECOD)
ungroup: no grouping variables
pivot_wider: reorganized (ARM, n) into (ARM A, ARM B, ARM C, ARM D) [was 6x4, now 2x6]
transmute: dropped 2 variables (DSDECOD, TERMDECOD)
new variable 'group' (character) with one unique value and 0% NA
new variable 'cat' (character) with 2 unique values and 0% NA
new variable 'catseq' (double) with one unique value and 0% NA
converted 'ARM A' from integer to character (0 new NA)
converted 'ARM B' from integer to character (0 new NA)
converted 'ARM C' from integer to character (0 new NA)
converted 'ARM D' from integer to character (0 new NA)
# A tibble: 2 × 7
group cat catseq `ARM A` `ARM B` `ARM C` `ARM D`
<chr> <chr> <dbl> <chr> <chr> <chr> <chr>
1 OTHER Lack Of Efficacy 2 1 ( 5.0%) 0 ( 0.0%) 3 ( 14.3%) 2 ( 8.7%)
2 OTHER Lost To Follow Up 2 0 ( 0.0%) 3 ( 14.3%) 2 ( 9.5%) 1 ( 4.3%)
Combine blocks into final data frame
# A tibble: 8 × 7
group cat catseq `ARM A` `ARM B` `ARM C` `ARM D`
<chr> <chr> <dbl> <chr> <chr> <chr> <chr>
1 COMPLETED <NA> 1 19 ( 9… 17 ( 8… 16 ( 7… 20 ( 8…
2 COMPLETED Subject Completed All Visits And Protocol Requirements 2 19 ( 9… 16 ( 7… 16 ( 7… 20 ( 8…
3 COMPLETED Subject Completed All Visits But With Major Protocol Vio… 2 0 ( 0… 1 ( 4… 0 ( 0… 0 ( 0…
4 NONCOMPLIANCE <NA> 1 0 ( 0… 1 ( 4… 0 ( 0… 0 ( 0…
5 NONCOMPLIANCE Non-Compliance With Study Drug 2 0 ( 0… 1 ( 4… 0 ( 0… 0 ( 0…
6 OTHER <NA> 1 1 ( 5… 3 ( 14… 5 ( 23… 3 ( 13…
7 OTHER Lack Of Efficacy 2 1 ( 5… 0 ( 0… 3 ( 14… 2 ( 8…
8 OTHER Lost To Follow Up 2 0 ( 0… 3 ( 14… 2 ( 9… 1 ( 4…
=========================================================================
Create and print report
=========================================================================
# A report specification: 1 pages
- file_path: 'C:\Users\dbosa\AppData\Local\Temp\RtmpeYEHwE/example10.pdf'
- output_type: PDF
- units: inches
- orientation: landscape
- margins: top 1 bottom 1 left 1 right 1
- line size/count: 9/41
- content:
# A table specification:
- data: tibble 'final' 8 rows 7 cols
- show_cols: all
- use_attributes: all
- width: 8.5
- title 1: 'Table 5.2.3'
- title 2: 'Subject Disposition by Category and Treatment Group'
- title 3: 'Safety Population'
- footnote 1: 'Program: DS_Table.R'
- footnote 2: 'NOTE: Denominator based on number of non-missing responses.'
- stub: group cat 'Completion Status' align='left'
- define: group dedupe='TRUE'
- define: cat
- define: catseq visible='FALSE'
- define: ARM A 'Placebo'
- define: ARM B 'Drug 50mg'
- define: ARM C 'Drug 100mg'
- define: ARM D 'Competitor'
lib_sync: synchronized data in library 'sdtm'
lib_unload: library 'sdtm' unloaded
=========================================================================
Log End Time: 2022-10-09 20:51:13
Log Elapsed Time: 0 00:00:02
=========================================================================