Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement plotObservedVsSimulated() #945

Merged
merged 64 commits into from
Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
8f89e27
initialize `plotObservedVsSimulated()`
IndrajeetPatil May 13, 2022
4ab18b8
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 14, 2022
76f02aa
naming
IndrajeetPatil May 14, 2022
4f5bb76
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 16, 2022
f3d3142
preliminary test
IndrajeetPatil May 16, 2022
06d05c2
show message only once across groups
IndrajeetPatil May 17, 2022
3c6cef8
interpolation in edge case
IndrajeetPatil May 17, 2022
2e03d33
DRY in setting same properties across plots
IndrajeetPatil May 18, 2022
4242ede
also test custom plot
IndrajeetPatil May 18, 2022
c0242c1
for now, only one line
IndrajeetPatil May 18, 2022
28b7c37
also test smoother
IndrajeetPatil May 18, 2022
879312c
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 18, 2022
c86dc5a
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 20, 2022
3a163fa
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 23, 2022
6eb6f25
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 25, 2022
6dd946c
update snapshots
IndrajeetPatil May 27, 2022
2a05157
fix tests
IndrajeetPatil May 27, 2022
d6090e1
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 30, 2022
88b628f
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil May 30, 2022
d7c31c8
fix tests
IndrajeetPatil May 30, 2022
7b0e4a1
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jun 8, 2022
4859ea0
Use `foldDistance` arg from tlf
IndrajeetPatil Jun 8, 2022
ee656b8
Need the latest version
IndrajeetPatil Jun 8, 2022
c63ae79
fix documentation issue
IndrajeetPatil Jun 8, 2022
cf2c46f
introduce tolerance
IndrajeetPatil Jun 8, 2022
24f42fe
Use unit-dependent thresholds
IndrajeetPatil Jun 10, 2022
f16e120
linear smoother by default
IndrajeetPatil Jun 13, 2022
1a35722
add example to aggregated data function
IndrajeetPatil Jun 13, 2022
cdbd7db
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jun 14, 2022
fa2d86c
Update DefaultPlotConfiguration.Rd
IndrajeetPatil Jun 14, 2022
483192f
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jun 14, 2022
6cca0a2
correct fold distance
IndrajeetPatil Jun 15, 2022
36fa79e
deal with linear scale
IndrajeetPatil Jun 15, 2022
b346daa
update snapshot
IndrajeetPatil Jun 15, 2022
344933a
set smoother to NULL
IndrajeetPatil Jun 15, 2022
5b96031
use long dash
IndrajeetPatil Jun 15, 2022
da242f8
use expand
IndrajeetPatil Jun 15, 2022
11bc947
add expand option
IndrajeetPatil Jun 15, 2022
8fca839
better
IndrajeetPatil Jun 15, 2022
f64b8f5
use messages instead
IndrajeetPatil Jun 16, 2022
d73921e
minor
IndrajeetPatil Jun 16, 2022
b00f122
catch with tlf
IndrajeetPatil Jun 22, 2022
5f02f9e
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jun 23, 2022
c935422
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jun 28, 2022
a5bd70c
update snapshots
IndrajeetPatil Jul 4, 2022
86ae2bc
pass args to legend config
IndrajeetPatil Jul 4, 2022
a08b28a
update other plots as well
IndrajeetPatil Jul 4, 2022
95b26c0
legend key text should be left-aligned
IndrajeetPatil Jul 4, 2022
f1c3670
address code review comments
IndrajeetPatil Jul 4, 2022
980a7a7
test adding more fold distances work
IndrajeetPatil Jul 4, 2022
6320c5e
Return `NULL` when `DataCombined` is empty
IndrajeetPatil Jul 4, 2022
4f533d2
Handle edge case in paired plot
IndrajeetPatil Jul 4, 2022
e9f66b1
remove `smoother` arg
IndrajeetPatil Jul 5, 2022
c44fc5a
update snapshots
IndrajeetPatil Jul 5, 2022
7cc7c71
fix legend alignment issue
IndrajeetPatil Jul 5, 2022
b7c1fd6
legendCaption -> legendKeys
IndrajeetPatil Jul 5, 2022
8ebcf11
catch up with tlf
IndrajeetPatil Jul 6, 2022
5f083a8
also include residual values
IndrajeetPatil Jul 6, 2022
2f692f1
add error bars to the plot
IndrajeetPatil Jul 15, 2022
ad53707
comments
IndrajeetPatil Jul 15, 2022
12c77ad
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jul 15, 2022
3fc9763
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jul 18, 2022
11aa0da
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jul 18, 2022
088ed2e
Merge branch 'develop' into 924_plot_observed_vs_simulated
IndrajeetPatil Jul 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Imports:
readr,
stringr,
tidyr,
tlf (>= 1.3.0)
tlf (>= 1.4.0)
Suggests:
knitr,
rmarkdown,
Expand Down Expand Up @@ -70,6 +70,7 @@ Collate:
'pk-parameter.R'
'pk-sim.R'
'plot-individual-time-profile.R'
'plot-observed-vs-simulated.R'
'plot-population-time-profile.R'
'population-characteristics.R'
'population.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export(pkAnalysesToTibble)
export(pkParameterByName)
export(plotGrid)
export(plotIndividualTimeProfile)
export(plotObservedVsSimulated)
export(plotPopulationTimeProfile)
export(populationAsDataFrame)
export(populationToDataFrame)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# ospsuite 12.0 (development version)

