# Introduction to the `clinUtils` package

#### Laure Cougnaud and Michela Pasetto

#### September 03, 2025

# 1 Introduction

This package `clinUtils` contains functionalities to facilitate the analysis of clinical datasets in R.

    library(clinUtils)

    # packages required for the examples in the vignette
    library(ggplot2)
    library(pander)
    library(htmltools)
    library(plyr)

Please note that the interactive outputs (table/visualization) in this vignette are only included if Pandoc is available (pre-requisite for R Markdown).

# 2 Data pre-processing

## 2.1 Data import

The `haven` R package enables to load SAS datasets in `sas7bdat` and `xpt` formats.

The function `loadDataADaMSDTM` is a wrapper to import multiple SAS datasets in `sas7bdat` or `xpt` format at once. It returns a list of dataset (once by domain). The variable labels are combined across datasets and available in a dedicated attribute.

    pathExampleDatasets <- list.files(
        path = system.file("extdata", "cdiscpilot01", "SDTM", package = "clinUtils"),
        pattern = "*.xpt",
        full.names = TRUE
    )

    data <- loadDataADaMSDTM(files = pathExampleDatasets)
    # A list is returned, each separated file is accessible via data$[fileName]
    pander(head(data$DM, 2))

Table continues below
STUDYID | DOMAIN | USUBJID | SUBJID | RFSTDTC | RFENDTC
--- | --- | --- | --- | --- | ---
CDISCPILOT01 | DM | 01-701-1148 | 1148 | 2013-08-23 | 2014-02-20
CDISCPILOT01 | DM | 01-701-1192 | 1192 | 2012-07-22 | 2013-01-20

Table continues below
RFXSTDTC | RFXENDTC | RFICDTC | RFPENDTC | DTHDTC | DTHFL | SITEID
--- | --- | --- | --- | --- | --- | ---
2013-08-23 | 2014-02-20 |  | 2014-02-20T15:07 |  |  | 701
2012-07-22 | 2013-01-20 |  | 2013-01-20T15:47 |  |  | 701

Table continues below
AGE | AGEU | SEX | RACE | ETHNIC | ARMCD
--- | --- | --- | --- | --- | ---
57 | YEARS | M | WHITE | NOT HISPANIC OR LATINO | Xan_Hi
80 | YEARS | F | WHITE | NOT HISPANIC OR LATINO | Xan_Lo

Table continues below
ARM | ACTARMCD | ACTARM | COUNTRY | DMDTC
--- | --- | --- | --- | ---
Xanomeline High Dose | Xan_Hi | Xanomeline High Dose | USA | 2013-08-14
Xanomeline Low Dose | Xan_Lo | Xanomeline Low Dose | USA | 2012-07-08

DMDY | DATASET
--- | ---
-9 | DM
-14 | DM

    pander(head(data$LB, 2))

Table continues below
STUDYID | DOMAIN | USUBJID | LBSEQ | LBTESTCD | LBTEST | LBCAT
--- | --- | --- | --- | --- | --- | ---
CDISCPILOT01 | LB | 01-701-1148 | 1 | ALB | Albumin | CHEMISTRY
CDISCPILOT01 | LB | 01-701-1148 | 38 | ALB | Albumin | CHEMISTRY

Table continues below
LBORRES | LBORRESU | LBORNRLO | LBORNRHI | LBSTRESC | LBSTRESN | LBSTRESU
--- | --- | --- | --- | --- | --- | ---
4.1 | g/dL | 3.3 | 4.9 | 41 | 41 | g/L
3.9 | g/dL | 3.3 | 4.9 | 39 | 39 | g/L

Table continues below
LBSTNRLO | LBSTNRHI | LBNRIND | LBBLFL | VISITNUM | VISIT | VISITDY
--- | --- | --- | --- | --- | --- | ---
33 | 49 | NORMAL | Y | 1 | SCREENING 1 | -7
33 | 49 | NORMAL |  | 4 | WEEK 2 | 14

LBDTC | LBDY | DATASET
--- | --- | ---
2013-08-14T13:04 | -9 | LB
2013-09-05T15:43 | 14 | LB

    pander(head(data$AE, 2))

Table continues below
STUDYID | DOMAIN | USUBJID | AESEQ | AESPID
--- | --- | --- | --- | ---
CDISCPILOT01 | AE | 01-701-1148 | 10 | E27
CDISCPILOT01 | AE | 01-701-1148 | 1 | E20

Table continues below
AETERM | AELLT | AELLTCD
--- | --- | ---
ACTINIC KERATOSIS | ACTINIC KERATOSIS | NA
APPLICATION SITE ERYTHEMA | APPLICATION SITE REDNESS | NA

Table continues below
AEDECOD | AEPTCD | AEHLT | AEHLTCD | AEHLGT | AEHLGTCD
--- | --- | --- | --- | --- | ---
ACTINIC KERATOSIS | NA | HLT_0609 | NA | HLGT_0490 | NA
APPLICATION SITE ERYTHEMA | NA | HLT_0617 | NA | HLGT_0152 | NA

Table continues below
AEBODSYS | AEBDSYCD | AESOC
--- | --- | ---
SKIN AND SUBCUTANEOUS TISSUE DISORDERS | NA | SKIN AND SUBCUTANEOUS TISSUE DISORDERS
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | NA | GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS

Table continues below
AESOCCD | AESEV | AESER | AEACN | AEREL | AEOUT
--- | --- | --- | --- | --- | ---
NA | MILD | N |  | NONE | NOT RECOVERED/NOT RESOLVED
NA | MILD | N |  | PROBABLE | NOT RECOVERED/NOT RESOLVED

