This vignette focuses on the visualizations available in the clinDataReview
package.
We will use example data sets from the clinUtils
package.
If you have doubts on the data format, please check first the vignette on data preprocessing available at: here.
If everything is clear on that side, let’s get started!
Please note that the patient profiles and interactive visualizations are only displayed in the vignette if Pandoc is available.
library(clinDataReview)
library(plotly)
library(clinUtils)
data(dataADaMCDISCP01)
attr(dataADaMCDISCP01, "labelVars")
labelVars <-
c(
varsLB <-"PARAM", "PARAMCD", "USUBJID", "TRTP",
"ADY", "VISITNUM", "VISIT", "LBSTRESN"
) dataADaMCDISCP01$ADLBC[, varsLB]
dataLB <-
c("USUBJID", "AESOC", "AEDECOD", "ASTDY", "AENDY", "AESEV")
varsAE <- dataADaMCDISCP01$ADAE[, varsAE]
dataAE <-
c("RFSTDTC", "USUBJID")
varsDM <- dataADaMCDISCP01$ADSL[, varsDM] dataDM <-
The interactive visualizations of the clinical data package include functionalities to link a plot to patient-specific report, e.g. patient profiles created with the patientProfilesVis
package.
Such patient profiles can be created via a config file, with a dedicated template report available in the clinDataReview
package.
A simple patient profile report for each subject in the example dataset is created below.
Please note that the patient profiles are created and included in the interactive visualizations only during an interactive session (via interactive()
) .
# create a directory to store the patient profiles:
"patientProfiles"
patientProfilesDir <-dir.create(patientProfilesDir)
# get examples of parameters for the report
system.file("skeleton", "config", package = "clinDataReview")
configDir <- getParamsFromConfig(
params <-configDir = configDir,
configFile = "config-patientProfiles.yml"
)# create patient profile with only one panel for the demo
$patientProfilesParams <- params$patientProfilesParams[1]
params# use dataset from the clinUtils package
$pathDataFolder <- system.file("extdata", "cdiscpilot01", "SDTM", package = "clinUtils")
params# store patient profile in this folder:
$patientProfilePath <- patientProfilesDir
params
# create patient profiles
clinDataReview::getPathTemplate(params$template)
pathTemplate <-file.copy(from = pathTemplate, to = ".")
rmarkdown::render(
report <-input = basename(pathTemplate),
envir = new.env()
)unlink(basename(pathTemplate))
unlink(basename(report))
Please refer to the vignette about reporting for more details on how to set up a config file and use template reports available in the package.
You can directly skip to reporting vignette, which is available here or run in your console the command below.
vignette("clinDataReview-reporting", "clinDataReview")
All the visualizations available in the package are interactive.
Visualization of individual profiles is available via the function scatterplotClinData
.
To facilitate the exploration of the data, the underlying data behind each visualization can be included as a table as well below the plot by setting the parameter table
to TRUE.
Please note that this functionality is not demonstrated in this document to ensure a lightweight vignette in the package.
Subject-specific report (e.g. patient profiles) can be linked to each profile (pathVar
parameter).
If the user clicks on the ‘P’ key while hovering on the plot, or click on the specific subject in the attached table, the specific patient profile is opened in a new window in the browser.
"ALT"
labParam <- subset(dataLB, PARAMCD == labParam)
dataPlot <-
with(dataPlot, tapply(ADY, VISIT, median))
visitLab <-names(visitLab) <- sub("-", "\n", names(visitLab))
# link to patient profiles
if(interactive())
$patientProfilePath <- paste0(
dataPlot"patientProfiles/subjectProfile-",
sub("/", "-", dataPlot$USUBJID), ".pdf"
)
scatterplotClinData(
data = dataPlot,
xVar = "ADY",
yVar = "LBSTRESN",
aesPointVar = list(color = "TRTP", fill = "TRTP"),
aesLineVar = list(group = "USUBJID", color = "TRTP"),
linePars = list(size = 0.5, alpha = 0.7),
hoverVars = c("USUBJID", "VISIT", "ADY", "LBSTRESN", "TRTP"),
labelVars = labelVars,
xPars = list(breaks = visitLab, labels = names(visitLab)),
title = paste("Actual value of",
getLabelParamcd(
paramcd = labParam, data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
)
),# include link to patient profiles:
pathVar = if(interactive()) "patientProfilePath",
subtitle = paste(
"Profile plot by subject",
"Points are positioned at relative day",
"Visits are positioned based on median relative day across subjects.",
sep = "\n"
),verbose = TRUE,
table = FALSE
)
# format data long -> wide format (one column per lab param)
subset(dataLB, PARAMCD %in% c("ALT", "ALB"))
dataPlot <- stats::aggregate(
dataPlot <-~ USUBJID + VISIT + VISITNUM + PARAMCD,
LBSTRESN data = dataPlot,
FUN = mean
) stats::reshape(
dataPlotWide <-data = dataPlot,
timevar = "PARAMCD", idvar = c("USUBJID", "VISIT", "VISITNUM"),
direction = "wide"
)colnames(dataPlotWide) <- sub("^LBSTRESN.", "", colnames(dataPlotWide))
# link to patient profiles
if(interactive())
$patientProfilePath <- paste0(
dataPlotWide"patientProfiles/subjectProfile-",
sub("/", "-", dataPlotWide$USUBJID), ".pdf"
)
# scatterplot per visit
scatterplotClinData(
data = dataPlotWide,
xVar = "ALT", yVar = "ALB",
xLab = getLabelParamcd(
paramcd = "ALT", data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
),yLab = getLabelParamcd(
paramcd = "ALB", data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
),aesPointVar = list(color = "USUBJID", fill = "USUBJID"),
facetPars = list(facets = ~ VISIT),
labelVars = labelVars,
pathVar = if(interactive()) "patientProfilePath",
table = FALSE
)
subset(dataLB, PARAMCD == "ALT")
dataALT <- subset(dataLB, PARAMCD == "BILI")
dataBILI <-
c("USUBJID", "VISIT")
byVar <-
merge(
dataPlot <-x = dataALT, y = dataBILI[, c(byVar, "LBSTRESN")],
by = c("USUBJID", "VISIT"),
suffixes = c(".ALT", ".BILI"),
all = TRUE
)paste0("LBSTRESN.", c("ALT", "BILI"))] <-
labelVars[ paste(
"Actual value of",
getLabelParamcd(
paramcd = c("ALT", "BILI"), data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
)
)
# link to patient profiles
if(interactive())
$patientProfilePath <- paste0(
dataPlot"patientProfiles/subjectProfile-",
sub("/", "-", dataPlot$USUBJID), ".pdf"
)
# scatterplot per visit
scatterplotClinData(
data = dataPlot,
xVar = "LBSTRESN.ALT", yVar = "LBSTRESN.BILI",
xLab = getLabelParamcd(
paramcd = "ALT", data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
),yLab = getLabelParamcd(
paramcd = "BILI", data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
),aesPointVar = list(color = "VISIT", fill = "VISIT"),
xTrans = "log10", yTrans = "log10",
hoverVars = "USUBJID",
labelVars = labelVars,
table = FALSE,
pathVar = if(interactive()) "patientProfilePath"
)
Time-intervals are displayed with the timeProfileIntervalPlot
function:
# link to patient profiles
if(interactive())
$patientProfilePath <- paste0(
dataAE"patientProfiles/subjectProfile-",
sub("/", "-", dataAE$USUBJID), ".pdf"
)timeProfileIntervalPlot(
data = dataAE,
paramVar = "USUBJID",
# time-variables
timeStartVar = "ASTDY",
timeEndVar = "ASTDY",
colorVar = "AESEV",
hoverVars = c("USUBJID", "AEDECOD", "ASTDY", "AENDY", "AESEV"),
labelVars = labelVars,
table = FALSE,
pathVar = if(interactive()) "patientProfilePath",
tableVars = c("USUBJID", "AEDECOD", "ASTDY", "AENDY", "AESEV")
)
By default, empty intervals are represented if the start/end time variables are missing. Missing start/end time can be imputed, or different symbols can be used to represent such cases:
# create variable to indicate status of start/end date
$AESTFLG <- ifelse(is.na(dataAE$ASTDY), "Missing start", "Complete")
dataAE$AEENFLG <- ifelse(is.na(dataAE$AENDY), "Missing end", "Complete")
dataAE c(
shapePalette <-`Missing start` = "triangle-left",
`Complete` = "square-open",
`Missing end` = "triangle-right"
)
# 'simple'-imputation:
# if start is missing, 'Missing' symbol displayed at end interval
$AESTDYIMP <- with(dataAE, ifelse(is.na(ASTDY), AENDY, ASTDY))
dataAE# if end is missing, 'Missing' symbol displayed at start interval
$AEENDYIMP <- with(dataAE, ifelse(is.na(AENDY), ASTDY, AENDY))
dataAE
timeProfileIntervalPlot(
data = dataAE,
paramVar = "USUBJID",
# time-variables
timeStartVar = "AESTDYIMP", timeStartLab = "Start day",
timeEndVar = "AEENDYIMP", timeEndLab = "End day",
# shape variables
timeStartShapeVar = "AESTFLG",
timeStartShapeLab = "Status of start date",
timeEndShapeVar = "AEENFLG",
timeEndShapeLab = "Status of end date",
shapePalette = shapePalette,
hoverVars = c("USUBJID", "AEDECOD", "AESEV", "ASTDY", "AESTFLG", "AENDY", "AEENFLG"),
labelVars = labelVars,
table = FALSE,
tableVars = c("USUBJID", "AEDECOD", "AESEV", "ASTDY", "AESTFLG", "AENDY", "AEENFLG"),
pathVar = if(interactive()) "patientProfilePath"
)
Summary statistics can also be visualized with the package, via different types of visualizations: sunburst, treemap and barplot.
These functions take as input a table of summary statistics, especially counts. Such table can e.g. computed with the inTextSummaryTable
R package (see corresponding package vignette for more information).
Subject-specific report (e.g. patient profiles) can be linked to each profile (pathVar
parameter). If the user clicks on the ‘P’ key while hovering on the plot, a zip file containing the reports for all corresponding patients is downloaded. If the attached table is display, each row can be extended to display the links of the respective patient profile reports.
In this example, counts of adverse events are extracted for each Primary System Organ Class and Dictionary-Derived Term.
Besides the counts of the number of subjects, the paths to the patient profile report for each subgroup are extracted and combined.
# total counts: Safety Analysis Set (patients with start date for the first treatment)
subset(dataDM, RFSTDTC != "")
dataTotal <-
## patient profiles report
if(interactive()){
# add path in data
$patientProfilePath <- paste0(
dataAE"patientProfiles/subjectProfile-",
sub("/", "-", dataAE$USUBJID), ".pdf"
)
# add link in data (for attached table)
$patientProfileLink <- with(dataAE,
dataAEpaste0(
'<a href="', patientProfilePath,
'" target="_blank">', USUBJID, '</a>'
)
)
# Specify extra summarizations besides the standard stats
# When the data is summarized,
# the patient profile path are summarized
# as well across patients
# (the paths should be collapsed with: ', ')
list(
statsExtraPP <-statPatientProfilePath = function(data)
toString(sort(unique(data$patientProfilePath))),
statPatientProfileLink = function(data)
toString(sort(unique(data$patientProfileLink)))
)
}
# get counts (records, subjects, % subjects) + stats with subjects profiles path
c(
statsPP <-::getStats(type = "count"),
inTextSummaryTableif(interactive())
list(
patientProfilePath = quote(statPatientProfilePath),
patientProfileLink = quote(statPatientProfileLink)
)
)
$AESEV <- factor(
dataAE$AESEV,
dataAElevels = c("MILD", "MODERATE", "SEVERE")
)$AESEVN <- as.numeric(dataAE$AESEV)
dataAE
# compute adverse event table
inTextSummaryTable::computeSummaryStatisticsTable(
tableAE <-
data = dataAE,
rowVar = c("AESOC", "AEDECOD"),
dataTotal = dataTotal,
labelVars = labelVars,
# The total across the variable used for the nodes
# should be specified
rowVarTotalInclude = c("AESOC", "AEDECOD"),
rowOrder = "total",
# statistics of interest
# include columns with patients
stats = statsPP,
# add extra 'statistic': concatenate subject IDs
statsExtra = if(interactive()) statsExtraPP
)::kable(head(tableAE),
knitrcaption = paste("Extract of the Adverse Event summary table",
"used for the sunburst and barplot visualization"
) )
AESOC | AEDECOD | isTotal | statN | statm | statPercTotalN | statPercN | n | % | m |
---|---|---|---|---|---|---|---|---|---|
CARDIAC DISORDERS | MYOCARDIAL INFARCTION | FALSE | 1 | 1 | 7 | 14.28571 | 1 | 14.3 | 1 |
GASTROINTESTINAL DISORDERS | DYSPEPSIA | FALSE | 1 | 1 | 7 | 14.28571 | 1 | 14.3 | 1 |
GASTROINTESTINAL DISORDERS | NAUSEA | FALSE | 2 | 7 | 7 | 28.57143 | 2 | 28.6 | 7 |
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE DERMATITIS | FALSE | 1 | 2 | 7 | 14.28571 | 1 | 14.3 | 2 |
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE ERYTHEMA | FALSE | 3 | 3 | 7 | 42.85714 | 3 | 42.9 | 3 |
GENERAL DISORDERS AND ADMINISTRATION SITE CONDITIONS | APPLICATION SITE IRRITATION | FALSE | 2 | 4 | 7 | 28.57143 | 2 | 28.6 | 4 |
The sunburstClinData
function visualizes the counts of hierarchical data in nested circles.
The different groups are visualized from the biggest class (root node) in the center of the visualization to the smallest sub-groups (leaves) on the outside of the circles.
The size of the different segments is relative the respective counts.
tableAE
dataSunburst <-
$m <- as.numeric(dataSunburst$m)
dataSunburst
sunburstClinData(
data = dataSunburst,
vars = c("AESOC", "AEDECOD"),
valueVar = "m",
valueLab = "Number of adverse events",
pathVar = if(interactive()) "patientProfileLink",
pathLab = clinUtils::getLabelVar(var = "USUBJID", labelVars = labelVars),
table = FALSE,
labelVars = labelVars
)
A treemap visualizes the counts of the hierarchical data in nested rectangles. The area of each rectangle is proportional to the counts of the respective group.
Note, that a treemap can also be colored accordingly to a meaningful variable. For instance, if we show adverse events, we might color the plot by severity. This can be achieved with the colorVar parameter.
tableAE
dataTreemap <-
$m <- as.numeric(dataTreemap$m)
dataTreemap
treemapClinData(
data = dataTreemap,
vars = c("AESOC", "AEDECOD"),
valueVar = "m",
valueLab = "Number of adverse events",
pathVar = if(interactive()) "patientProfileLink",
pathLab = clinUtils::getLabelVar(var = "USUBJID", labelVars = labelVars),
table = FALSE,
labelVars = labelVars
)
A barplot visualizes the counts for one single variable in a specific order.
subset(tableAE, AEDECOD != "Total")
dataPlot <-
$n <- as.numeric(dataPlot$n)
dataPlot
# create plot
barplotClinData(
data = dataPlot,
xVar = "AEDECOD",
yVar = "n",
yLab = "Number of patients with adverse events",
textVar = "n",
labelVars = labelVars,
pathVar = if(interactive()) "patientProfileLink",
pathLab = clinUtils::getLabelVar(var = "USUBJID", labelVars = labelVars),
table = FALSE
)
subset(dataADaMCDISCP01$ADVS,
dataVSDIABP <-== "DIABP" & ANL01FL == "Y" &
PARAMCD AVISIT %in% c("Baseline", "Week 2", "Week 4", "Week 6", "Week 8")
)
# add link to patient profiles report
# add path in data
if(interactive()){
$patientProfilePath <- paste0(
dataVSDIABP"patientProfiles/subjectProfile-",
sub("/", "-", dataVSDIABP$USUBJID), ".pdf"
)
# add link in data (for attached table)
$patientProfileLink <- with(dataVSDIABP,
dataVSDIABPpaste0(
'<a href="', patientProfilePath,
'" target="_blank">', USUBJID, '</a>'
)
)
}
# Specify extra summarizations besides the standard stats
# When the data is summarized,
# the patient profile path are summarized
# as well across patients
# (the paths should be collapsed with: ', ')
if(interactive())
list(
statsExtraPP <-statPatientProfilePath = function(data)
toString(sort(unique(data$patientProfilePath))),
statPatientProfileLink = function(data)
toString(sort(unique(data$patientProfileLink)))
)
# get default counts + stats with subjects profiles path
c(
statsPP <-::getStats(x = dataVSDIABP$AVAL, type = "summary"),
inTextSummaryTableif(interactive())
list(
patientProfilePath = quote(statPatientProfilePath),
patientProfileLink = quote(statPatientProfileLink)
)
)
# compute summary table of actual value
inTextSummaryTable::computeSummaryStatisticsTable(
summaryTableCont <-
data = dataVSDIABP,
rowVar = c("AVISIT", "ATPT"),
var = "AVAL",
labelVars = labelVars,
# statistics of interest
# for DT output, include columns with patients
stats = statsPP,
# add extra 'statistic': concatenate subject IDs
statsExtra = if(interactive()) statsExtraPP
)::kable(head(summaryTableCont, 1)) knitr
AVISIT | ATPT | isTotal | statN | statm | statMean | statSD | statSE | statMedian | statMin | statMax | statPercTotalN | statPercN | n | Mean | SD | SE | Median | Min | Max | % | m |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Baseline | AFTER LYING DOWN FOR 5 MINUTES | FALSE | 7 | 7 | 70.71429 | 9.214378 | 3.482708 | 70 | 56 | 84 | 7 | 100 | 7 | 70.7 | 9.2 | 3.48 | 70.0 | 56 | 84 | 100 | 7 |
subset(summaryTableCont, !isTotal)
dataPlot <-
errorbarClinData(
data = dataPlot,
xVar = "AVISIT",
colorVar = "ATPT",
# use non-rounded statistics for the plot
yVar = "statMean",
yErrorVar = "statSE",
# display rounded stats in the hover + n: number of subjects
hoverVars = c("AVISIT", "ATPT", "n", "Mean", "SE"),
yLab = "Mean", yErrorLab = "Standard Error",
title = "Diastolic Blood Pressure summary profile by actual visit and and analysis timepoint",
# include lines connecting the error bars
mode = "markers+lines",
table = FALSE, labelVars = labelVars,
pathVar = if(interactive()) "patientProfileLink",
pathLab = clinUtils::getLabelVar(var = "USUBJID", labelVars = labelVars)
)
A boxplot visualizes the distribution of a continuous variable of interest versus specific categorical variables.
This visualization doesn’t rely on pre-computed statistics, so the continuous variable of interest is directly passed to the functionality.
subset(dataADaMCDISCP01$ADVS,
dataPlot <-== "DIABP" & ANL01FL == "Y" &
PARAMCD AVISIT %in% c("Baseline", "Week 2", "Week 4", "Week 6", "Week 8")
)
# link to patient profiles
if(interactive())
$patientProfilePath <- paste0(
dataPlot"patientProfiles/subjectProfile-",
sub("/", "-", dataPlot$USUBJID), ".pdf"
)
boxplotClinData(
data = dataPlot,
xVar = "AVISIT",
yVar = "AVAL",
colorVar = "TRTA",
facetVar = "ATPT",
title = "Diastolic Blood Pressure distribution by actual visit and analysis timepoint",
yLab = "Actual value of the Diastolic Blood Pressure parameter (mmHg)",
labelVars = labelVars,
pathVar = if(interactive()) "patientProfilePath",
pathLab = clinUtils::getLabelVar(var = "USUBJID", labelVars = labelVars),
table = FALSE
)
To include multiple clinical data visualizations (with or without attached table) in a loop (in the same Rmarkdown chunk), the list of visualizations should be passed to the knitPrintListObjects
function of the clinUtils
package.
# consider only restricted set of lab parameters
subset(dataLB, PARAMCD %in% c("SODIUM", "K"))
dataPlot <-
# link to patient profiles
if(interactive())
$patientProfilePath <- paste0(
dataPlot"patientProfiles/subjectProfile-",
sub("/", "-", dataPlot$USUBJID), ".pdf"
)
# 1) create plot+table for each laboratory parameter:
library(plyr) # for ddply
dlply(dataPlot, "PARAMCD", function(dataLBParam){
plotsLab <-
unique(dataLBParam$PARAMCD)
paramcd <-
scatterplotClinData(
data = dataLBParam,
xVar = "ADY",
yVar = "LBSTRESN",
aesPointVar = list(color = "TRTP", fill = "TRTP"),
aesLineVar = list(group = "USUBJID", color = "TRTP"),
labelVars = labelVars,
title = paste("Actual value of",
getLabelParamcd(
paramcd = paramcd, data = dataLBParam, paramcdVar = "PARAMCD", paramVar = "PARAM"
)
),# include link to patient profiles:
pathVar = if(interactive()) "patientProfilePath",
table = FALSE,
# important: each plot should have an unique ID!
# for unique relationship of interactivity between plot <-> table
id = paste("labProfileLoop", paramcd, sep = "-")
)
})
# include this output in the report:
getLabelParamcd(
listLabels <-paramcd = names(plotsLab), data = dataLB, paramcdVar = "PARAMCD", paramVar = "PARAM"
)::knitPrintListObjects(
clinUtilsxList = plotsLab,
titles = listLabels, titleLevel = 4
)
A watermark can be included in any of the visualization of the package, from a specified file, via the watermark
parameter.
In this example, an exploratory watermark is added to the adverse events barplot.
# create a file with a 'EXPLORATORY' watermark
tempfile(pattern = "watermark", fileext = ".png")
file <-getWatermark(file = file)
# create plot
barplotClinData(
data = subset(tableAE, AEDECOD != "Total"),
xVar = "AEDECOD",
yVar = "n",
yLab = "Number of patients with adverse events",
textVar = "n",
labelVars = labelVars,
# include the watermark
watermark = file,
table = FALSE
)
Palette for the colors and shapes associated with specific variables can be set for all clinical data visualizations at once by setting the clinDataReview.colors
and clinDataReview.shapes
options at the start of the R session.
Please see the clinUtils
package for the default colors and shapes.
# display default palettes
getOption("clinDataReview.colors")
colorsDefault <-str(colorsDefault)
## function (n, alpha = 1, begin = 0, end = 1, direction = 1, option = "D")
getOption("clinDataReview.shapes")
shapesDefault <-str(shapesDefault)
## int [1:24] 21 22 23 24 25 0 1 2 3 4 ...
timeProfileIntervalPlot(
data = dataAE,
paramVar = "USUBJID",
# time-variables
timeStartVar = "ASTDY",
timeEndVar = "AENDY",
colorVar = "AESEV",
timeStartShapeVar = "AESTFLG",
timeEndShapeVar = "AEENFLG",
labelVars = labelVars
)
The palettes can be set for all visualizations, e.g. at the start of the R session, with:
# change palettes for the entire R session
options(clinDataReview.colors = c("gold", "pink", "cyan"))
options(clinDataReview.shapes = clinShapes)
In case the palette contains less elements than available in the data, these are replicated.
timeProfileIntervalPlot(
data = dataAE,
paramVar = "USUBJID",
# time-variables
timeStartVar = "ASTDY",
timeEndVar = "AENDY",
colorVar = "AESEV",
timeStartShapeVar = "AESTFLG",
timeEndShapeVar = "AEENFLG",
labelVars = labelVars
)
Palettes are reset to the default patient profiles palettes at the start of a new R session, or by setting:
# change palettes for the entire R session
options(clinDataReview.colors = colorsDefault)
options(clinDataReview.shapes = shapesDefault)
R version 4.4.0 (2024-04-24) Platform: x86_64-pc-linux-gnu Running under: Ubuntu 22.04.4 LTS
Matrix products: default BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0
locale: [1] C
time zone: Etc/UTC tzcode source: system (glibc)
attached base packages: [1] stats graphics grDevices utils datasets methods base
other attached packages: [1] plyr_1.8.9 plotly_4.10.4 ggplot2_3.5.1 clinUtils_0.2.0 clinDataReview_1.6.1 knitr_1.47
loaded via a namespace (and not attached): [1] gtable_0.3.5 xfun_0.44 bslib_0.7.0 htmlwidgets_1.6.4 ggrepel_0.9.5 vctrs_0.6.5
[7] tools_4.4.0 crosstalk_1.2.1 generics_0.1.3 curl_5.2.1 parallel_4.4.0 tibble_3.2.1
[13] fansi_1.0.6 pkgconfig_2.0.3 data.table_1.15.4 uuid_1.2-0 lifecycle_1.0.4 flextable_0.9.6
[19] compiler_4.4.0 farver_2.1.2 stringr_1.5.1 textshaping_0.4.0 munsell_0.5.1 httpuv_1.6.15
[25] fontquiver_0.2.1 fontLiberation_0.1.0 htmltools_0.5.8.1 sass_0.4.9 yaml_2.3.8 lazyeval_0.2.2
[31] crayon_1.5.2 pillar_1.9.0 later_1.3.2 jquerylib_0.1.4 tidyr_1.3.1 gfonts_0.2.0
[37] openssl_2.2.0 DT_0.33 cachem_1.1.0 jsonvalidate_1.3.2 mime_0.12 fontBitstreamVera_0.1.1 [43] zip_2.3.1 tidyselect_1.2.1 digest_0.6.35 stringi_1.8.4 reshape2_1.4.4 dplyr_1.1.4
[49] purrr_1.0.2 bookdown_0.39 labeling_0.4.3 forcats_1.0.0 cowplot_1.1.3 fastmap_1.2.0
[55] grid_4.4.0 colorspace_2.1-0 cli_3.6.2 magrittr_2.0.3 base64enc_0.1-3 crul_1.4.2
[61] utf8_1.2.4 withr_3.0.0 gdtools_0.3.7 scales_1.3.0 promises_1.3.0 officer_0.6.6
[67] rmarkdown_2.27 httr_1.4.7 inTextSummaryTable_3.3.2 ragg_1.3.2 askpass_1.2.0 hms_1.1.3
[73] shiny_1.8.1.1 evaluate_0.24.0 haven_2.5.4 viridisLite_0.4.2 rlang_1.1.4 Rcpp_1.0.12
[79] httpcode_0.3.0 xtable_1.8-4 glue_1.7.0 xml2_1.3.6 jsonlite_1.8.8 R6_2.5.1
[85] systemfonts_1.1.0