Here is an example of a subject listing using the sassy system of functions. The example shows demographics for each patient and vital signs by visit.

Program

Note the following about this example:

  • The example makes extensive use of the datastep function from the libr package.
  • A label row has been created that spans the width of the page, and contains a selection of demographic attributes of each subject.
  • Titles have been placed in the document header using the header = TRUE parameter.
  • Footnotes have been placed in the document footer using the footer = TRUE parameter.
  • The supsc and symbol functions from the common package have been used to create superscripts and symbols in the titles, footnotes, and body of the report.
  • A variable “PG” has been created to force a page break every three subjects.
  • The program produces both RTF and PDF outputs from the same report object.
library(sassy)

# Prepare Log -------------------------------------------------------------

options("logr.autolog" = TRUE,
        "logr.on" = TRUE,
        "logr.notes" = FALSE)

# Get path to temp directory
tmp <- tempdir() 

# Get sample data directory
dir <- system.file("extdata", package = "sassy")

# Open log
lf <- log_open(file.path(tmp, "example11.log"))


# Prepare formats ---------------------------------------------------------

sep("Prepare formats")

fc <- fcat(SEX = c("M" = "Male", "F" = "Female"),
           AGE = "%d Years",
           RACE = value(condition(x == "WHITE", "White"),
                        condition(x == "BLACK OR AFRICAN AMERICAN", "Black or African American"),
                        condition(x == "ASIAN OR PACIFIC ISLANDER", "Asian or Pacific Islander"),
                        condition(TRUE, "Other")),
           WEIGHT = "%6.2f kg",
           EAR = c("L" = "Left", "R" = "Right"),
           DOSE = "%4.2fug",
           ETHNIC = value(condition(x == "NOT HISPANIC OR LATINO", "Not Hispanic or Latino"),
                          condition(x == "HISPANIC OR LATINO", "Hispanic or Latino"),
                          condition(TRUE, "Unknown")), 
           ARMT = value(condition(x == "ARM A", "Placebo"),
                        condition(x == "ARM B", "Drug 50mg"),
                        condition(x == "ARM C", "Drug 100mg"),
                        condition(x == "ARM D", "Competitor"),
                        condition(TRUE, "Not Treated/Screen Failure")),
           UNITS = value(condition(x ==  "BEATS/MIN", "bpm"),
                         condition(x == "BREATHS/MIN", "brths/min"),
                         condition(x == "C", symbol("degC")),
                         condition(x == "mmHg", "")),
           VISIT = value(condition(x == "DAY 1", "Day 1"),
                         condition(x == "WEEK 2", "Week 2"),
                         condition(x == "WEEK 4", "Week 4"),
                         condition(x == "WEEK 6", "Week 6"),
                         condition(x == "WEEK 8", "Week 8"),
                         condition(x == "WEEK 12", "Week 12"),
                         condition(x == "WEEK 16", "Week 16"),
                         condition(TRUE, "Early Termination"))
) 

# Prepare Data ------------------------------------------------------------

sep("Prepare Data")

libname(sdtm, dir, "csv")

lib_load(sdtm)

put("Format desired vital signs") 
datastep(sdtm.VS, 
         keep = v(USUBJID, VSTESTCD, VSCOMB, VISITNUM, VISIT),
         where = expression(VSTESTCD != 'HEIGHT' & VISITNUM > 0),
         {
           
           if (VSORRESU == "C")
             VSCOMB <- paste0(VSORRES, fapply(VSORRESU, fc$UNITS)) 
           else 
             VSCOMB <- paste(VSORRES, fapply(VSORRESU, fc$UNITS)) 
         }) -> vso

put("Pivot vitals signs")
proc_transpose(vso, id = VSTESTCD, var = VSCOMB, 
               by = v(USUBJID, VISITNUM, VISIT)) |> 
  proc_sort(by = v(USUBJID, VISITNUM)) -> vsot


put("Assign and apply formats")               
formats(sdtm.DM) <- fc
dmf <- fdata(sdtm.DM) |> put()