Table continues below
AESCAN | AESCONG | AESDISAB | AESDTH | AESHOSP | AESLIFE | AESOD | AEDTC
--- | --- | --- | --- | --- | --- | --- | ---
N | N | N | N | N | N | N | 2014-02-20
N | N | N | N | N | N | N | 2013-09-05

AESTDTC | AEENDTC | AESTDY | AEENDY | DATASET
--- | --- | --- | --- | ---
2014-02-12 |  | 174 | NA | AE
2013-08-25 |  | 3 | NA | AE

    # Access labels for all variables
    labelVars <- attr(data, "labelVars")
    head(labelVars)

    ##                               STUDYID                                DOMAIN
    ##                    "Study Identifier"                 "Domain Abbreviation"
    ##                               USUBJID                                 AESEQ
    ##           "Unique Subject Identifier"                     "Sequence Number"
    ##                                AESPID                                AETERM
    ##          "Sponsor-Defined Identifier" "Reported Term for the Adverse Event"

    # Access label for a particular variable:
    labelVars["USUBJID"]

    ##                     USUBJID
    ## "Unique Subject Identifier"

## 2.2 Example datasets

To demonstrate the functionalities of this package, a subset of the datasets from the CDISC Pilot 01 study is available in the package as dataset.

### 2.2.1 ADaM

    # load data
    data(dataADaMCDISCP01)
    dataADaM <- dataADaMCDISCP01
    names(dataADaM)

    ## [1] "ADAE"     "ADCM"     "ADLBC"    "ADPP"     "ADQSADAS" "ADQSCIBC" "ADQSNPIX"
    ## [8] "ADSL"     "ADVS"

    pander(head(dataADaM$ADSL, 2))

Table continues below
STUDYID | USUBJID | SUBJID | SITEID | SITEGR1 | ARM
--- | --- | --- | --- | --- | ---
CDISCPILOT01 | 01-701-1148 | 1148 | 701 | 701 | Xanomeline High Dose
CDISCPILOT01 | 01-701-1192 | 1192 | 701 | 701 | Xanomeline Low Dose

Table continues below
TRT01P | TRT01PN | TRT01A | TRT01AN | TRTSDT
--- | --- | --- | --- | ---
Xanomeline High Dose | 81 | Xanomeline High Dose | 81 | 2013-08-23
Xanomeline Low Dose | 54 | Xanomeline Low Dose | 54 | 2012-07-22

Table continues below
TRTEDT | TRTDUR | AVGDD | CUMDOSE | AGE | AGEGR1 | AGEGR1N | AGEU | RACE
--- | --- | --- | --- | --- | --- | --- | --- | ---
2014-02-20 | 182 | 77.1 | 14040 | 57 | <65 | 1 | YEARS | WHITE
2013-01-20 | 183 | 54 | 9882 | 80 | 65-80 | 2 | YEARS | WHITE

Table continues below
RACEN | SEX | ETHNIC | SAFFL | ITTFL | EFFFL | COMP8FL
--- | --- | --- | --- | --- | --- | ---
1 | M | NOT HISPANIC OR LATINO | Y | Y | Y | Y
1 | F | NOT HISPANIC OR LATINO | Y | Y | Y | Y

Table continues below
COMP16FL | COMP24FL | DISCONFL | DSRAEFL | DTHFL | BMIBL | BMIBLGR1 | HEIGHTBL
--- | --- | --- | --- | --- | --- | --- | ---
Y | Y |  |  |  | 28.3 | 25-<30 | 175.3
Y | Y |  |  |  | 27.8 | 25-<30 | 151.1

Table continues below
WEIGHTBL | EDUCLVL | DISONSDT | DURDIS | DURDSGR1 | VISIT1DT | RFSTDTC
--- | --- | --- | --- | --- | --- | ---
87.1 | 15 | 2010-12-12 | 32.1 | >=12 | 2013-08-14 | 2013-08-23
63.5 | 12 | 2009-03-15 | 39.8 | >=12 | 2012-07-08 | 2012-07-22

RFENDTC | VISNUMEN | RFENDT | DCDECOD | DCREASCD | MMSETOT | DATASET
--- | --- | --- | --- | --- | --- | ---
2014-02-20 | 12 | 2014-02-20 | COMPLETED | Completed | 21 | ADSL
2013-01-20 | 12 | 2013-01-20 | COMPLETED | Completed | 23 | ADSL

    pander(head(dataADaM$ADLBC, 2))

Table continues below
STUDYID | SUBJID | USUBJID | TRTP | TRTPN
--- | --- | --- | --- | ---
CDISCPILOT01 | 1148 | 01-701-1148 | Xanomeline High Dose | 81
CDISCPILOT01 | 1148 | 01-701-1148 | Xanomeline High Dose | 81

Table continues below
TRTA | TRTAN | TRTSDT | TRTEDT | AGE | AGEGR1
--- | --- | --- | --- | --- | ---
Xanomeline High Dose | 81 | 2013-08-23 | 2014-02-20 | 57 | <65
Xanomeline High Dose | 81 | 2013-08-23 | 2014-02-20 | 57 | <65

Table continues below
AGEGR1N | RACE | RACEN | SEX | COMP24FL | DSRAEFL | SAFFL | AVISIT
--- | --- | --- | --- | --- | --- | --- | ---
1 | WHITE | 1 | M | Y |  | Y | Baseline
1 | WHITE | 1 | M | Y |  | Y | Baseline

