# Example 16: Shift Table

This example produces a shift table of lab values. A shift table is used to observe the change in lab values over visits, and is commonly used in the pharmaceutical industry.

## Program

    library(sassy)

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

    # Get temp location for log and report output
    tmp <- tempdir()

    lf <- log_open(file.path(tmp, "example16.log"))

    # Get data ----------------------------------------------------------------

    sep("Get data")

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

    put("Open data library")
    libname(sdtm, pth, "csv")

    # Create Formats ----------------------------------------------------------

    sep("Create Formats")

    put("Format for visits")
    vfmt <- value(condition(x == "DAY 1", "Day 1"),
                  condition(x == "WEEK 2", "Week 2"),
                  condition(x == "WEEK 6", "Week 6"),
                  condition(x == "WEEK 12", "Week 12"),
                  as.factor = TRUE)

    put("Format for ARMs")
    afmt <- value(condition(x == "ARM A", "Placebo"),
                  condition(x == "ARM B", "Drug (10mg)"),
                  condition(x == "ARM C", "Drug (20mg)"),
                  condition(x == "ARM D", "Competitor"),
                  as.factor = TRUE)

    put("Format for Lab Result Indicator")
    rfmt <- value(condition(x == "LOW", "Low"),
                  condition(x == "NORMAL", "Normal"),
                  condition(x == "HIGH", "High"),
                  condition(x == "UNKNOWN", "Unknown"),
                  as.factor = TRUE)

    # Prepare data ------------------------------------------------------------

    sep("Prepare data")

    put("Pull out needed visits and columns")
    lbsub1 <- subset(sdtm$LB, VISIT %in% toupper(levels(vfmt)),
                     v(USUBJID, VISIT, VISITNUM, LBCAT, LBORRESU, LBTEST,
                       LBTESTCD, LBBLFL, LBNRIND)) |> put()

    put("Pull out baseline subset")
    lbsub2 <- subset(lbsub1, LBBLFL == 'Y',
                     v(USUBJID, VISIT, LBCAT, LBTESTCD, LBNRIND)) |> put()

    put("Merge and append change from baseline")
    datastep(lbsub1, merge = lbsub2, merge_by = v(USUBJID, LBCAT, LBTESTCD),
             rename = v(LBNRIND.1 = LBNRIND, LBNRIND.2 = BLBNRIND, VISIT.1 = VISIT),
             drop = VISIT.2, {

               if (is.na(LBNRIND.1)) {

                 LBNRIND.1 <- "UNKNOWN"
               }

               if (is.na(LBNRIND.2)) {

                 LBNRIND.2 <- "UNKNOWN"
               }

             }) -> lbsub

    put("Pull needed ARMs and columns for DM")
    dmsub <- subset(sdtm$DM, ARM != "SCREEN FAILURE" & is.na(ARM) == FALSE,
                    v(USUBJID, ARMCD, ARM)) |> put()

    put("Merge DM with LB to get subject treatments")
    datastep(lbsub, merge = dmsub, merge_by = USUBJID,
             where = expression(toupper(VISIT) != 'SCREENING'),
             {
               VISIT <- fapply(VISIT, vfmt)
             }) -> lbdat

    # Get population counts ---------------------------------------------------

    sep("Get population counts")

    proc_sort(lbdat, by = v(ARM, USUBJID),
              keep = v(ARM, USUBJID),
              options = nodupkey) -> lb_unique

    put("Get population frequencies")
    proc_freq(lb_unique, tables = ARM,
              output = long,
              options = v(nopercent, nonobs)) -> lb_pop

    # Prepare lab test labels -------------------------------------------------

    sep("Lab test labels")

    put("Get lookup data for lab tests")
    proc_sort(lbdat, by = v(LBTESTCD, LBTEST, LBORRESU),
              keep = v(LBTESTCD, LBTEST, LBORRESU),
              options = nodupkey) -> tcodes

    put("Create test label with units")
    datastep(tcodes, where = expression(is.na(LBORRESU) == FALSE),
             keep = v(LBTESTCD, LABEL),
             {

               LABEL <- paste0(LBTEST, " (", LBORRESU, ")")

             }) -> tfmtdat

    put("Create lab value lookup")
    tfmt <- tfmtdat$LABEL
    names(tfmt) <- tfmtdat$LBTESTCD

    # Calculate frequencies ----------------------------------------------------

    sep("Calculate frequencies")

    put("Apply formats")
    lbdat$LBNRIND <- fapply(lbdat$LBNRIND, rfmt)
    lbdat$BLBNRIND <- fapply(lbdat$BLBNRIND, rfmt)

    put("Get freqs by ARM and visit")
    proc_freq(lbdat, by = v(ARM, LBTESTCD, VISIT),
              tables = LBNRIND * BLBNRIND) -> lb_freq

    put("Combine frequencies and percents")
    datastep(lb_freq,
             drop = v(VAR1, VAR2, CNT, PCT),
             {
               if (CNT == 0) {
                 CNTPCT <- fapply(CNT, "%d", width = 10, justify = "left")
               } else {
                 CNTPCT <- fapply2(CNT, PCT, "%d", "(%5.1f%%)")
               }
             }) -> lb_comb

    put("Transpose ARMs")
    proc_transpose(lb_comb, id = v(BY1, CAT2), copy = N,
                   by = v(BY2, BY3, CAT1), var = CNTPCT,
                   options = noname) -> lb_final

    put("Apply formats")
    lb_final$BY2 <- fapply(lb_final$BY2, tfmt)

    put("Rename variables")
    datastep(lb_final,
             rename = c(BY2 = "LBTEST", BY3 = "VISIT", CAT1 = "RIND"),
             {}) -> lb_final

    put("Sort by lab test")
    proc_sort(lb_final, by = v(LBTEST, VISIT)) -> lb_final

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

    sep("Create report")

    put("Create output path")
    pth <- file.path(tmp, "output/example16.pdf") |> put()

    tbl <- create_table(lb_final) |>
      spanning_header(`ARM A.Low`, `ARM A.Unknown`, "Placebo", n = lb_pop["ARM A"]) |>
      spanning_header(`ARM B.Low`, `ARM B.Unknown`, "Drug 10mg", n = lb_pop["ARM B"]) |>
      spanning_header(`ARM C.Low`, `ARM C.Unknown`, "Drug 20mg", n = lb_pop["ARM C"]) |>
      spanning_header(`ARM D.Low`, `ARM D.Unknown`, "Competitor", n = lb_pop["ARM D"]) |>
      define(LBTEST, visible = FALSE) |>
      define(VISIT, "Visit", format = vfmt, dedupe = TRUE, align = "left",
             id_var = TRUE, blank_after = TRUE) |>
      define(N, "n", visible = FALSE) |>
      define(RIND, "", align = "left", id_var = TRUE) |>
      define(`ARM A.Low`, "Low") |>
      define(`ARM A.Normal`, "Normal") |>
      define(`ARM A.High`, "High") |>
      define(`ARM A.Unknown`, "Unknown") |>
      define(`ARM B.Low`, "Low") |>
      define(`ARM B.Normal`, "Normal") |>
      define(`ARM B.High`, "High") |>
      define(`ARM B.Unknown`, "Unknown") |>
      define(`ARM C.Low`, "Low", page_wrap = TRUE) |>
      define(`ARM C.Normal`, "Normal") |>
      define(`ARM C.High`, "High") |>
      define(`ARM C.Unknown`, "Unknown") |>
      define(`ARM D.Low`, "Low") |>
      define(`ARM D.Normal`, "Normal") |>
      define(`ARM D.High`, "High") |>
      define(`ARM D.Unknown`, "Unknown") |>

      put("Create report")
    rpt <- create_report(pth, output_type = "PDF",
                         font = "Courier", font_size = 9) |>
      set_margins(top = 1, left = 1, right = 1, bottom = .5) |>
      page_header(c("Protocol: ABC 12345-678", "DRUG/INDICATION: Consultopan",
                    "TLF Version: Final Database Lock (03FEB2024)"),
                  right = c("(Page [pg] of [tpg])", "DATABASE VERSION: 01FEB2024",
                            "TASK: CSR")) |>
      titles("Table 4.3.1.1", "", "Shift Table of Laboratory Values - Hematology",
             "(Safety Population)", blank_row = "below") |>
      page_by(LBTEST, "Laboratory Value: ") |>
      add_content(tbl) |>
      footnotes("PROGRAM/OUTPUT: T_LABSHIFT/T_4_3_1_1_HEM",
                "DATE (TIME): " %p% toupper(fapply(Sys.time(), "%d%b%Y (%H:%M)")),
                columns = 2, borders = "top", blank_row = "below") |>
      footnotes(paste("Note 1: For N(%) of participants, percentages are calculated",
                      "as the number of participants for each ARM at each visit",
                      "as the denominator."),
                "Reference: Listing 2.8.1.1, 2.8.1.2", blank_row = "none")

    put("Write out report to file system")
    res <- write_report(rpt)

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

    sep("Clean Up")

    log_close()

    # View report
    # file.show(res$modified_path)

    # View log
    # file.show(lf)