put("Prepare final data for reporting")
datastep(dmf, format = fc,
         by = USUBJID,
         retain = list(PTCNT = 0, PG = 1),
         merge = vsot, merge_by = USUBJID,
         {
           
           # Combine subject info into label row
           BASELINE <- paste0(USUBJID, ", Site=", SITEID,
                              ", Age=", AGE, ", Sex=", SEX, ", Race=", RACE, 
                              ", Ethnic=", ETHNIC)
           
           # Deal with non-recorded blood pressure
           if (is.na(DIABP))
             DIABP <- "-"
           
           if (is.na(SYSBP))
             SYSBP <- "-"
           
           # Combine distolic and systolic in one column
           BP <- paste0(trimws(DIABP), "/", trimws(SYSBP), " mmHg")
           
           # Format treatment group
           if (first.)
             TREATMENT <- fapply(ARM, fc$ARMT)
           else 
             TREATMENT <- ""
           
           # Count up patients
           if (first.) {
             PTCNT <- PTCNT + 1 
           }
           
           # Create paging variable with 3 patients per page
           if (PTCNT == 4) {
             
             PTCNT <- 1
             PG <- PG + 1
           }
           
         }) -> final


# Create report -----------------------------------------------------------

sep("Create and Print Report")

tbl <- create_table(final, show_cols = "none", 
                    width = 9, first_row_blank = FALSE,
                    header_bold = TRUE) |> 
  column_defaults(from = "VISIT", to = "BP", width = 1.25) |> 
  stub(v(BASELINE, TREATMENT), label = "Subject/Treatment") |> 
  define(BASELINE, label_row = TRUE) |> 
  define(TREATMENT) |> 
  define(VISIT, label = "Visit") |> 
  define(TEMP, label = "Temperature") |> 
  define(PULSE, label = "Pulse") |> 
  define(RESP, label = "Respirations") |> 
  define(BP, label = "Blood Pressure") |> 
  define(USUBJID, blank_after = TRUE, visible = FALSE) |> 
  define(PG, page_break = TRUE, visible = FALSE)

rpt <- create_report(file.path(tmp, "example11"), font = "Courier", font_size = 9) |> 
  add_content(tbl) |> 
  set_margins(top = 1, bottom = 1) |> 
  page_header("Program:" %p% Sys.path(), right = "Draft", width = 7) |> 
  titles( "Study: ABC", "Appendix 10.2.6.1.2.1", "Source: DM, VS",
          columns = 3, header = TRUE, blank_row = "none") |> 
  titles("Subject Listing with Vital Signs by Visit{supsc('1')}",
         "All Randomized Patients", align = "center", header = TRUE, blank_row = "below") |> 
  footnotes("{supsc('1')} Baseline through completion of study or early termination.",
            "Values flagged with {symbol('dagger')} were excluded from the by-visit " %p% 
              "analysis in tables showing the qualitative test results.",
            blank_row = "none", footer = TRUE) |> 
  page_footer("Date: " %p% toupper(fapply(Sys.time(), "%d%b%Y %H:%M:%S")),
              "Archytas", "Page [pg] of [tpg]")

# Generate both RTF and PDF with same report object
res1 <- write_report(rpt, output_type = "RTF")
res2 <- write_report(rpt, output_type = "PDF")


# Uncomment to show reports
# file.show(res1$modified_path)
# file.show(res2$modified_path)

# Uncomment to show log
# file.show(lf)


# Clean Up ----------------------------------------------------------------

lib_unload(sdtm)

log_close()

Output

Here is an image of the first page of the RTF report produced by the above subject listing:

Log

Here is the log produced by the above subject listing:

=========================================================================
Log Path: C:/Users/dbosa/AppData/Local/Temp/RtmpkNnsUE/log/example12.log
Program Path: C:/packages/Testing/procs/ProcsCombinedFigure.R
Working Directory: C:/packages/Testing/procs
User Name: dbosa
R Version: 4.3.1 (2023-06-16 ucrt)
Machine: SOCRATES x86-64
Operating System: Windows 10 x64 build 22621
Base Packages: stats graphics grDevices utils datasets methods base Other
Packages: tidylog_1.0.2 ggplot2_3.4.2 patchwork_1.1.3 procs_1.0.3
reporter_1.4.1 libr_1.2.8 fmtr_1.5.9 logr_1.3.4 common_1.0.8 sassy_1.1.0
Log Start Time: 2023-09-06 21:06:40.53712
=========================================================================

=========================================================================
Prepare Data
=========================================================================

Define data library

# library 'sdtm': 7 items
- attributes: csv not loaded
- path: C:/Users/dbosa/AppData/Local/R/win-library/4.3/sassy/extdata
- items:
  Name Extension Rows Cols     Size        LastModified
1   AE       csv  150   27  88.5 Kb 2023-08-07 17:51:40
2   DM       csv   87   24  45.5 Kb 2023-08-07 17:51:40
3   DS       csv  174    9  34.1 Kb 2023-08-07 17:51:40
4   EX       csv   84   11  26.4 Kb 2023-08-07 17:51:40
5   IE       csv    2   14  13.4 Kb 2023-08-07 17:51:40
6   SV       csv  685   10  70.3 Kb 2023-08-07 17:51:40
7   VS       csv 3358   17 467.4 Kb 2023-08-07 17:51:40

Load data into workspace

lib_load: library 'sdtm' loaded

Prepare format

# A user-defined format: 4 conditions
  Name Type        Expression    Label Order
1  obj    U x >= 18 & x <= 24 18 to 24    NA
2  obj    U x >= 25 & x <= 44 25 to 44    NA
3  obj    U x >= 45 & x <= 64 45 to 64    NA
4  obj    U           x >= 65    >= 65    NA

Prepare data

datastep: columns decreased from 24 to 5

# A tibble: 85 × 5
   USUBJID    SEX     AGE ARM   AGECAT  
   <chr>      <chr> <dbl> <chr> <chr>   
 1 ABC-01-049 M        39 ARM D 25 to 44
 2 ABC-01-050 M        47 ARM B 45 to 64
 3 ABC-01-051 M        34 ARM A 25 to 44
 4 ABC-01-052 F        45 ARM C 45 to 64
 5 ABC-01-053 F        26 ARM B 25 to 44
 6 ABC-01-054 M        44 ARM D 25 to 44
 7 ABC-01-055 F        47 ARM C 45 to 64
 8 ABC-01-056 M        31 ARM A 25 to 44
 9 ABC-01-113 M        74 ARM D >= 65   
10 ABC-01-114 F        72 ARM B >= 65   
# ℹ 75 more rows
# ℹ Use `print(n = ...)` to see more rows

Convert agecat to factor it will sort correctly

Split by ARM

=========================================================================
Create Plots
=========================================================================

Create plot for ARM A

Create plot for ARM B

Create plot for ARM C

Create plot for ARM D

Combine 4 plots into 1 using patchwork

=========================================================================
Create and print report
=========================================================================

# A report specification: 1 pages
- file_path: 'C:\Users\dbosa\AppData\Local\Temp\RtmpkNnsUE/output/example12.rtf'
- output_type: RTF
- units: inches
- orientation: landscape
- margins: top 1 bottom 1 left 1 right 1
- line size/count: 9/36
- page_header: left=Sponsor: Company right=Study: ABC
- footnote 1: 'Program: DM_Figure.R'
- page_footer: left=Date Produced: 06Sep23 21:06 center= right=Page [pg] of [tpg]
- content: 
# A plot specification: 
- height: 4.5
- width: 7
- title 1: 'Figure 3.2'
- title 2: 'Distribution of Subjects by Treatment Group'

lib_sync: synchronized data in library 'sdtm'

lib_unload: library 'sdtm' unloaded

=========================================================================
Log End Time: 2023-09-06 21:06:43.938549
Log Elapsed Time: 0 00:00:03
=========================================================================

Next: Example 12: Combined Figure