knitr::opts_chunk$set( collapse = TRUE, comment = "#>", dpi = 300, fig.align = "center", out.width = "100%", fig.height = 6, fig.width = 8, fig.showtext = TRUE )
require(tlf) # Place default legend position above the plot for prettier time profile plots setDefaultLegendPosition(LegendPositions$outsideTop)
The following vignette aims at documenting and illustrating workflows for producing Time Profile plots using the tlf
-Library.
Time profile plots aims at comparing observed and simulated data along time. In such plots, observed data are usually plotted as scatter points with errorbars showing population range or confidence intervals; while simulated data are usually plotted using lines with shaded ribbons showing population range or confidence intervals.
plotTimeProfile
Besides, the usual tlf
input arguments commonly used by the plot functions (data
, metaData
, dataMapping
, plotConfiguration
and plotObject
), the function plotTimeProfile
also includes the following optional input arguments:
observedData
: A data.frame to use for plot. Unlike data
, meant for simulated data, plotted as lines and ribbons; observedData
is plotted as scatter points and errorbars.observedDataMapping
: An ObservedDataMapping
object mapping x
, y
, ymin
, ymax
, and aesthetic groups to their variable names of observedData
.dataMapping
argumentFor time profile simulated data, TimeProfileDataMapping
objects are required through the dataMapping
argument of the function plotTimeProfile
.
As highlighted below, the class is initialized using the method $new
.
The optional arguments ymin
and ymax
define the shaded area of population range or confidence interval.
The argument y
is optional, only if ymin
and ymax
are provided.
It can be noted that the argument group
can be used to group the data by these aesthetics.
TimeProfileDataMapping$new( x, y = NULL, ymin = NULL, ymax = NULL, group = NULL )
observedDataMapping
argumentFor time profile observed data, ObservedDataMapping
objects are required through the observedDataMapping
argument of the function plotTimeProfile
.
As highlighted below, the class is initialized using the method $new
.
The optional arguments ymin
and ymax
define the error bars of population range or confidence interval.
The optional argument error
can be used instead to calculate the errorbars from the variable y
.
The argument mdv
, meant for missing dependent variable, is a flag inspired from Nonmem for excluding data from the final time profile plot.
It can be noted that the argument group
can be used to group the data by these aesthetics.
ObservedDataMapping$new( x, y, error = NULL, ymin = NULL, ymax = NULL, group = NULL, mdv = NULL )
Some time profiles can also show another quantity in parallel and whose unit is different.
A right axis is usually added for such plots to describe the values of that quantity.
The TimeProfileDataMapping
and ObservedData
objects do not support yet a y2
argument for displaying data on the right axis.
For the examples presented in the sequel, two simulated and two observed datasets are created below:
# Simulation time simTime <- seq(0, 24, 0.1) # metaData will be used during the smart mapping # to label the axes as "dimension [unit]" metaData <- list( time = list( dimension = "Time", unit = "h" ), concentration = list( dimension = "Concentration", unit = "mg/l" ) ) # Spread metaData to min/max metaData$maxConcentration <- metaData$concentration metaData$minConcentration <- metaData$concentration # Simulated data 1: exponential decay simData1 <- data.frame( time = simTime, concentration = 15 * exp(-0.1 * simTime), minConcentration = 12 * exp(-0.12 * simTime), maxConcentration = 18 * exp(-0.08 * simTime), caption = "Simulated Data 1" ) # Simulated data 2: first order absorption with exponential decay simData2 <- data.frame( time = simTime, concentration = 10 * exp(-0.1 * simTime) * (1 - exp(-simTime)), minConcentration = 8 * exp(-0.12 * simTime) * (1 - exp(-simTime)), maxConcentration = 12 * exp(-0.08 * simTime) * (1 - exp(-simTime)), caption = "Simulated Data 2" )
# Observed data 1 obsData1 <- data.frame( time = c(1, 3, 6, 12, 24), concentration = c(11.7, 8.4, 7.5, 3.6, 1.0), sd = c(0.204, 0.21, 0.241, 0, 0.18), caption = "Observed Data 1" ) # Observed data 2 obsData2 <- data.frame( time = c(0, 1, 3, 6, 12, 24), concentration = c(0, 6.1, 7.3, 4.4, 3.2, 1.1), minConcentration = c(0, 5.7, 7.0, 4.3, 2.5, 0.8), maxConcentration = c(0, 7.1, 8.2, 5.8, 3.3, 1.11), caption = "Observed Data 2" )
The following sections show how to plot a Time Profile for specific scenarios
# Define Data Mapping simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", group = "caption" ) plotTimeProfile( data = simData1, metaData = metaData, dataMapping = simDataMapping ) plotTimeProfile( data = simData2, metaData = metaData, dataMapping = simDataMapping )
# Define Data Mapping simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) plotTimeProfile( data = simData1, metaData = metaData, dataMapping = simDataMapping ) plotTimeProfile( data = simData2, metaData = metaData, dataMapping = simDataMapping )
# Define Data Mapping # Not using a group here will plot both profiles # but as they were the same data simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration" ) plotTimeProfile( data = rbind.data.frame( simData1, simData2 ), metaData = metaData, dataMapping = simDataMapping )
# Define Data Mapping # Using a group to plot both profiles simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", group = "caption" ) plotTimeProfile( data = rbind.data.frame( simData1, simData2 ), metaData = metaData, dataMapping = simDataMapping )
# Define Data Mapping # ymin and ymax define the CI simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) plotTimeProfile( data = rbind.data.frame( simData1, simData2 ), metaData = metaData, dataMapping = simDataMapping )
# Define Data Mapping obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", group = "caption" ) plotTimeProfile( observedData = obsData1, metaData = metaData, observedDataMapping = obsDataMapping ) plotTimeProfile( observedData = obsData2, metaData = metaData, observedDataMapping = obsDataMapping )
For the first dataset, the uncertainty/error was defined as standard deviation using the variable "sd"
.
This variable will be used by the ObservedDataMapping
object to create corresponding ymin
and ymax
values for the error bars.
Tips
NA
values cannot be used and should be replaced by 0
for error variable or y
for ymin
and ymax
variables. ymin
values of errorbars should be replaced by y
beforehand in order to still see the ymax
range of the errorbars.# Define Data Mapping obsDataMapping1 <- ObservedDataMapping$new( x = "time", y = "concentration", error = "sd", group = "caption" ) plotTimeProfile( observedData = obsData1, metaData = metaData, observedDataMapping = obsDataMapping1 )
For the second dataset, the uncertainty/error was defined as "minConcentration"
and "minConcentration"
, they are input directly as ymin
and ymax
and will be taken as is by the ObservedDataMapping
object.
# Define Data Mapping obsDataMapping2 <- ObservedDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) plotTimeProfile( observedData = obsData2, metaData = metaData, observedDataMapping = obsDataMapping2 )
# Define Data Mapping obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", group = "caption" ) plotTimeProfile( observedData = rbind.data.frame( obsData1[, c("time", "concentration", "caption")], obsData2[, c("time", "concentration", "caption")] ), metaData = metaData, observedDataMapping = obsDataMapping )
In this example, the observed data were not using the same way of displaying errors. Before plotting, the data needs to be merged in a consistent way to let the dataMapping know which variable(s) to use.
Below, ymin
and ymax
were consequently calculated for obsData1
before merging.
# Use common variable before usinf rbind.data.frame obsData1$minConcentration <- obsData1$concentration - obsData1$sd obsData1$maxConcentration <- obsData1$concentration + obsData1$sd obsData2$sd <- 0 # Define Data Mapping obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) plotTimeProfile( observedData = rbind.data.frame( obsData1, obsData2 ), metaData = metaData, observedDataMapping = obsDataMapping )
The following code flags all values higher than 10
as mdv
, leading to a plot without any observed data points higher than 10
(removing the first observation)
# Use common variable before usinf rbind.data.frame mdvData <- obsData1 mdvData$mdv <- mdvData$concentration > 10 # Define Data Mapping obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption", mdv = "mdv" ) plotTimeProfile( observedData = mdvData, metaData = metaData, observedDataMapping = obsDataMapping )
By combining data
and their dataMapping
with observedData
and their observedDataMapping
,
complex time profiles comparing observed and simulated data can be plotted.
# Define Data Mappings simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", group = "caption" ) obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", group = "caption" ) plotTimeProfile( data = simData1, observedData = obsData1, metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping ) plotTimeProfile( data = simData2, observedData = obsData2, metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping )
# Define Data Mappings simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) plotTimeProfile( data = simData1, observedData = obsData1, metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping ) plotTimeProfile( data = simData2, observedData = obsData2, metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping )
Combining multiple simulations and/or observed data sets is rather simple. To differentiate between the simulations/data sets, a grouping variable corresponding to the legend caption needs to be defined as illustrated below.
# Define Data Mappings simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", group = "caption" ) obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", group = "caption" ) simAndObsTimeProfile <- plotTimeProfile( data = rbind.data.frame( simData1, simData2 ), observedData = rbind.data.frame( obsData1, obsData2 ), metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping ) simAndObsTimeProfile
In this example observed and simulated data do not share any mapping. Therefore, their color pattern may not match. To update the plot and have appropriate colors and labels in the legend, it is possible to get the legend properties and update them as illustrated in the example below that updates the positions of the legend entries:
plotLegend <- getLegendCaption(simAndObsTimeProfile) knitr::kable(as.data.frame(plotLegend$color))
plotLegend$color <- plotLegend$color[c(1, 3, 2, 4), ] updateTimeProfileLegend(simAndObsTimeProfile, plotLegend)
Note that if observed and simulated data share common groupings, they will be merged in the final legend.
commonSimData1 <- simData1 commonObsData1 <- obsData1 commonSimData1$caption <- "Common data 1" commonObsData1$caption <- "Common data 1" commonSimData2 <- simData2 commonObsData2 <- obsData2 commonSimData2$caption <- "Common data 2" commonObsData2$caption <- "Common data 2" plotTimeProfile( data = rbind.data.frame(commonSimData1, commonSimData2), observedData = rbind.data.frame(commonObsData1, commonObsData2), metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping )
# Define Data Mappings simDataMapping <- TimeProfileDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) obsDataMapping <- ObservedDataMapping$new( x = "time", y = "concentration", ymin = "minConcentration", ymax = "maxConcentration", group = "caption" ) plotTimeProfile( data = rbind.data.frame( simData1, simData2 ), observedData = rbind.data.frame( obsData1, obsData2 ), metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping )
For time profile plot properties can be defined in advance using TimeProfilePlotConfiguration
objects.
The code below defines a smart plot configuration that uses data
, metaData
, and dataMapping
to define the names of labels.
tpConfiguration <- TimeProfilePlotConfiguration$new( data = simData1, metaData = metaData, dataMapping = simDataMapping )
As every PlotConfiguration
objects, TimeProfilePlotConfiguration
objects include fields defining the properties of its lines
, points
, ribbons
and errorbars
.
By default, these properties are used from the current theme, but can be overridden as illustrated below.
# Define which lines are used for simulated data tpConfiguration$lines$linetype <- Linetypes$solid tpConfiguration$lines$size <- 1 tpConfiguration$lines$color <- c("blue", "red") # Define which ribbons are used for simulated data CI tpConfiguration$ribbons$fill <- c("dodgerblue", "firebrick") tpConfiguration$ribbons$alpha <- 0.25 # Define which shapes are used for observed data tpConfiguration$points$shape <- Shapes$circle tpConfiguration$points$size <- 1 tpConfiguration$points$color <- c("blue", "red") # Define which errorbars are used for observed data CI tpConfiguration$errorbars$color <- "black" tpConfiguration$errorbars$size <- 0.5
The corresponding plot is shown below:
plotTimeProfile( data = rbind.data.frame( simData1, simData2 ), observedData = rbind.data.frame( obsData1, obsData2 ), metaData = metaData, dataMapping = simDataMapping, observedDataMapping = obsDataMapping, plotConfiguration = tpConfiguration )
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.