Table continues below
AVISITN | ADY | ADT | VISIT | VISITNUM | PARAM
--- | --- | --- | --- | --- | ---
0 | -9 | 2013-08-14 | SCREENING 1 | 1 | Sodium (mmol/L)
0 | -9 | 2013-08-14 | SCREENING 1 | 1 | Potassium (mmol/L)

Table continues below
PARAMCD | PARAMN | PARCAT1 | AVAL | BASE | CHG | A1LO | A1HI | R2A1LO | R2A1HI
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
SODIUM | 18 | CHEM | 139 | 139 | NA | 132 | 147 | 1.053 | 0.9456
K | 19 | CHEM | 4 | 4 | NA | 3.4 | 5.4 | 1.176 | 0.7407

Table continues below
BR2A1LO | BR2A1HI | ANL01FL | ALBTRVAL | ANRIND | BNRIND | ABLFL | AENTMTFL
--- | --- | --- | --- | --- | --- | --- | ---
1.053 | 0.9456 |  | 81.5 | N | N | Y |
1.176 | 0.7407 |  | 4.1 | N | N | Y |

LBSEQ | LBNRIND | LBSTRESN | DATASET
--- | --- | --- | ---
26 | NORMAL | 139 | ADLBC
19 | NORMAL | 4 | ADLBC

    pander(head(dataADaM$ADAE, 2))

Table continues below
STUDYID | SITEID | USUBJID | TRTA | TRTAN | AGE
--- | --- | --- | --- | --- | ---
CDISCPILOT01 | 701 | 01-701-1148 | Xanomeline High Dose | 81 | 57
CDISCPILOT01 | 701 | 01-701-1148 | Xanomeline High Dose | 81 | 57

Table continues below
AGEGR1 | AGEGR1N | RACE | RACEN | SEX | SAFFL | TRTSDT | TRTEDT
--- | --- | --- | --- | --- | --- | --- | ---
<65 | 1 | WHITE | 1 | M | Y | 2013-08-23 | 2014-02-20
<65 | 1 | WHITE | 1 | M | Y | 2013-08-23 | 2014-02-20

Table continues below
ASTDT | ASTDTF | ASTDY | AENDT | AENDY | ADURN | ADURU
--- | --- | --- | --- | --- | --- | ---
2013-08-25 |  | 3 | NA | NA | NA |
2013-08-25 |  | 3 | NA | NA | NA |

Table continues below
AETERM | AELLT | AELLTCD
--- | --- | ---
APPLICATION SITE ERYTHEMA | APPLICATION SITE REDNESS | NA
APPLICATION SITE PRURITUS | APPLICATION SITE ITCHING | NA

Table continues below
AEDECOD | AEPTCD | AEHLT | AEHLTCD | AEHLGT | AEHLGTCD
--- | --- | --- | --- | --- | ---
APPLICATION SITE ERYTHEMA | NA | HLT_0617 | NA | HLGT_0152 | NA
APPLICATION SITE PRURITUS | NA | HLT_0317 | NA | HLGT_0338 | NA

Table continues below
AEBODSYS | AESOC | AESOCCD
--- | --- | ---
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | NA
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | NA

Table continues below
AESEV | AESER | AESCAN | AESCONG | AESDISAB | AESDTH | AESHOSP | AESLIFE
--- | --- | --- | --- | --- | --- | --- | ---
MILD | N | N | N | N | N | N | N
MILD | N | N | N | N | N | N | N

Table continues below
AESOD | AEREL | AEACN | AEOUT | AESEQ | TRTEMFL
--- | --- | --- | --- | --- | ---
N | PROBABLE |  | NOT RECOVERED/NOT RESOLVED | 1 | Y
N | PROBABLE |  | NOT RECOVERED/NOT RESOLVED | 2 | Y

Table continues below
AOCCFL | AOCCSFL | AOCCPFL | AOCC02FL | AOCC03FL | AOCC04FL
--- | --- | --- | --- | --- | ---
Y | Y | Y |  |  |
|  | Y |  |  |

CQ01NAM | AOCC01FL | DATASET
--- | --- | ---
DERMATOLOGIC EVENTS | Y | ADAE
DERMATOLOGIC EVENTS |  | ADAE

    # and variable labels
    labelVarsADaM <- attr(dataADaM, "labelVars")
    head(labelVarsADaM)

    ##                     STUDYID                      SITEID
    ##          "Study Identifier"     "Study Site Identifier"
    ##                     USUBJID                        TRTA
    ## "Unique Subject Identifier"          "Actual Treatment"
    ##                       TRTAN                         AGE
    ##      "Actual Treatment (N)"                       "Age"

### 2.2.2 SDTM

    # load data
    data(dataSDTMCDISCP01)
    dataSDTM <- dataSDTMCDISCP01
    names(dataSDTM)

    ##  [1] "AE"     "CM"     "DM"     "DS"     "EX"     "LB"     "MH"     "QS"
    ##  [9] "SUPPDM" "SV"     "VS"

    pander(head(dataSDTM$DM, 2))

Table continues below
STUDYID | DOMAIN | USUBJID | SUBJID | RFSTDTC | RFENDTC
--- | --- | --- | --- | --- | ---
CDISCPILOT01 | DM | 01-701-1148 | 1148 | 2013-08-23 | 2014-02-20
CDISCPILOT01 | DM | 01-701-1192 | 1192 | 2012-07-22 | 2013-01-20

