Introduction
This vignette shows how to link simulation outputs to observed data
and how to apply weights and transformations in the parameter
identification workflow.
Mappings are created with the PIOutputMapping
class, which
connects a simulation quantity to one or more observed datasets. Each
mapping can be customized by assigning dataset- or point-specific
weights and by applying transformations such as offsets or scaling
factors. These adjustments help align observed and simulated values and
control how much influence each dataset has on the optimization.
Creating a mapping
Mappings are created with the PIOutputMapping
class,
which connects a simulation quantity (for example a plasma
concentration) to one or more observed datasets.
The quantity itself is retrieved from a loaded simulation via
getQuantity
.
In the following we use the Aciclovir example simulation:
library(ospsuite.parameteridentification)
#> Loading required package: ospsuite
# Load example simulation
simulation <- loadSimulation(
system.file("extdata", "Aciclovir.pkml", package = "ospsuite")
)
# Retrieve a quantity by path from the simulation
quantity <- getQuantity(
path = "Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral Venous Blood)",
container = simulation
)
# Create a new mapping for this quantity
outputMapping <- PIOutputMapping$new(quantity = quantity)
The path
corresponds to the quantity’s location in the
simulation structure. At this point, outputMapping
represents the link between the chosen simulation output and the
observed data that will be added next.
For a full description of all fields and methods, see the PIOutputMapping
documentation.
Adding observed data
Observed datasets can be imported from Excel and attached to the
mapping.
Import settings (such as the naming pattern) are defined via an importer
configuration. For more background on working with observed data and
importing from Excel, see the Observed
data article in the {ospsuite} documentation.
filePath <- system.file("extdata", "Aciclovir_Profiles.xlsx",
package = "ospsuite.parameteridentification")
importConfig <- createImporterConfigurationForFile(filePath)
importConfig$namingPattern <- "{Source}.{Sheet}.{Dose}"
obsData <- loadDataSetsFromExcel(
xlsFilePath = filePath,
importerConfigurationOrPath = importConfig,
importAllSheets = TRUE
)
# Attach one or more datasets to the mapping (labels come from the naming pattern)
outputMapping$addObservedDataSets(
data = obsData$`Aciclovir_Profiles.Vergin 1995.Iv.250 mg`
)
Scaling
Scaling specifies whether residuals are calculated on the raw
(linear) scale or on a logarithmic scale. The default is linear
("lin"
), but logarithmic scaling ("log"
) can
be selected to emphasize relative differences when data span several
orders of magnitude.
outputMapping$scaling <- "log"
Setting weights
Weights control the relative influence of observed data on the
optimization.
They can be supplied directly when datasets are added to a mapping, or
they can be set or updated later with setDataWeights()
. In
both cases, weights are passed as a named list where the names must
match the dataset labels.
Adding weights directly
Weights can be attached at the time of adding datasets. This allows entire datasets to be up- or down-weighted in one step.
## Not run: example only
exampleMapping$addObservedDataSets(
data = list("Study A" = dsA, "Study B" = dsB),
weights = list(
"Study A" = 1.0,
"Study B" = 0.5
)
)
## End(Not run)
The code above is illustrative and not executed here.
Example with one dataset
In the Aciclovir example only one dataset is mapped. A vector of weights is used, with one weight per data point:
# number of observed points
nPoints <- length(obsData$`Aciclovir_Profiles.Vergin 1995.Iv.250 mg`$yValues)
# give less weight to the first 3 points, full weight to the rest
outputMapping$setDataWeights(
list(
"Aciclovir_Profiles.Vergin 1995.Iv.250 mg" =
c(rep(0.3, 3), rep(1.0, nPoints - 3))
)
)
Printing the PIOutputMapping
object provides a concise
summary showing the output path and the labels of attached datasets, the
labels where weights were applied, and the selected scaling:
print(outputMapping)
#> <PIOutputMapping>
#> • Output path: Organism|PeripheralVenousBlood|Aciclovir|Plasma (Peripheral
#> Venous Blood)
#> • Observed data labels: Aciclovir_Profiles.Vergin 1995.Iv.250 mg
#> • Data weight labels: Aciclovir_Profiles.Vergin 1995.Iv.250 mg
#> • Scaling: log
The assigned weights can also be accessed directly via the dataWeights field, which is useful for validation:
outputMapping$dataWeights
#> $`Aciclovir_Profiles.Vergin 1995.Iv.250 mg`
#> [1] 0.3 0.3 0.3 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
Other options
Weights are always applied at the level of individual data points.
Using a single scalar for a dataset (for example 0.5
) is
just a shorthand, which internally assigns that same weight to every
point in the dataset.
The examples below illustrate the different ways weights can be specified.
Dataset-level weights
A scalar weight is broadcast to all points of a dataset:
## Not run: example only
outputMapping$setDataWeights(
list(
"StudyA" = 1.0, # full influence
"StudyB" = 0.5 # down-weight entire dataset
)
)
## End(Not run)
Transformations
Observed data can be adjusted through transformations to improve comparability with the corresponding simulation output. This is done per dataset or globally for all datasets in the mapping. Transformations allow offsets and scaling factors to be applied to x- and y-values, for example to account for systematic shifts between experiments or to align starting points across datasets.
outputMapping$setDataTransformations(
labels = "Aciclovir_Profiles.Vergin 1995.Iv.250 mg",
xOffsets = 0.5, yOffsets = 0,
xFactors = 1, yFactors = 1
)
outputMapping$dataTransformations
#> $xOffsets
#>
#> 0.0
#> Aciclovir_Profiles.Vergin 1995.Iv.250 mg
#> 0.5
#>
#> $yOffsets
#>
#> 0
#> Aciclovir_Profiles.Vergin 1995.Iv.250 mg
#> 0
#>
#> $xFactors
#>
#> 1
#> Aciclovir_Profiles.Vergin 1995.Iv.250 mg
#> 1
#>
#> $yFactors
#>
#> 1
#> Aciclovir_Profiles.Vergin 1995.Iv.250 mg
#> 1