# Example 6: Figure with By Group

The sassy system gives you capabilities that few other R packages can match. The system not only support reports with by-groups. You can even apply a by-group to a figure.

## Program

Note the following about this example:

- The plot is created as a single plot with no by-groups
- The plot is added to the report with the `add_content()` function, just like the figures in the previous example.
- The `page_by()` function on the `create_plot()` statement generates the paging for both the report and plot.

    library(sassy)
    library(ggplot2)

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

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

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

    # Open log
    lgpth <- log_open(file.path(tmp, "example6.log"))

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

    sep("Prepare Data")

    put("Load data files")
    libname(dat, pkg, "csv", filter = c("DM", "VS"))

    put("Prepare factor levels")
    visit_levels <- c("SCREENING", "DAY 1",   "WEEK 2",  "WEEK 4",  "WEEK 6",
                      "WEEK 8",  "WEEK 12", "WEEK 16") |> put()

    arm_levels <- c("ARM A", "ARM B") |> put()

    test_codes <- c("SYSBP", "DIABP", "PULSE", "RESP") |> put()

    put("Prepare data for analysis")
    datastep(dat$DM, merge = dat$VS, merge_by = v(STUDYID, USUBJID),
             keep = v(STUDYID, USUBJID, ARM, VISIT, VSTESTCD, VSORRES),
             merge_in = v(inDM, inVS),
             where = expression(VISIT != "END OF STUDY EARLY TERMINATION" &
                                ARM %in% c("ARM A", "ARM B") &
                                VSTESTCD %in% test_codes
                                ),
             {

               # Assign factors to VISIT and ARM
               VISIT <- factor(VISIT, levels = visit_levels)
               ARM <- factor(ARM, levels = arm_levels)
               VSTESTCD <- factor(VSTESTCD, levels = test_codes)

               if (!inDM & inVS) {
                 delete()
               }

             }) -> vitals

    # Create Plot -------------------------------------------------------------

    sep("Create Plot")

    put("Assign Colors")
    arm_cols <- c("ARM B" = "#1f77b4", "ARM A" = "#2f2f2f")

    put("Define Boxplot")
    p_box <- ggplot2::ggplot(
      vitals,
      ggplot2::aes(x = VISIT, y = VSORRES, fill = ARM, colour = ARM)
    ) +
      ggplot2::geom_boxplot(
        position = ggplot2::position_dodge(width = 0.75),
        width = 0.6,
        outlier.size = 0.7,
        alpha = 0.9
      ) +
      ggplot2::scale_fill_manual(values = arm_cols) +
      ggplot2::scale_colour_manual(values = arm_cols) +
      ggplot2::labs(
        x = NULL,
        y = "Lab Value",
        fill = NULL,
        colour = NULL
      ) +
      ggplot2::theme_bw(base_size = 10) +
      ggplot2::theme(
        legend.position = "bottom",
        panel.grid.major.x = ggplot2::element_blank(),
        plot.title = ggplot2::element_text(face = "bold", hjust = 0),
        plot.caption = ggplot2::element_text(hjust = 0)
      )

    put("Create format for lab codes")
    lbfmt <- value(condition(x == "SYSBP", "Systolic Blood Pressure (mmHg)"),
                   condition(x == "DIABP", "Diastolic Blood Pressure (mmHg)"),
                   condition(x == "PULSE", "Pulse (bpm)"),
                   condition(x == "RESP", "Respirations (bpm)"))

    # Report ------------------------------------------------------------------

    sep("Report")

    put("Create plot object definition")
    plt <- create_plot(p_box, height = 4, width = 7, borders = "outside") |>
      titles("Figure 10. Box Plot: Median and Interquartile Range of Vital Signs by Treatment Arm",
             bold = TRUE, font_size = 12, align = "left") |>
      page_by(VSTESTCD, label = "Lab Test: ", format = lbfmt, blank_row = "none") |>
      footnotes(
        "Source: example6.rtf. {version$version.string}",
        "Note: Boxes span the interquartile range (25th to 75th percentile); horizontal line = median;",
        "whiskers = 1.5×IQR; individual outliers are those beyond this range.",
        font_size = 9, italics = TRUE, blank_row = "none"
      )

    put("Create report output path")
    pth <- file.path(tempdir(), "example6.rtf")

    put("Create report")
    rpt <- create_report(pth, font = "Arial", font_size = 10, output_type = "RTF") |>
      page_header("Sponsor: Company", right = "Study: ABC", blank_row = "below") |>
      add_content(plt) |>
      page_footer("Date Produced: {fapply(Sys.Date(), 'date7')}", right = "Page [pg] of [tpg]")

    put("Write report to file system")
    write_report(rpt)

    put("Close log")
    log_close()

    # View report
    # file.show(pth)

    # View log
    # file.show(lgpth)