Table continues below
RFXSTDTC | RFXENDTC | RFICDTC | RFPENDTC | DTHDTC | DTHFL | SITEID
--- | --- | --- | --- | --- | --- | ---
2013-08-23 | 2014-02-20 |  | 2014-02-20T15:07 |  |  | 701
2012-07-22 | 2013-01-20 |  | 2013-01-20T15:47 |  |  | 701

Table continues below
AGE | AGEU | SEX | RACE | ETHNIC | ARMCD
--- | --- | --- | --- | --- | ---
57 | YEARS | M | WHITE | NOT HISPANIC OR LATINO | Xan_Hi
80 | YEARS | F | WHITE | NOT HISPANIC OR LATINO | Xan_Lo

Table continues below
ARM | ACTARMCD | ACTARM | COUNTRY | DMDTC
--- | --- | --- | --- | ---
Xanomeline High Dose | Xan_Hi | Xanomeline High Dose | USA | 2013-08-14
Xanomeline Low Dose | Xan_Lo | Xanomeline Low Dose | USA | 2012-07-08

DMDY | DATASET
--- | ---
-9 | DM
-14 | DM

    pander(head(dataSDTM$LB, 2))

Table continues below
STUDYID | DOMAIN | USUBJID | LBSEQ | LBTESTCD | LBTEST | LBCAT
--- | --- | --- | --- | --- | --- | ---
CDISCPILOT01 | LB | 01-701-1148 | 1 | ALB | Albumin | CHEMISTRY
CDISCPILOT01 | LB | 01-701-1148 | 38 | ALB | Albumin | CHEMISTRY

Table continues below
LBORRES | LBORRESU | LBORNRLO | LBORNRHI | LBSTRESC | LBSTRESN | LBSTRESU
--- | --- | --- | --- | --- | --- | ---
4.1 | g/dL | 3.3 | 4.9 | 41 | 41 | g/L
3.9 | g/dL | 3.3 | 4.9 | 39 | 39 | g/L

Table continues below
LBSTNRLO | LBSTNRHI | LBNRIND | LBBLFL | VISITNUM | VISIT | VISITDY
--- | --- | --- | --- | --- | --- | ---
33 | 49 | NORMAL | Y | 1 | SCREENING 1 | -7
33 | 49 | NORMAL |  | 4 | WEEK 2 | 14

LBDTC | LBDY | DATASET
--- | --- | ---
2013-08-14T13:04 | -9 | LB
2013-09-05T15:43 | 14 | LB

    pander(head(dataSDTM$AE, 2))

Table continues below
STUDYID | DOMAIN | USUBJID | AESEQ | AESPID
--- | --- | --- | --- | ---
CDISCPILOT01 | AE | 01-701-1148 | 10 | E27
CDISCPILOT01 | AE | 01-701-1148 | 1 | E20

Table continues below
AETERM | AELLT | AELLTCD
--- | --- | ---
ACTINIC KERATOSIS | ACTINIC KERATOSIS | NA
APPLICATION SITE ERYTHEMA | APPLICATION SITE REDNESS | NA

Table continues below
AEDECOD | AEPTCD | AEHLT | AEHLTCD | AEHLGT | AEHLGTCD
--- | --- | --- | --- | --- | ---
ACTINIC KERATOSIS | NA | HLT_0609 | NA | HLGT_0490 | NA
APPLICATION SITE ERYTHEMA | NA | HLT_0617 | NA | HLGT_0152 | NA

Table continues below
AEBODSYS | AEBDSYCD | AESOC
--- | --- | ---
SKIN AND SUBCUTANEOUS TISSUE DISORDERS | NA | SKIN AND SUBCUTANEOUS TISSUE DISORDERS
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | NA | GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS

Table continues below
AESOCCD | AESEV | AESER | AEACN | AEREL | AEOUT
--- | --- | --- | --- | --- | ---
NA | MILD | N |  | NONE | NOT RECOVERED/NOT RESOLVED
NA | MILD | N |  | PROBABLE | NOT RECOVERED/NOT RESOLVED

Table continues below
AESCAN | AESCONG | AESDISAB | AESDTH | AESHOSP | AESLIFE | AESOD | AEDTC
--- | --- | --- | --- | --- | --- | --- | ---
N | N | N | N | N | N | N | 2014-02-20
N | N | N | N | N | N | N | 2013-09-05

AESTDTC | AEENDTC | AESTDY | AEENDY | DATASET
--- | --- | --- | --- | ---
2014-02-12 |  | 174 | NA | AE
2013-08-25 |  | 3 | NA | AE

    # and variable labels
    labelVarsSDTM <- attr(dataSDTM, "labelVars")
    head(labelVarsSDTM)

    ##                               STUDYID                                DOMAIN
    ##                    "Study Identifier"                 "Domain Abbreviation"
    ##                               USUBJID                                 AESEQ
    ##           "Unique Subject Identifier"                     "Sequence Number"
    ##                                AESPID                                AETERM
    ##          "Sponsor-Defined Identifier" "Reported Term for the Adverse Event"

## 2.3 Variable labels

Typical data stored in SAS contains the label for each variable available in the dataset.

The function `getLabelVar` extracts the label for the specified variable(s), ensuring that the variable code is used if no label is specified.

    # variable label is extracted from 'labelVars'
    getLabelVar(var = "AEDECOD", labelVars = labelVars)

    ##                   AEDECOD
    ## "Dictionary-Derived Term"

This function also supports extraction of variable labels from the ‘label’ attribute of the column, as in the tibble as returned by the `read_sas`/`read_xpt` functions from the `haven` package.

## 2.4 Get parameter label from its parameter code

