Author: Laure Cougnaud
Date: 2025-09-04

# Template to create patient profiles for SDTM datasets

### Study: X, Batch X

#### Laure Cougnaud

#### September 04, 2025

# 1 Introduction

This document presents an example of standard patient profile report for a typical CDISC ‘Study Data Tabulation Model’ (a.k.a SDTM) datasets.

This document is intended to be used as a template to create patient profile report for your specific study. You can copy the source code of this document into your working directory with:

    # get template from the package
    pathTemplate <- system.file(
        "doc", "patientProfiles-template-SDTM.Rmd",
        package = "patientProfilesVis"
    )
    file.copy(from = pathTemplate, to = ".")
    # Note: your current working directory can be checked with: getwd()

This template can then be further tailored to your specific study (changing data path, variables available in the data, …).

The following R packages are required:

        library(patientProfilesVis)

## 1.1 Dataset

The SDTMs datasets are imported into R.

        library(clinUtils)

        # For this vignette, an example STDM dataset is used
        # (subset of the CDISC Pilot 01 study)
        data(dataSDTMCDISCP01, package = "clinUtils")
        dataAll <- dataSDTMCDISCP01
        labelVars <- attr(dataSDTMCDISCP01, "labelVars")

        # If SDTM datasets should be imported from the external folder,
        # the following code could be used:
    #   pathFiles <- list.files(path = "/path/to/data", pattern = "*.sas7bdat$", full.names = TRUE)
    #   dataAll <- loadDataADaMSDTM(files = pathFiles)
    #   labelVars <- attr(dataAll, "labelVars")

        patientProfilesPlots <- list()

# 2 Creation of patient profiles

For each domain of interest, the subject profiles are created. See the vignette:

## 2.1 Demographics

        dmPlots <- subjectProfileTextPlot(
            data = dataAll$DM,
            paramValueVar = c(
                "SEX", "RACE", "ETHNIC",
                "COUNTRY", "ARM",
                "AGE", "RFSTDTC"
            ),
            # optional:
            labelVars = labelVars
        )
        patientProfilesPlots <- c(patientProfilesPlots, list(DM = dmPlots))

## 2.2 Medical history

        mhPlots <- subjectProfileTextPlot(
            data = dataAll$MH,
            paramValueVar = c("MHDECOD", "MHTERM", "MHSTDTC"), #"MHENDTC" if available
            # optional:
            paramGroupVar = c("MHSTDTC", "MHDY"),
            title = "Medical history (Start - End)",
            labelVars = labelVars,
            table = TRUE
        )
        patientProfilesPlots <- c(patientProfilesPlots, list(MH = mhPlots))

## 2.3 Concomitant medications

        cmPlots <- subjectProfileIntervalPlot(
            data = dataAll$CM,
            paramVar = c(
                "CMTRT",
                "CMDOSE", "CMDOSU", "CMROUTE",
                "CMDOSFRQ"
            ),
            timeStartVar = "CMSTDY",
            timeEndVar = "CMENDY",
            # optional:
            paramGroupVar = "CMCLAS", # or CMINDC
            colorVar = "CMCLAS", # or CMINDC
            labelVars = labelVars,
            title = "Concomitant medications",
            # To zoom in axis scale in study time frame
            # (to avoid scale is focused on negative pre-study time frame for CM)
            timeTrans = getTimeTrans(type = "asinh-neg"),
            alpha = 0.8,
            timeAlign = FALSE
        )

    ## 171 record(s) with missing Study Day of Start of Medication and 208 record(s) with missing Study Day of End of Medication are imputed with minimal imputation.

        patientProfilesPlots <- c(patientProfilesPlots, list(CM = cmPlots))

## 2.4 Treatment exposure

        exPlots <- subjectProfileIntervalPlot(
            data = dataAll$EX,
            paramVar = c(
                "EXTRT", "EXDOSE", "EXDOSU",
                "EXDOSFRM", "EXDOSFRQ", "EXROUTE"
            ), # "EXTPT" if available
            timeStartVar = "EXSTDY",
            timeEndVar = "EXENDY",
            # optional:
            colorVar = "EXDOSFRM",
            title = "Treatment exposure",
            alpha = 0.8,
            labelVars = labelVars
        )

        patientProfilesPlots <- c(patientProfilesPlots, list(EX = exPlots))

## 2.5 Adverse events

        dataAE <- dataAll$AE$AESEV
        # to adapt for specific dataset
        dataAE$AESEV <- factor(dataAll$AE$AESEV, levels = c("MILD", "MODERATE", "SEVERE"))

    ## Warning in dataAE$AESEV <- factor(dataAll$AE$AESEV, levels = c("MILD",
    ## "MODERATE", : Coercing LHS to a list

        aePlots <- subjectProfileIntervalPlot(
            data = dataAll$AE,
            paramVar = "AEDECOD", # AETERM, depending on coding
            timeStartVar = "AESTDY",
            timeEndVar = "AEENDY",
            # optional:
            paramGroupVar = "AESOC",
            colorVar = "AESEV",
            title = "Adverse events",
            timeAlign = FALSE,
            alpha = 0.8,
            labelVars = labelVars
        )

    ## 3 record(s) with missing Study Day of Start of Adverse Event and 19 record(s) with missing Study Day of End of Adverse Event are imputed with minimal imputation.

        patientProfilesPlots <- c(patientProfilesPlots, list(AE = aePlots))