## Output

And here are the first two pages of the report:

## Log

Here is the log for the above program:

    =========================================================================
    Log Path: C:/Users/dbosa/AppData/Local/Temp/Rtmpq0yZA5/log/example6.log
    Program Path: C:/Studies/Testing1/P6b.R
    Working Directory: C:/Studies/Testing1
    User Name: dbosa
    R Version: 4.4.3 (2025-02-28 ucrt)
    Machine: SOCRATES x86-64
    Operating System: Windows 10 x64 build 26100
    Base Packages: stats graphics grDevices utils datasets methods base
    Other Packages: tidylog_1.1.0 ggplot2_3.5.1 procs_1.0.7 reporter_1.4.5
    libr_1.3.9 logr_1.3.9 fmtr_1.7.0 common_1.1.4 sassy_1.2.9
    Log Start Time: 2025-12-12 14:27:32.328372
    =========================================================================

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

    Load data files

    # library 'dat': 2 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   DM       csv   87   24  45.8 Kb 2025-12-12 08:37:33
    2   VS       csv 3358   17 467.7 Kb 2025-12-12 08:37:33

    Prepare factor levels

    SCREENING
    DAY 1
    WEEK 2
    WEEK 4
    WEEK 6
    WEEK 8
    WEEK 12
    WEEK 16

    ARM A
    ARM B

    SYSBP
    DIABP
    PULSE
    RESP

    Prepare data for analysis

    datastep: columns decreased from 24 to 6

    # A tibble: 1,231 × 6
       STUDYID USUBJID    ARM   VISIT     VSTESTCD VSORRES
       <chr>   <chr>      <fct> <fct>     <fct>      <dbl>
     1 ABC     ABC-01-050 ARM B SCREENING DIABP         80
     2 ABC     ABC-01-050 ARM B DAY 1     DIABP         78
     3 ABC     ABC-01-050 ARM B WEEK 2    DIABP         64
     4 ABC     ABC-01-050 ARM B WEEK 4    DIABP         86
     5 ABC     ABC-01-050 ARM B WEEK 6    DIABP         70
     6 ABC     ABC-01-050 ARM B WEEK 8    DIABP         80
     7 ABC     ABC-01-050 ARM B WEEK 12   DIABP         64
     8 ABC     ABC-01-050 ARM B WEEK 16   DIABP         82
     9 ABC     ABC-01-050 ARM B SCREENING PULSE         76
    10 ABC     ABC-01-050 ARM B DAY 1     PULSE         68
    # ℹ 1,221 more rows
    # ℹ Use `print(n = ...)` to see more rows

    =========================================================================
    Create Plot
    =========================================================================

    Assign Colors

    Define Boxplot

    Create format for lab codes

    # A user-defined format: 4 conditions
      Name Type   Expression                           Label Order
    1  obj    U x == "SYSBP"  Systolic Blood Pressure (mmHg)    NA
    2  obj    U x == "DIABP" Diastolic Blood Pressure (mmHg)    NA
    3  obj    U x == "PULSE"                     Pulse (bpm)    NA
    4  obj    U  x == "RESP"              Respirations (bpm)    NA

    =========================================================================
    Report
    =========================================================================

    Create plot object definition

    Create report output path

    Create report

    Write report to file system

    # A report specification: 4 pages
    - file_path: 'C:\Users\dbosa\AppData\Local\Temp\Rtmpq0yZA5/example6.rtf'
    - output_type: RTF
    - units: inches
    - orientation: landscape
    - margins: top 0.5 bottom 0.5 left 1 right 1
    - line size/count: 9/42
    - page_header: left=Sponsor: Company right=Study: ABC
    - page_footer: left=Date Produced: 12DEC25 center= right=Page [pg] of [tpg]
    - content:
    # A plot specification:
    - data: 1231 rows, 6 cols
    - layers: 1
    - height: 4
    - width: 7
    - page by: VSTESTCD
    - title 1: 'Figure 10. Box Plot: Median and Interquartile Range of Vital Signs by Treatment Arm'
    - footnote 1: 'Source: example6.rtf. R version 4.4.3 (2025-02-28 ucrt)'
    - footnote 2: 'Note: Boxes span the interquartile range (25th to 75th percentile); horizontal line = median;'
    - footnote 3: 'whiskers = 1.5×IQR; individual outliers are those beyond this range.'

    Close log

    =========================================================================
    Log End Time: 2025-12-12 14:27:40.667776
    Log Elapsed Time: 0 00:00:08
    =========================================================================

Next: Example 7: Survival Analysis