In typical basic CDISC dataset (a.k.a BDS: Basic Data Structure), as laboratory, vital signs datasets, variables are available to store parameter and parameter code (`PARAM`/`PARAMCD` in ADaM).

The function `getLabelParamcd` get the label from specific parameter(s) code.

Variables with parameter name and code are based by default on standard ADaM CDISC parameter variables (`PARAM`/`PARAMCD`).

    # For ADaM dataset
    getLabelParamcd(paramcd = "CHOL", data = dataADaM$ADLB)

    ##                   CHOL
    ## "Cholesterol (mmol/L)"

    getLabelParamcd(paramcd = "BILI", data = dataADaM$ADLB)

    ##                 BILI
    ## "Bilirubin (umol/L)"

    # For SDTM dataset
    getLabelParamcd(paramcd = "CHOL", data = dataSDTM$LB, paramcdVar = "LBTESTCD", paramVar = "LBTEST")

    ##          CHOL
    ## "Cholesterol"

    getLabelParamcd(paramcd = "BILI", data = dataSDTM$LB, paramcdVar = "LBTESTCD", paramVar = "LBTEST")

    ##        BILI
    ## "Bilirubin"

# 3 Visualizations

## 3.1 Palette for CDISC variables

Palette for typical CDISC-variable(s) are available in the package.

### 3.1.1 Normal reference range indicators

Meaningful colors and symbols for a Normal Reference Range Indicator CDISC variable (`-NRIND`) are extracted via the `colorPaletteNRIND` and `shapePaletteNRIND` respectively:

    print(colorPaletteNRIND)

    ##      LOW   NORMAL     HIGH ABNORMAL  UNKNOWN       NA
    ## "orange" "green4" "orange"    "red"   "grey"   "grey"

    print(shapePaletteNRIND)

    ##      LOW   NORMAL     HIGH ABNORMAL  UNKNOWN       NA
    ##       25       21       24       18        3        3

    plot(
        x = seq_along(colorPaletteNRIND),
        col = colorPaletteNRIND,
        bg = colorPaletteNRIND,
        pch = shapePaletteNRIND
    )
    text(
        x = seq_along(colorPaletteNRIND),
        labels = names(colorPaletteNRIND), pos = 3
    )
    title("Palette for CDISC normal reference range indicator")

The `getPaletteCDISC` function extracts such palette for a specified variable.

This retains only the categories available in the variable, and ensures that extra symbols are extracted in case non standard categories are available in the data.

    dataPlot <- subset(dataSDTM$LB, LBTEST == "Leukocytes")

    colorPalette <- getPaletteCDISC(x = dataPlot$LBNRIND, var = "NRIND", type = "color")
    print(colorPalette)

    ##      LOW   NORMAL     HIGH
    ## "orange" "green4" "orange"

    shapePalette <- getPaletteCDISC(x = dataPlot$LBNRIND, var = "NRIND", type = "shape")
    print(shapePalette)

    ##    LOW NORMAL   HIGH
    ##     25     21     24

    # visualize profile over time
    gg <- ggplot(data = dataPlot) +
        geom_point(aes(x = LBDY, y = LBSTRESN,
                color = LBNRIND, fill = LBNRIND, shape = LBNRIND)) +
        ggtitle("Evolution of Leukocytes actual value over time")
    print(gg)

    # use 'standard' symbols/colors
    # ('limits' is only required if the categories are not already ordered in LBNRIND)
    gg +
        scale_color_manual(values = colorPalette, limits = names(colorPalette)) +
        scale_fill_manual(values = colorPalette, limits = names(colorPalette)) +
        scale_shape_manual(values = shapePalette, limits = names(colorPalette))

## 3.2 Get default palettes

Default palettes for visualizations are included in the package.

Palettes can be extracted based on a variable, or number of elements.

These packages are included for consistency across the entire suite of R packages.

    dataPlot <- subset(dataADaM$ADLB, PARAMCD == "CHOL")

    # extract palettes
    colorPalette <- getColorPalette(x = dataPlot$USUBJID)
    shapePalette <- getShapePalette(x = dataPlot$USUBJID)
    linetypePalette <- getLinetypePalette(x = dataPlot$USUBJID)

    # create the plot
    ggplot(data = dataPlot, aes(x = ADY, y = CHG, color = USUBJID)) +
        geom_point(aes(shape = USUBJID)) +
        geom_line(aes(linetype = USUBJID, group = USUBJID)) +
        scale_color_manual(values = colorPalette) +
        scale_shape_manual(values = shapePalette) +
        scale_linetype_manual(values = linetypePalette) +
        labs(x = "Relative day", y = "Change from baseline",
            title = "Profile plot of cholesterol change from baseline")

    ## Warning: Removed 7 rows containing missing values or values outside the scale range
    ## (`geom_point()`).

    ## Warning: Removed 7 rows containing missing values or values outside the scale range
    ## (`geom_line()`).

Custom palettes can be specified via the `palette` parameter.

# 4 Tables

## 4.1 Rounding

In R, numbers are by default rounded to the even digit for rounding off a 5.

Numbers can be rounded based on the ‘rounding up’ strategy for rounding off a 5 with `roundHalfUp` & `roundHalfUpTextFormat`. This is useful when statistics created with the `SAS` software should be reproduced in R.

    # round up
    roundHalfUp(c(0.45, 0.55), 1)

    ## [1] 0.5 0.6

    # versus R default:
    round(c(0.45, 0.55), 1)

    ## [1] 0.4 0.6

## 4.2 Display data in an interactive table