## 2.6 Laboratory measurements

        dataLB <- dataAll$LB
        # to adapt for dataset
        dataLB$LBNRIND <- factor(dataLB$LBNRIND, levels = c("LOW", "NORMAL", "HIGH", "ABNORMAL"))

        # specify custom color and shape palette
        colorPaletteLB <- clinUtils::getPaletteCDISC(x = dataLB$LBNRIND, var = "NRIND", type = "color")
        shapePaletteLB <- clinUtils::getPaletteCDISC(x = dataLB$LBNRIND, var = "NRIND", type = "shape")

        lbPlots <- subjectProfileLinePlot(
            data = dataLB,
            paramValueVar = "LBSTRESN",
            paramNameVar = "LBTEST",
            timeVar = "LBDY",
            # optional
            paramValueRangeVar = c("LBSTNRLO", "LBSTNRHI"),
            paramGroupVar = "LBCAT", # "LBSCAT" if available
            colorVar = "LBNRIND", colorPalette = colorPaletteLB,
            shapeVar = "LBNRIND", shapePalette = shapePaletteLB,
            shapeSize = 4,
            title = "Laboratory test measurements",
            alpha = 0.8,
            labelVars = labelVars
        )

        patientProfilesPlots <- c(patientProfilesPlots, list(LB = lbPlots))

## 2.7 Vital signs

        # If available, reference range indicator
        # could be displayed via the color/shape variable

        vsPlots <- subjectProfileLinePlot(
            data = dataAll$VS,
            paramValueVar = "VSSTRESN",
            paramNameVar = "VSTEST",
            timeVar = "VSDY",
            # optional
    #       paramGroupVar = "VSCAT", # if available
            colorVar = "VSPOS", # if available
            shapeSize = 2,
            title = "Vital signs",
            alpha = 0.8,
            labelVars = labelVars
        )

    ## Empty records in the: 'VSPOS' variable are converted to NA.

        patientProfilesPlots <- c(patientProfilesPlots, list(VS = vsPlots))

## 2.8 Electrocardiogram

Note: no ECG data is available for the example dataset, so this domain is not considered for the example.

        # If available, reference range indicator
        # could be displayed via the color/shape variable

        egPlots <- subjectProfileLinePlot(
            data = dataAll$EG,
            paramValueVar = "EGSTRESN",
            paramNameVar = "EGTEST",
            timeVar = "EGDY",
            # optional
            title = "Electrocardiogram",
            alpha = 0.8,
            labelVars = labelVars
        )
        patientProfilesPlots <- c(patientProfilesPlots, list(EG = egPlots))

## 2.9 Creation of the reports

The subject profile report is created by subject.

Please note that the subject profile reports are not not created by default in the vignette, for time constraints.

Feel free to run yourself the code, and check the resulting pdf reports!

        pathsPatientProfiles <- createSubjectProfileReport(

            listPlots = patientProfilesPlots,

            # optional
            reportPerSubject = TRUE,
            verbose = TRUE,
            outputFile = './patientProfiles/subjectProfile.pdf',
            timeAlign = "all", timeAlignPerSubject = "all",
            exportBatchSize = 5,

            # export subjects with highest adverse events severity
            subjectSortData = dataAll$AE,
            subjectSortVar = "AESEV",
            subjectSortDecreasing = TRUE,

    #       # only patients with severe adverse events
    #       subjectSubsetData = dataAll$AE,
    #       subsetVar = "AETOXGR",
    #       subsetValue = "SEVERE"
        )

# 3 Session information

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: pander(v.0.6.6), clinUtils(v.0.2.0), patientProfilesVis(v.2.0.10) and knitr(v.1.50)

loaded via a namespace (and not attached): gtable(v.0.3.6), jsonlite(v.2.0.0), compiler(v.4.5.1), Rcpp(v.1.1.0), stringr(v.1.5.1), parallel(v.4.5.1), gridExtra(v.2.3), jquerylib(v.0.1.4), scales(v.1.4.0), yaml(v.2.3.10), fastmap(v.1.2.0), ggplot2(v.3.5.2.9002), R6(v.2.6.1), plyr(v.1.8.9), htmlwidgets(v.1.6.4), forcats(v.1.0.0), tibble(v.3.3.0), bslib(v.0.9.0), pillar(v.1.11.0), RColorBrewer(v.1.1-3), rlang(v.1.1.6), DT(v.0.33), stringi(v.1.8.7), cachem(v.1.1.0), xfun(v.0.53), sass(v.0.4.10), S7(v.0.2.0), viridisLite(v.0.4.2), cli(v.3.6.5), magrittr(v.2.0.3), crosstalk(v.1.2.2), digest(v.0.6.37), grid(v.4.5.1), haven(v.2.5.5), hms(v.1.1.3), cowplot(v.1.2.0), lifecycle(v.1.0.4), vctrs(v.0.6.5), evaluate(v.1.0.5), glue(v.1.8.0), data.table(v.1.17.8), farver(v.2.1.2), reshape2(v.1.4.4), rmarkdown(v.2.29), tools(v.4.5.1), pkgconfig(v.2.0.3) and htmltools(v.0.5.8.1)