## Output

Here is the output report:

## Log

Here is the log:

    =========================================================================
    Log Path: C:/Users/dbosa/AppData/Local/Temp/RtmpaGlETW/log/example16.log
    Program Path: C:/packages/Testing/sassytests/ShiftTable2.R
    Working Directory: C:/packages/Testing
    User Name: dbosa
    R Version: 4.3.2 (2023-10-31 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 procs_1.0.5
    reporter_1.4.3 libr_1.2.9 logr_1.3.5 fmtr_1.6.2 common_1.1.1 sassy_1.2.1
    Log Start Time: 2024-02-06 14:52:04.476925
    =========================================================================

    =========================================================================
    Get data
    =========================================================================

    Open data library

    # library 'sdtm': 8 items
    - attributes: csv not loaded
    - path: C:/Users/dbosa/AppData/Local/R/win-library/4.4/sassy/extdata
    - items:
      Name Extension Rows Cols     Size        LastModified
    1   AE       csv  150   27  88.8 Kb 2024-05-03 08:12:15
    2   DM       csv   87   24  45.8 Kb 2024-05-03 08:12:15
    3   DS       csv  174    9  34.4 Kb 2024-05-03 08:12:15
    4   EX       csv   84   11  26.7 Kb 2024-05-03 08:12:15
    5   IE       csv    2   14  13.7 Kb 2024-05-03 08:12:15
    6   LB       csv 1618   24 348.7 Kb 2024-05-03 08:12:15
    7   SV       csv  685   10  70.6 Kb 2024-05-03 08:12:15
    8   VS       csv 3358   17 467.7 Kb 2024-05-03 08:12:15

    =========================================================================
    Create Formats
    =========================================================================

    Format for visits

    # A user-defined format: 4 conditions
    - as.factor: TRUE
      Name Type     Expression   Label Order
    1  obj    U   x == "DAY 1"   Day 1    NA
    2  obj    U  x == "WEEK 2"  Week 2    NA
    3  obj    U  x == "WEEK 6"  Week 6    NA
    4  obj    U x == "WEEK 12" Week 12    NA

    Format for ARMs

    # A user-defined format: 4 conditions
    - as.factor: TRUE
      Name Type   Expression       Label Order
    1  obj    U x == "ARM A"     Placebo    NA
    2  obj    U x == "ARM B" Drug (10mg)    NA
    3  obj    U x == "ARM C" Drug (20mg)    NA
    4  obj    U x == "ARM D"  Competitor    NA

    Format for Lab Result Indicator

    # A user-defined format: 4 conditions
    - as.factor: TRUE
      Name Type     Expression   Label Order
    1  obj    U     x == "LOW"     Low    NA
    2  obj    U  x == "NORMAL"  Normal    NA
    3  obj    U    x == "HIGH"    High    NA
    4  obj    U x == "UNKNOWN" Unknown    NA

    =========================================================================
    Prepare data
    =========================================================================

    Pull out needed visits and columns

    # A tibble: 1,578 × 9
       USUBJID    VISIT   VISITNUM LBCAT      LBORRESU LBTEST     LBTESTCD LBBLFL LBNRIND
       <chr>      <chr>      <dbl> <chr>      <chr>    <chr>      <chr>    <chr>  <chr>
     1 ABC-01-049 DAY 1          1 HEMATOLOGY x10(9)/L Basophils  BASO     Y      NORMAL
     2 ABC-01-049 WEEK 2         2 HEMATOLOGY x10(9)/L Basophils  BASO     <NA>   NORMAL
     3 ABC-01-049 WEEK 6         6 HEMATOLOGY x10(9)/L Basophils  BASO     <NA>   NORMAL
     4 ABC-01-049 WEEK 12       12 HEMATOLOGY x10(9)/L Basophils  BASO     <NA>   NORMAL
     5 ABC-01-049 DAY 1          1 HEMATOLOGY x10(9)/L Eosinophi… EOS      Y      NORMAL
     6 ABC-01-049 WEEK 2         2 HEMATOLOGY x10(9)/L Eosinophi… EOS      <NA>   NORMAL
     7 ABC-01-049 WEEK 6         6 HEMATOLOGY x10(9)/L Eosinophi… EOS      <NA>   NORMAL
     8 ABC-01-049 WEEK 12       12 HEMATOLOGY x10(9)/L Eosinophi… EOS      <NA>   NORMAL
     9 ABC-01-049 DAY 1          1 HEMATOLOGY %        Hematocrit HCT      Y      NORMAL
    10 ABC-01-049 WEEK 2         2 HEMATOLOGY %        Hematocrit HCT      <NA>   NORMAL
    # ℹ 1,568 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Pull out baseline subset

    # A tibble: 409 × 5
       USUBJID    VISIT LBCAT      LBTESTCD LBNRIND
       <chr>      <chr> <chr>      <chr>    <chr>
     1 ABC-01-049 DAY 1 HEMATOLOGY BASO     NORMAL
     2 ABC-01-049 DAY 1 HEMATOLOGY EOS      NORMAL
     3 ABC-01-049 DAY 1 HEMATOLOGY HCT      NORMAL
     4 ABC-01-049 DAY 1 HEMATOLOGY HGB      NORMAL
     5 ABC-01-049 DAY 1 HEMATOLOGY LYM      NORMAL
     6 ABC-01-050 DAY 1 HEMATOLOGY BASO     NORMAL
     7 ABC-01-050 DAY 1 HEMATOLOGY EOS      NORMAL
     8 ABC-01-050 DAY 1 HEMATOLOGY HCT      NORMAL
     9 ABC-01-050 DAY 1 HEMATOLOGY HGB      NORMAL
    10 ABC-01-050 DAY 1 HEMATOLOGY LYM      NORMAL
    # ℹ 399 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Merge and append change from baseline

    datastep: columns increased from 9 to 10

    # A tibble: 1,578 × 10
       USUBJID    VISIT   VISITNUM LBCAT LBORRESU LBTEST LBTESTCD LBBLFL LBNRIND BLBNRIND
       <chr>      <chr>      <dbl> <chr> <chr>    <chr>  <chr>    <chr>  <chr>   <chr>
     1 ABC-01-049 WEEK 12       12 HEMA… x10(9)/L Basop… BASO     <NA>   NORMAL  NORMAL
     2 ABC-01-049 WEEK 2         2 HEMA… x10(9)/L Basop… BASO     <NA>   NORMAL  NORMAL
     3 ABC-01-049 DAY 1          1 HEMA… x10(9)/L Basop… BASO     Y      NORMAL  NORMAL
     4 ABC-01-049 WEEK 6         6 HEMA… x10(9)/L Basop… BASO     <NA>   NORMAL  NORMAL
     5 ABC-01-049 WEEK 12       12 HEMA… x10(9)/L Eosin… EOS      <NA>   NORMAL  NORMAL
     6 ABC-01-049 DAY 1          1 HEMA… x10(9)/L Eosin… EOS      Y      NORMAL  NORMAL
     7 ABC-01-049 WEEK 2         2 HEMA… x10(9)/L Eosin… EOS      <NA>   NORMAL  NORMAL
     8 ABC-01-049 WEEK 6         6 HEMA… x10(9)/L Eosin… EOS      <NA>   NORMAL  NORMAL
     9 ABC-01-049 WEEK 12       12 HEMA… %        Hemat… HCT      <NA>   NORMAL  NORMAL
    10 ABC-01-049 WEEK 2         2 HEMA… %        Hemat… HCT      <NA>   NORMAL  NORMAL
    # ℹ 1,568 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Pull needed ARMs and columns for DM

    # A tibble: 85 × 3
       USUBJID    ARMCD ARM
       <chr>      <chr> <chr>
     1 ABC-01-049 4     ARM D
     2 ABC-01-050 2     ARM B
     3 ABC-01-051 1     ARM A
     4 ABC-01-052 3     ARM C
     5 ABC-01-053 2     ARM B
     6 ABC-01-054 4     ARM D
     7 ABC-01-055 3     ARM C
     8 ABC-01-056 1     ARM A
     9 ABC-01-113 4     ARM D
    10 ABC-01-114 2     ARM B
    # ℹ 75 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Merge DM with LB to get subject treatments

    datastep: columns increased from 10 to 12

    # A tibble: 1,578 × 12
       USUBJID    VISIT   VISITNUM LBCAT LBORRESU LBTEST LBTESTCD LBBLFL LBNRIND BLBNRIND
       <chr>      <ord>      <dbl> <chr> <chr>    <chr>  <chr>    <chr>  <chr>   <chr>
     1 ABC-01-049 Week 12       12 HEMA… x10(9)/L Lymph… LYM      <NA>   NORMAL  NORMAL
     2 ABC-01-049 Week 6         6 HEMA… x10(9)/L Lymph… LYM      <NA>   NORMAL  NORMAL
     3 ABC-01-049 Week 2         2 HEMA… x10(9)/L Lymph… LYM      <NA>   NORMAL  NORMAL
     4 ABC-01-049 Week 6         6 HEMA… x10(9)/L Basop… BASO     <NA>   NORMAL  NORMAL
     5 ABC-01-049 Week 12       12 HEMA… x10(9)/L Eosin… EOS      <NA>   NORMAL  NORMAL
     6 ABC-01-049 Week 12       12 HEMA… g/dL     Hemog… HGB      <NA>   NORMAL  NORMAL
     7 ABC-01-049 Week 2         2 HEMA… x10(9)/L Eosin… EOS      <NA>   NORMAL  NORMAL
     8 ABC-01-049 Week 6         6 HEMA… x10(9)/L Eosin… EOS      <NA>   NORMAL  NORMAL
     9 ABC-01-049 Week 12       12 HEMA… %        Hemat… HCT      <NA>   NORMAL  NORMAL
    10 ABC-01-049 Day 1          1 HEMA… x10(9)/L Lymph… LYM      Y      NORMAL  NORMAL
    # ℹ 1,568 more rows
    # ℹ 2 more variables: ARMCD <chr>, ARM <chr>
    # ℹ Use `print(n = ...)` to see more rows

    =========================================================================
    Get population counts
    =========================================================================

    proc_sort: input data set 86 rows and 12 columns
               by: ARM USUBJID
               keep: ARM USUBJID
               order: a a
               options: nodupkey
               output data set 86 rows and 2 columns

    # A tibble: 86 × 2
       ARM   USUBJID
       <chr> <chr>
     1 ARM A ABC-01-051
     2 ARM A ABC-01-056
     3 ARM A ABC-02-034
     4 ARM A ABC-02-038
     5 ARM A ABC-02-109
     6 ARM A ABC-03-002
     7 ARM A ABC-03-006
     8 ARM A ABC-03-091
     9 ARM A ABC-04-075
    10 ARM A ABC-04-080
    # ℹ 76 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Get population frequencies

    proc_freq: input data set 86 rows and 2 columns
               tables: ARM
               output: long
               view: TRUE
               output: 1 datasets

    # A tibble: 1 × 6
      VAR   STAT  `ARM A` `ARM B` `ARM C` `ARM D`
      <chr> <chr>   <dbl>   <dbl>   <dbl>   <dbl>
    1 ARM   CNT        20      21      20      23

    =========================================================================
    Lab test labels
    =========================================================================

    Get lookup data for lab tests

    proc_sort: input data set 10 rows and 12 columns
               by: LBTESTCD LBTEST LBORRESU
               keep: LBTESTCD LBTEST LBORRESU
               order: a a a
               options: nodupkey
               output data set 10 rows and 3 columns

    # A tibble: 10 × 3
       LBTESTCD LBTEST      LBORRESU
       <chr>    <chr>       <chr>
     1 BASO     Basophils   x10(9)/L
     2 BASO     Basophils   <NA>
     3 EOS      Eosinophils x10(9)/L
     4 EOS      Eosinophils <NA>
     5 HCT      Hematocrit  %
     6 HCT      Hematocrit  <NA>
     7 HGB      Hemoglobin  g/dL
     8 HGB      Hemoglobin  <NA>
     9 LYM      Lymphocytes x10(9)/L
    10 LYM      Lymphocytes <NA>

    Create test label with units

    datastep: columns decreased from 3 to 2

    # A tibble: 5 × 2
      LBTESTCD LABEL
      <chr>    <chr>
    1 BASO     Basophils (x10(9)/L)
    2 EOS      Eosinophils (x10(9)/L)
    3 HCT      Hematocrit (%)
    4 HGB      Hemoglobin (g/dL)
    5 LYM      Lymphocytes (x10(9)/L)

    Create lab value lookup

    =========================================================================
    Calculate frequencies
    =========================================================================

    Apply formats

    Get freqs by ARM and visit

    proc_freq: input data set 1578 rows and 12 columns
               tables: LBNRIND * BLBNRIND
               by: ARM LBTESTCD VISIT
               view: TRUE
               output: 1 datasets

    # A tibble: 1,280 × 10
       BY1   BY2   BY3   VAR1    VAR2     CAT1   CAT2        N   CNT   PCT
       <chr> <chr> <ord> <chr>   <chr>    <fct>  <fct>   <dbl> <dbl> <dbl>
     1 ARM A BASO  Day 1 LBNRIND BLBNRIND Low    Low        19     0     0
     2 ARM A BASO  Day 1 LBNRIND BLBNRIND Low    Normal     19     0     0
     3 ARM A BASO  Day 1 LBNRIND BLBNRIND Low    High       19     0     0
     4 ARM A BASO  Day 1 LBNRIND BLBNRIND Low    Unknown    19     0     0
     5 ARM A BASO  Day 1 LBNRIND BLBNRIND Normal Low        19     0     0
     6 ARM A BASO  Day 1 LBNRIND BLBNRIND Normal Normal     19    19   100
     7 ARM A BASO  Day 1 LBNRIND BLBNRIND Normal High       19     0     0
     8 ARM A BASO  Day 1 LBNRIND BLBNRIND Normal Unknown    19     0     0
     9 ARM A BASO  Day 1 LBNRIND BLBNRIND High   Low        19     0     0
    10 ARM A BASO  Day 1 LBNRIND BLBNRIND High   Normal     19     0     0
    # ℹ 1,270 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Combine frequencies and percents

    datastep: columns decreased from 10 to 7

    # A tibble: 1,280 × 7
       BY1   BY2   BY3   CAT1   CAT2        N CNTPCT
       <chr> <chr> <ord> <fct>  <fct>   <dbl> <chr>
     1 ARM A BASO  Day 1 Low    Low        19 "0         "
     2 ARM A BASO  Day 1 Low    Normal     19 "0         "
     3 ARM A BASO  Day 1 Low    High       19 "0         "
     4 ARM A BASO  Day 1 Low    Unknown    19 "0         "
     5 ARM A BASO  Day 1 Normal Low        19 "0         "
     6 ARM A BASO  Day 1 Normal Normal     19 "19 (100.0%)"
     7 ARM A BASO  Day 1 Normal High       19 "0         "
     8 ARM A BASO  Day 1 Normal Unknown    19 "0         "
     9 ARM A BASO  Day 1 High   Low        19 "0         "
    10 ARM A BASO  Day 1 High   Normal     19 "0         "
    # ℹ 1,270 more rows
    # ℹ Use `print(n = ...)` to see more rows

    Transpose ARMs

    proc_transpose: input data set 1280 rows and 7 columns
                    by: BY2 BY3 CAT1
                    var: CNTPCT
                    id: BY1 CAT2
                    copy: N
                    name: NAME
                    output dataset 80 rows and 20 columns

    # A tibble: 80 × 20
       BY2   BY3    CAT1      N `ARM A.Low`  `ARM A.Normal` `ARM A.High` `ARM A.Unknown`
       <chr> <ord>  <fct> <dbl> <chr>        <chr>          <chr>        <chr>
     1 BASO  Day 1  Low      19 "0         " "0         "   "0         " "0         "
     2 EOS   Day 1  Low      19 "3 ( 15.8%)" "0         "   "0         " "0         "
     3 HCT   Day 1  Low      20 "3 ( 15.0%)" "0         "   "0         " "0         "
     4 HGB   Day 1  Low      20 "3 ( 15.0%)" "0         "   "0         " "0         "
     5 LYM   Day 1  Low      19 "0         " "0         "   "0         " "0         "
     6 BASO  Week 2 Low      19 "0         " "0         "   "0         " "0         "
     7 EOS   Week 2 Low      19 "1 (  5.3%)" "0         "   "0         " "0         "
     8 HCT   Week 2 Low      20 "1 (  5.0%)" "1 (  5.0%)"   "0         " "0         "
     9 HGB   Week 2 Low      20 "1 (  5.0%)" "1 (  5.0%)"   "0         " "0         "
    10 LYM   Week 2 Low      19 "0         " "0         "   "0         " "0         "
    # ℹ 70 more rows
    # ℹ 12 more variables: `ARM B.Low` <chr>, `ARM B.Normal` <chr>, `ARM B.High` <chr>,
    #   `ARM B.Unknown` <chr>, `ARM C.Low` <chr>, `ARM C.Normal` <chr>,
    #   `ARM C.High` <chr>, `ARM C.Unknown` <chr>, `ARM D.Low` <chr>,
    #   `ARM D.Normal` <chr>, `ARM D.High` <chr>, `ARM D.Unknown` <chr>
    # ℹ Use `print(n = ...)` to see more rows

    Apply formats

    Rename variables

    datastep: columns started with 20 and ended with 20

    # A tibble: 80 × 20
       LBTEST   VISIT RIND      N `ARM A.Low` `ARM A.Normal` `ARM A.High` `ARM A.Unknown`
       <chr>    <ord> <fct> <dbl> <chr>       <chr>          <chr>        <chr>
     1 Basophi… Day 1 Low      19 "0        … "0         "   "0         " "0         "
     2 Eosinop… Day 1 Low      19 "3 ( 15.8%… "0         "   "0         " "0         "
     3 Hematoc… Day 1 Low      20 "3 ( 15.0%… "0         "   "0         " "0         "
     4 Hemoglo… Day 1 Low      20 "3 ( 15.0%… "0         "   "0         " "0         "
     5 Lymphoc… Day 1 Low      19 "0        … "0         "   "0         " "0         "
     6 Basophi… Week… Low      19 "0        … "0         "   "0         " "0         "
     7 Eosinop… Week… Low      19 "1 (  5.3%… "0         "   "0         " "0         "
     8 Hematoc… Week… Low      20 "1 (  5.0%… "1 (  5.0%)"   "0         " "0         "
     9 Hemoglo… Week… Low      20 "1 (  5.0%… "1 (  5.0%)"   "0         " "0         "
    10 Lymphoc… Week… Low      19 "0        … "0         "   "0         " "0         "
    # ℹ 70 more rows
    # ℹ 12 more variables: `ARM B.Low` <chr>, `ARM B.Normal` <chr>, `ARM B.High` <chr>,
    #   `ARM B.Unknown` <chr>, `ARM C.Low` <chr>, `ARM C.Normal` <chr>,
    #   `ARM C.High` <chr>, `ARM C.Unknown` <chr>, `ARM D.Low` <chr>,
    #   `ARM D.Normal` <chr>, `ARM D.High` <chr>, `ARM D.Unknown` <chr>
    # ℹ Use `print(n = ...)` to see more rows

    Sort by lab test

    proc_sort: input data set 80 rows and 20 columns
               by: LBTEST VISIT
               keep: LBTEST VISIT RIND N ARM A.Low ARM A.Normal ARM A.High ARM A.Unknown ARM B.Low ARM B.Normal ARM B.High ARM B.Unknown ARM C.Low ARM C.Normal ARM C.High ARM C.Unknown ARM D.Low ARM D.Normal ARM D.High ARM D.Unknown
               order: a a
               output data set 80 rows and 20 columns

    # A tibble: 80 × 20
       LBTEST   VISIT RIND      N `ARM A.Low` `ARM A.Normal` `ARM A.High` `ARM A.Unknown`
       <chr>    <ord> <fct> <dbl> <chr>       <chr>          <chr>        <chr>
     1 Basophi… Day 1 Low      19 "0        … "0         "   "0         " "0         "
     2 Basophi… Day 1 Norm…    19 "0        … "19 (100.0%)"  "0         " "0         "
     3 Basophi… Day 1 High     19 "0        … "0         "   "0         " "0         "
     4 Basophi… Day 1 Unkn…    19 "0        … "0         "   "0         " "0         "
     5 Basophi… Week… Low      19 "0        … "0         "   "0         " "0         "
     6 Basophi… Week… Norm…    19 "0        … "18 ( 94.7%)"  "0         " "1 (  5.3%)"
     7 Basophi… Week… High     19 "0        … "0         "   "0         " "0         "
     8 Basophi… Week… Unkn…    19 "0        … "0         "   "0         " "0         "
     9 Basophi… Week… Low      19 "0        … "0         "   "0         " "0         "
    10 Basophi… Week… Norm…    19 "0        … "18 ( 94.7%)"  "0         " "1 (  5.3%)"
    # ℹ 70 more rows
    # ℹ 12 more variables: `ARM B.Low` <chr>, `ARM B.Normal` <chr>, `ARM B.High` <chr>,
    #   `ARM B.Unknown` <chr>, `ARM C.Low` <chr>, `ARM C.Normal` <chr>,
    #   `ARM C.High` <chr>, `ARM C.Unknown` <chr>, `ARM D.Low` <chr>,
    #   `ARM D.Normal` <chr>, `ARM D.High` <chr>, `ARM D.Unknown` <chr>
    # ℹ Use `print(n = ...)` to see more rows

    =========================================================================
    Create report
    =========================================================================

    Create output path

    C:\Users\dbosa\AppData\Local\Temp\RtmpaGlETW/output/example16.pdf

    # A table specification:
    - data: tibble 'lb_final' 80 rows 20 cols
    - show_cols: all
    - use_attributes: all
    - spanning_header: from='ARM A.Low' to='ARM A.Unknown' 'Placebo' level=1
    - spanning_header: from='ARM B.Low' to='ARM B.Unknown' 'Drug 10mg' level=1
    - spanning_header: from='ARM C.Low' to='ARM C.Unknown' 'Drug 20mg' level=1
    - spanning_header: from='ARM D.Low' to='ARM D.Unknown' 'Competitor' level=1
    - define: LBTEST visible='FALSE'
    - define: VISIT 'Visit' align='left' id_var='TRUE' dedupe='TRUE'
    - define: N visible='FALSE'
    - define: RIND '' align='left' id_var='TRUE'
    - define: ARM A.Low 'Low'
    - define: ARM A.Normal 'Normal'
    - define: ARM A.High 'High'
    - define: ARM A.Unknown 'Unknown'
    - define: ARM B.Low 'Low'
    - define: ARM B.Normal 'Normal'
    - define: ARM B.High 'High'
    - define: ARM B.Unknown 'Unknown'
    - define: ARM C.Low 'Low' page_wrap='TRUE'
    - define: ARM C.Normal 'Normal'
    - define: ARM C.High 'High'
    - define: ARM C.Unknown 'Unknown'
    - define: ARM D.Low 'Low'
    - define: ARM D.Normal 'Normal'
    - define: ARM D.High 'High'
    - define: ARM D.Unknown 'Unknown'

    Write out report to file system

    # A report specification: 10 pages
    - file_path: 'C:\Users\dbosa\AppData\Local\Temp\RtmpaGlETW/output/example16.pdf'
    - output_type: PDF
    - units: inches
    - orientation: landscape
    - margins: top 1 bottom 0.5 left 1 right 1
    - line size/count: 9/46
    - page by: LBTEST
    - page_header: left=Protocol: ABC 12345-678, DRUG/INDICATION: Consultopan, TLF Version: Final Database Lock (03FEB2024) right=(Page [pg] of [tpg]), DATABASE VERSION: 01FEB2024, TASK: CSR
    - title 1: 'Table 4.3.1.1'
    - title 2: ''
    - title 3: 'Shift Table of Laboratory Values - Hematology'
    - title 4: '(Safety Population)'
    - footnote 1: 'PROGRAM/OUTPUT: T_LABSHIFT/T_4_3_1_1_HEM'
    - footnote 2: 'DATE (TIME): 06FEB2024 (14:52)'
    - footnote 3: 'Note 1: For N(%) of participants, percentages are calculated as the number of participants for each ARM at each visit as the denominator.'
    - footnote 4: 'Reference: Listing 2.8.1.1, 2.8.1.2'
    - content:
    # A table specification:
    - data: tibble 'lb_final' 80 rows 20 cols
    - show_cols: all
    - use_attributes: all
    - spanning_header: from='ARM A.Low' to='ARM A.Unknown' 'Placebo' level=1
    - spanning_header: from='ARM B.Low' to='ARM B.Unknown' 'Drug 10mg' level=1
    - spanning_header: from='ARM C.Low' to='ARM C.Unknown' 'Drug 20mg' level=1
    - spanning_header: from='ARM D.Low' to='ARM D.Unknown' 'Competitor' level=1
    - define: LBTEST visible='FALSE'
    - define: VISIT 'Visit' align='left' id_var='TRUE' dedupe='TRUE'
    - define: N visible='FALSE'
    - define: RIND '' align='left' id_var='TRUE'
    - define: ARM A.Low 'Low'
    - define: ARM A.Normal 'Normal'
    - define: ARM A.High 'High'
    - define: ARM A.Unknown 'Unknown'
    - define: ARM B.Low 'Low'
    - define: ARM B.Normal 'Normal'
    - define: ARM B.High 'High'
    - define: ARM B.Unknown 'Unknown'
    - define: ARM C.Low 'Low' page_wrap='TRUE'
    - define: ARM C.Normal 'Normal'
    - define: ARM C.High 'High'
    - define: ARM C.Unknown 'Unknown'
    - define: ARM D.Low 'Low'
    - define: ARM D.Normal 'Normal'
    - define: ARM D.High 'High'
    - define: ARM D.Unknown 'Unknown'

    =========================================================================
    Clean Up
    =========================================================================

    =========================================================================
    Log End Time: 2024-02-06 14:52:16.546017
    Log Elapsed Time: 0 00:00:12
    =========================================================================