The `getClinDT` function is an utility function, based on the the DT package, with sensitive default settings and extra common functionalities of interest for data in clinical trials, as listing or summary tables of descriptive statistics.

There are built-in functionalities to expand row variable(s) to display patient-specific information or include bar visualization.

    dataTEAE <- subset(dataADaM$ADAE, SAFFL == "Y" & TRTEMFL == "Y")

    # set column names to labels
    labelVarsTEAE <- getLabelVar(
        var = colnames(dataTEAE),
        labelVars = labelVarsADaM
    )
    colnamesTEAE <- setNames(names(labelVarsTEAE), labelVarsTEAE)

    dataTEAE <- dataTEAE[order(dataTEAE$AESOC), ]

    getClinDT(
        dataTEAE,
        colnames = colnamesTEAE,
        rowGroupVar = c("AESOC"),
        barVar = "AGE",
        barRange = c(0, 100),
        caption = "Listing of treatment-emergent adverse events on the safety analysis set"
    )

## 4.3 Comparison of tables

In clinical trials, datasets are typically delivered in successive batches, depending on the patient recruitment and the different milestones of the study.

The changes between successive data deliveries can be compared with the `compareTables` function.

    # Build example dataset with treatment-emergent adverse events
    # of multiple batches

    varsListing <- c("USUBJID", "AESOC", "AEDECOD", "ASTDT", "AESEV", "AEOUT")
    dataTEAEListing <- dataTEAE[, varsListing]

    # simulate removal of observations in new batch
    dataTEAENew <- dataTEAE[-sample.int(n = nrow(dataTEAEListing), size = 3), ]

    # simulate addition of observations in new batch
    dataTEAEOld <- dataTEAE[-sample.int(n = nrow(dataTEAEListing), size = 3), ]

    # simulate change of observations
    dataTEAEOld[seq_len(2), "AESEV"] <- "SEVERE"

    refVars <- c("USUBJID", "AESOC", "AEDECOD", "ASTDT")
    tableComparison <- compareTables(
        newData = dataTEAENew,
        oldData = dataTEAEOld,
        referenceVars = refVars,
        changeableVars = setdiff(colnames(dataTEAEListing), refVars),
        # parameters passed to datatable
        colnames = setNames(names(labelVarsADaM), labelVarsADaM)
    )

The new, old datasets with change information, or the difference between datasets are extracted. See documentation of `outputType` parameter for further details.

The table below highlight the differences between the datasets in an interactive table.

    tableComparison$`table-comparison-interactive`

# 5 Reporting

## 5.1 Include list of objects in a Rmarkdown document

Dedicated functions: `knitPrintListPlots` and `knitPrintListObjects` are available to include a list of plots/objects in a `Rmarkdown` document, enabling to specify any (knitr) chunk options and title header for each object.

This function inserts each object in a separated code chunk, such as independent option (as figure dimensions) can be specified for each object.

Please note that this function should be used within a chunk having the option: `results = 'asis'`.

### 5.1.1 Static visualizations (`ggplot2`)

For example, by default in knitr the options to specify figure dimensions should be the same for all plots generated from the same chunk (`fig.height`/`fig.width`).

Plots can be included with different specified dimension with: `knitPrintListPlots`.

    dataLB <- subset(dataSDTM$LB,
        LBTESTCD %in% c("ALB", "ALT", "CHOL", "HCT", "KETONES", "PH")
    )
    dataLB$ACTARM <- dataSDTM$DM$ACTARM[match(dataLB$USUBJID, dataSDTM$DM$USUBJID)]

    # create plots:
    listPlotsLB <- plyr::dlply(dataLB, "LBCAT", function(data)
          ggplot(data = data) +
              geom_histogram(aes(fill = LBNRIND, x = ACTARM), stat = "count", position = "dodge") +
              facet_wrap(~LBTEST) +
              theme(axis.text.x = element_text(angle = -45, hjust = 0))
    )
    # n2mfrow: extract default dimensions for a specified number of plots
    figDim <- plyr::dlply(dataLB, "LBCAT", function(data)
          n2mfrow(length(unique(data$LBTESTCD)))
    )
    knitPrintListPlots(
        plotsList = listPlotsLB,
        generalLabel = "lab-hist-static",
        type = "ggplot2",
        # set caption for each figure
        fig.cap = paste("Barplot of", tolower(names(listPlotsLB)), "measurements"),
        # specify different dimensions
        fig.width = sapply(figDim, "[[", 1) * 2 + 1, # 3 in for each plot + 1 in for the legend
        fig.height = sapply(figDim, "[[", 2) * 2 + 2, # 3 in for each plot + 2 for x-axis labels
        # include title before each visualization
        titles = simpleCap(tolower(names(listPlotsLB))),
        titleLevel = 4
    )

#### 5.1.1.1 Chemistry

Barplot of chemistry measurements

#### 5.1.1.2 Hematology

Barplot of hematology measurements

#### 5.1.1.3 Urinalysis

Barplot of urinalysis measurements

### 5.1.2 Interactive visualizations (`plotly`)

A list of interactive figures is created with the `plotly` package:

    library(plotly)
    listPlotsInteractiveLB <- sapply(listPlotsLB, function(ggplot)
          ggplotly(ggplot) %>% partial_bundle()
        , simplify = FALSE)

#### 5.1.2.1 `htmltools::tagList`