* Adds new visualization functions:

- `plotObservedVsSimulated()` for observed versus predicted scatter plots.

# ospsuite 11.0.123

## New features
Expand Down
32 changes: 17 additions & 15 deletions R/default-plot-configuration.R
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@
#' @field legendPosition A character string defining the legend position.
#' Available options can be seen using `tlf::LegendPositions` list.
#' @field legendTitleSize,legendTitleColor,legendTitleFontFamily,legendTitleFontFace,legendTitleAngle,legendTitleAlign Aesthetic properties for the legend title.
#' @field legendCaptionSize,legendCaptionColor,legendCaptionFontFamily,legendCaptionFontFace,legendCaptionAngle,legendCaptionAlign Aesthetic properties for the legend caption.
#' @field xAxisTicksLabels,xAxisLabelTicksSize,xAxisLabelTicksColor,xAxisLabelTicksFontFamily,xAxisLabelTicksFontFace,xAxisLabelTicksAngle,xAxisLabelTicksAlign Aesthetic properties for the x-axis label.
#' @field yAxisTicksLabels,yAxisLabelTicksSize,yAxisLabelTicksColor,yAxisLabelTicksFontFamily,yAxisLabelTicksFontFace,yAxisLabelTicksAngle,yAxisLabelTicksAlign Aesthetic properties for the y-axis label.
#' @field legendKeysSize,legendKeysColor,legendKeysFontFamily,legendKeysFontFace,legendKeysAngle,legendKeysAlign Aesthetic properties for the legend caption.
#' @field xAxisTicksLabels,xAxisLabelTicksSize,xAxisLabelTicksColor,xAxisLabelTicksFontFamily,xAxisLabelTicksFontFace,xAxisLabelTicksAngle,xAxisLabelTicksAlign,xAxisExpand Aesthetic properties for the x-axis label.
#' @field yAxisTicksLabels,yAxisLabelTicksSize,yAxisLabelTicksColor,yAxisLabelTicksFontFamily,yAxisLabelTicksFontFace,yAxisLabelTicksAngle,yAxisLabelTicksAlign,yAxisExpand Aesthetic properties for the y-axis label.
#' @field xAxisLimits,yAxisLimits A numeric vector of axis limits for the x-and
#' y-axis, respectively.
#' @field xAxisTicks,yAxisTicks A numeric vector or a function defining where to
Expand Down Expand Up @@ -226,21 +226,21 @@ DefaultPlotConfiguration <- R6::R6Class(
legendTitleFontFamily = "",
legendTitleFontFace = tlf::FontFaces$plain,
legendTitleAngle = 0,
legendTitleAlign = tlf::Alignments$center,
legendTitleAlign = tlf::Alignments$left,

# legendCaption ------------------------------------
# legendKeys ------------------------------------

legendCaptionSize = tlf::PlotAnnotationTextSize$plotLegendCaptionSize,
legendCaptionColor = "black",
legendCaptionFontFamily = "",
legendCaptionFontFace = tlf::FontFaces$plain,
legendCaptionAngle = 0,
legendCaptionAlign = tlf::Alignments$center,
legendKeysSize = tlf::PlotAnnotationTextSize$plotLegendCaptionSize,
legendKeysColor = "black",
legendKeysFontFamily = "",
legendKeysFontFace = tlf::FontFaces$plain,
legendKeysAngle = 0,
legendKeysAlign = tlf::Alignments$left,

# XAxisConfiguration ------------------------------------

xAxisLimits = NULL,
xAxisScale = tlf::Scaling$lin,
xAxisScale = NULL,
xAxisTicks = NULL,
xAxisTicksLabels = tlf::TickLabelTransforms$identity,
xAxisLabelTicksSize = NULL,
Expand All @@ -253,7 +253,7 @@ DefaultPlotConfiguration <- R6::R6Class(
# YAxisConfiguration ------------------------------------

yAxisLimits = NULL,
yAxisScale = tlf::Scaling$lin,
yAxisScale = NULL,
yAxisTicks = NULL,
yAxisTicksLabels = tlf::TickLabelTransforms$identity,
yAxisLabelTicksSize = NULL,
Expand Down Expand Up @@ -292,12 +292,14 @@ DefaultPlotConfiguration <- R6::R6Class(
xAxisColor = "black",
xAxisSize = 0.5,
xAxisLinetype = tlf::Linetypes$solid,
xAxisExpand = FALSE,

# yAxis ------------------------------------

yAxisColor = "black",
yAxisSize = 0.5,
yAxisLinetype = tlf::Linetypes$solid,
yAxisExpand = FALSE,

# xGrid ------------------------------------

Expand All @@ -324,8 +326,8 @@ DefaultPlotConfiguration <- R6::R6Class(

# There is no `pointsFill` because it doesn't make sense to "fill" a line
# with color. There is already `pointsColor` for that.
pointsColor = NULL,
pointsShape = NULL,
pointsColor = tlf::ColorMaps$ospDefault,
pointsShape = names(tlf::Shapes),
pointsSize = 3,
pointsAlpha = 0.75,

Expand Down
3 changes: 1 addition & 2 deletions R/get-net-task.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#' @title Returns an instance of the specified `.NET` Task
#' @name .getNetTask
#' Get an instance of the specified `.NET` Task
#'
#' @param taskName The name of the task to retrieve (**without** `Get` prefix).
#'
Expand Down
16 changes: 16 additions & 0 deletions R/messages.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,26 @@ messages$unpairableDatasetsRemoved <- function() {
"Following non-grouped or unpairable datasets have been removed"
}

messages$valuesNotInterpolated <- function() {
"Predicted values couldn't be interpolated at following time points"
}

messages$printMultipleEntries <- function(header, entries) {
message(paste0(header, ":\n"), paste0(entries, collapse = "\n"))
}

messages$linearScaleWithFoldDistance <- function() {
"Linear scale is inappropriate when `foldDistance` argument is specified."
}

messages$errorLoadingUnitsForDimension <- function(dimensions) {
messages$printMultipleEntries("Could not load units for the following dimensions", dimensions)
}

messages$plottingWithEmptyDataCombined <- function() {
"No plot can be created because the entered `DataCombined` object does not contain any datasets."
}

messages$plottingWithNoPairedDatasets <- function() {
"No plot can be created because the entered `DataCombined` object does not contain any observed-simulated datasets that can be paired."
}
5 changes: 5 additions & 0 deletions R/plot-individual-time-profile.R
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ plotIndividualTimeProfile <- function(dataCombined,
validateIsOfType(dataCombined, "DataCombined")
validateIsSameLength(objectCount(dataCombined), 1L) # only single instance is allowed

if (is.null(dataCombined$groupMap)) {
warning(messages$plottingWithEmptyDataCombined())
return(NULL)
}

# data frames -----------------------------

combinedData <- dataCombined$toDataFrame()
Expand Down
142 changes: 142 additions & 0 deletions R/plot-observed-vs-simulated.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#' Observed versus predicted/simulated scatter plot
#'
#' @inheritParams plotIndividualTimeProfile
#' @param foldDistance A vector for plotting lines at required fold distances
#' The vector can include only fold distance values different from `1`. Even
#' if it is not specified, it will **always** be included.
#'
#' @import tlf
#'
#' @family plotting
#'
#' @examples
#'
#' # TODO: add example
#'
#' @export
plotObservedVsSimulated <- function(dataCombined,
defaultPlotConfiguration = NULL,
foldDistance = 2) {
# validation -----------------------------

defaultPlotConfiguration <- defaultPlotConfiguration %||% DefaultPlotConfiguration$new()
validateIsOfType(dataCombined, "DataCombined")
validateIsSameLength(objectCount(dataCombined), 1L) # only single instance is allowed
validateIsOfType(defaultPlotConfiguration, "DefaultPlotConfiguration", nullAllowed = FALSE)

if (is.null(dataCombined$groupMap)) {
warning(messages$plottingWithEmptyDataCombined())
return(NULL)
}

# data frames -----------------------------

combinedData <- dataCombined$toDataFrame()

# Remove the observed and simulated datasets which can't be paired.
combinedData <- .removeUnpairableDatasets(combinedData)

# Return early if there are no pair-able datasets present
if (nrow(combinedData) == 0L) {
warning(messages$plottingWithNoPairedDatasets())
return(NULL)
}

# Getting all units on the same scale
combinedData <- .unitConverter(combinedData, defaultPlotConfiguration$xUnit, defaultPlotConfiguration$yUnit)

# `ObsVsPredPlotConfiguration` object -----------------------------

# Create an instance of `ObsVsPredPlotConfiguration` class by doing a
# one-to-one mapping of internal plot configuration object's public fields
obsVsPredPlotConfiguration <- .convertGeneralToSpecificPlotConfiguration(
data = combinedData,
specificPlotConfiguration = tlf::ObsVsPredPlotConfiguration$new(),
generalPlotConfiguration = defaultPlotConfiguration
)

# Linear scaling is stored as identity scaling in `{tlf}`
is_any_scale_linear <- (
obsVsPredPlotConfiguration$xAxis$scale == "identity" ||
obsVsPredPlotConfiguration$yAxis$scale == "identity"
)

# The argument `foldDistance` should only include fold values different from
# the default value, which must always be present.
#
# The default value depends on the scale:
#
# - For linear scale: `1`
# - For logarithmic scale: `0`
defaultFoldDistance <- ifelse(is_any_scale_linear, 0, 1)

if (!any(dplyr::near(defaultFoldDistance, foldDistance))) {
foldDistance <- c(defaultFoldDistance, foldDistance)
}

if (is_any_scale_linear && !is.null(foldDistance)) {
warning(messages$linearScaleWithFoldDistance())
foldDistance <- 0
}

# paired data frame -----------------------------

# Create observed versus simulated paired data using interpolation for each
# grouping level and combine the resulting data frames in a row-wise manner.
#
# Both of these routines will be carried out by `dplyr::group_modify()`.
pairedData <- combinedData %>%
dplyr::group_by(group) %>%
dplyr::group_modify(.f = ~ .createObsVsPredData(.x, scaling = obsVsPredPlotConfiguration$yAxis$scale)) %>%
dplyr::ungroup()

# Add min and max values for horizontal error bars
pairedData <- dplyr::mutate(
pairedData,
obsValueLower = obsValue - obsErrorValue,
obsValueHigher = obsValue + obsErrorValue
)

# Time points at which predicted values can't be interpolated, and need to be
# extrapolated.
#
# This will happen in rare case scenarios where simulated data is sampled at a
# lower frequency than observed data.
predValueMissingIndices <- which(is.na(pairedData$predValue))

# Warn the user about failure to interpolate.
if (length(predValueMissingIndices) > 0) {
warning(
messages$printMultipleEntries(
header = messages$valuesNotInterpolated(),
entries = pairedData$obsTime[predValueMissingIndices]
)
)
}

# axes labels -----------------------------

# The type of plot can be guessed from the specific `PlotConfiguration` object
# used, since each plot has a unique corresponding class. The labels can then
# be prepared accordingly.
axesLabels <- .createAxesLabels(combinedData, obsVsPredPlotConfiguration)
obsVsPredPlotConfiguration$labels$xlabel$text <- obsVsPredPlotConfiguration$labels$xlabel$text %||% axesLabels$xLabel
obsVsPredPlotConfiguration$labels$ylabel$text <- obsVsPredPlotConfiguration$labels$ylabel$text %||% axesLabels$yLabel

# plot -----------------------------

tlf::plotObsVsPred(
data = as.data.frame(pairedData),
dataMapping = tlf::ObsVsPredDataMapping$new(
x = "obsValue",
y = "predValue",
group = "group",
xmin = "obsValueLower",
xmax = "obsValueHigher",
lines = NULL
),
foldDistance = foldDistance,
smoother = NULL,
plotConfiguration = obsVsPredPlotConfiguration
)
}
2 changes: 0 additions & 2 deletions R/plot-population-time-profile.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
plotPopulationTimeProfile <- function(dataCombined,
defaultPlotConfiguration = NULL,
quantiles = c(0.05, 0.5, 0.95)) {
# validation -----------------------------

validateIsNumeric(quantiles, nullAllowed = FALSE)
validateIsOfLength(quantiles, 3L)

Expand Down
Loading