A list of interactive figures can be included within a Rmarkdown document with the `tagList` function of the `htmltools` package.

    tagListArgs <- mapply(list,
        # section header
        lapply(names(listPlotsInteractiveLB), htmltools::h4),
        # interactive plots
        listPlotsInteractiveLB,
        SIMPLIFY = FALSE
    )
    tagListArgs <- unlist(tagListArgs, recursive = FALSE)
    do.call(htmltools::tagList, tagListArgs)

#### CHEMISTRY

#### HEMATOLOGY

#### URINALYSIS

#### 5.1.2.2 Inclusion in a separated chunk

The function `knitPrintListPlots` with the `type` set to ‘plotly’ enables to include additionally e.g. a caption or a title.

    knitPrintListPlots(
        plotsList = listPlotsInteractiveLB,
        generalLabel = "lab-hist-interactive",
        type = "plotly",
        # include title before each visualization
        titles = simpleCap(tolower(names(listPlotsInteractiveLB))),
        titleLevel = 5
    )

##### 5.1.2.2.1 Chemistry

##### 5.1.2.2.2 Hematology

##### 5.1.2.2.3 Urinalysis

### 5.1.3 Tables (`flextable`)

The `flextable` package enables to create highly customizable tables for Word/PowerPoint format (among others).

The function `knitPrintListObjects` enables to include a list of `flextable` objects within a single chunk.

Please note that the following chunk option should be used: `results = 'asis'`.

    library(flextable)
    listFtLB <- plyr::dlply(dataLB, "LBCAT", function(dataParcat){
          flextable::flextable(data = head(dataParcat))
        })

    knitPrintListObjects(
        xList = listFtLB,
        generalLabel = "lab-listing-ft",
        titles = simpleCap(tolower(names(listFtLB))),
        # different alignment for each table
        ft.align = c("center", "right", "left"),
        titleLevel = 4
    )

#### 5.1.3.1 Chemistry

STUDYID | DOMAIN | USUBJID | LBSEQ | LBTESTCD | LBTEST | LBCAT | LBORRES | LBORRESU | LBORNRLO | LBORNRHI | LBSTRESC | LBSTRESN | LBSTRESU | LBSTNRLO | LBSTNRHI | LBNRIND | LBBLFL | VISITNUM | VISIT | VISITDY | LBDTC | LBDY | DATASET | ACTARM
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
CDISCPILOT01 | LB | 01-701-1148 | 1 | ALB | Albumin | CHEMISTRY | 4.1 | g/dL | 3.3 | 4.9 | 41 | 41 | g/L | 33 | 49 | NORMAL | Y | 1 | SCREENING 1 | -7 | 2013-08-14T13:04 | -9 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 38 | ALB | Albumin | CHEMISTRY | 3.9 | g/dL | 3.3 | 4.9 | 39 | 39 | g/L | 33 | 49 | NORMAL |  | 4 | WEEK 2 | 14 | 2013-09-05T15:43 | 14 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 73 | ALB | Albumin | CHEMISTRY | 3.8 | g/dL | 3.3 | 4.9 | 38 | 38 | g/L | 33 | 49 | NORMAL |  | 5 | WEEK 4 | 28 | 2013-09-19T14:30 | 28 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 103 | ALB | Albumin | CHEMISTRY | 4.1 | g/dL | 3.3 | 4.9 | 41 | 41 | g/L | 33 | 49 | NORMAL |  | 7 | WEEK 6 | 42 | 2013-10-03T12:17 | 42 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 133 | ALB | Albumin | CHEMISTRY | 3.8 | g/dL | 3.3 | 4.9 | 38 | 38 | g/L | 33 | 49 | NORMAL |  | 8 | WEEK 8 | 56 | 2013-10-18T15:46 | 57 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 163 | ALB | Albumin | CHEMISTRY | 3.8 | g/dL | 3.3 | 4.9 | 38 | 38 | g/L | 33 | 49 | NORMAL |  | 9 | WEEK 12 | 84 | 2013-11-17T10:59 | 87 | LB | Xanomeline High Dose

#### 5.1.3.2 Hematology

STUDYID | DOMAIN | USUBJID | LBSEQ | LBTESTCD | LBTEST | LBCAT | LBORRES | LBORRESU | LBORNRLO | LBORNRHI | LBSTRESC | LBSTRESN | LBSTRESU | LBSTNRLO | LBSTNRHI | LBNRIND | LBBLFL | VISITNUM | VISIT | VISITDY | LBDTC | LBDY | DATASET | ACTARM
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
CDISCPILOT01 | LB | 01-701-1148 | 17 | HCT | Hematocrit | HEMATOLOGY | 49.0 | % | 39 | 54 | 0.49 | 0.49 | 1 | 0.39 | 0.54 | NORMAL | Y | 1 | SCREENING 1 | -7 | 2013-08-14T13:04 | -9 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 54 | HCT | Hematocrit | HEMATOLOGY | 43.0 | % | 39 | 54 | 0.43 | 0.43 | 1 | 0.39 | 0.54 | NORMAL |  | 4 | WEEK 2 | 14 | 2013-09-05T15:43 | 14 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 88 | HCT | Hematocrit | HEMATOLOGY | 44.0 | % | 39 | 54 | 0.44 | 0.44 | 1 | 0.39 | 0.54 | NORMAL |  | 5 | WEEK 4 | 28 | 2013-09-19T14:30 | 28 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 118 | HCT | Hematocrit | HEMATOLOGY | 44.0 | % | 39 | 54 | 0.44 | 0.44 | 1 | 0.39 | 0.54 | NORMAL |  | 7 | WEEK 6 | 42 | 2013-10-03T12:17 | 42 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 148 | HCT | Hematocrit | HEMATOLOGY | 44.0 | % | 39 | 54 | 0.44 | 0.44 | 1 | 0.39 | 0.54 | NORMAL |  | 8 | WEEK 8 | 56 | 2013-10-18T15:46 | 57 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 179 | HCT | Hematocrit | HEMATOLOGY | 45.0 | % | 39 | 54 | 0.45 | 0.45 | 1 | 0.39 | 0.54 | NORMAL |  | 9 | WEEK 12 | 84 | 2013-11-17T10:59 | 87 | LB | Xanomeline High Dose

#### 5.1.3.3 Urinalysis

STUDYID | DOMAIN | USUBJID | LBSEQ | LBTESTCD | LBTEST | LBCAT | LBORRES | LBORRESU | LBORNRLO | LBORNRHI | LBSTRESC | LBSTRESN | LBSTRESU | LBSTNRLO | LBSTNRHI | LBNRIND | LBBLFL | VISITNUM | VISIT | VISITDY | LBDTC | LBDY | DATASET | ACTARM
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
CDISCPILOT01 | LB | 01-701-1148 | 20 | KETONES | Ketones | URINALYSIS | 0 | NO UNITS |  |  | 0 | 0 |  |  |  | NORMAL | Y | 1 | SCREENING 1 | -7 | 2013-08-14T13:04 | -9 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 57 | KETONES | Ketones | URINALYSIS | 0 | NO UNITS |  |  | 0 | 0 |  |  |  | NORMAL |  | 4 | WEEK 2 | 14 | 2013-09-05T15:43 | 14 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 182 | KETONES | Ketones | URINALYSIS | 0 | NO UNITS |  |  | 0 | 0 |  |  |  | NORMAL |  | 9 | WEEK 12 | 84 | 2013-11-17T10:59 | 87 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 277 | KETONES | Ketones | URINALYSIS | 0 | NO UNITS |  |  | 0 | 0 |  |  |  | NORMAL |  | 12 | WEEK 24 | 168 | 2014-02-08T14:40 | 170 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 27 | PH | pH | URINALYSIS | 5.0 | NO UNITS | 5 | 8 | 5 | 5 |  | 5 | 8 | NORMAL | Y | 1 | SCREENING 1 | -7 | 2013-08-14T13:04 | -9 | LB | Xanomeline High Dose
CDISCPILOT01 | LB | 01-701-1148 | 64 | PH | pH | URINALYSIS | 7.0 | NO UNITS | 5 | 8 | 7 | 7 |  | 5 | 8 | NORMAL |  | 4 | WEEK 2 | 14 | 2013-09-05T15:43 | 14 | LB | Xanomeline High Dose

Please note that the table alignment option is more visible in a Word output format.

# 6 Appendix

## 6.1 Session info

R version 4.5.1 (2025-06-13)

Platform: x86_64-pc-linux-gnu

locale: LC_CTYPE=en_US.UTF-8, LC_NUMERIC=C, LC_TIME=en_US.UTF-8, LC_COLLATE=C, LC_MONETARY=en_US.UTF-8, LC_MESSAGES=en_US.UTF-8, LC_PAPER=en_US.UTF-8, LC_NAME=C, LC_ADDRESS=C, LC_TELEPHONE=C, LC_MEASUREMENT=en_US.UTF-8 and LC_IDENTIFICATION=C

attached base packages: stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: flextable(v.0.9.10), plotly(v.4.11.0), plyr(v.1.8.9), htmltools(v.0.5.8.1), pander(v.0.6.6), ggplot2(v.3.5.2), clinUtils(v.0.2.1) and knitr(v.1.50)

loaded via a namespace (and not attached): sass(v.0.4.10), generics(v.0.1.4), tidyr(v.1.3.1), fontLiberation(v.0.1.0), xml2(v.1.4.0), hms(v.1.1.3), digest(v.0.6.37), magrittr(v.2.0.3), evaluate(v.1.0.5), grid(v.4.5.1), RColorBrewer(v.1.1-3), fastmap(v.1.2.0), jsonlite(v.2.0.0), zip(v.2.3.3), httr(v.1.4.7), purrr(v.1.1.0), crosstalk(v.1.2.2), viridisLite(v.0.4.2), scales(v.1.4.0), fontBitstreamVera(v.0.1.1), textshaping(v.1.0.3), lazyeval(v.0.2.2), jquerylib(v.0.1.4), cli(v.3.6.5), fontquiver(v.0.2.1), rlang(v.1.1.6), withr(v.3.0.2), cachem(v.1.1.0), yaml(v.2.3.10), gdtools(v.0.4.3), officer(v.0.6.10), tools(v.4.5.1), uuid(v.1.2-1), tzdb(v.0.5.0), dplyr(v.1.1.4), DT(v.0.33), forcats(v.1.0.0), curl(v.7.0.0), vctrs(v.0.6.5), R6(v.2.6.1), lifecycle(v.1.0.4), htmlwidgets(v.1.6.4), ragg(v.1.5.0), pkgconfig(v.2.0.3), pillar(v.1.11.0), bslib(v.0.9.0), gtable(v.0.3.6), data.table(v.1.17.8), glue(v.1.8.0), Rcpp(v.1.1.0), systemfonts(v.1.2.3), haven(v.2.5.5), xfun(v.0.53), tibble(v.3.3.0), tidyselect(v.1.2.1), farver(v.2.1.2), rmarkdown(v.2.29), labeling(v.0.4.3), readr(v.2.1.5), compiler(v.4.5.1), askpass(v.1.2.1) and openssl(v.2.3.3)