---
title: "Time Profile Plots"
output:
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Time Profile Plots}
  %\VignetteEncoding{UTF-8}
  %\VignetteEngine{knitr::rmarkdown}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  dpi = 300,
  fig.align = "center",
  out.width = "100%",
  fig.height = 6,
  fig.width = 8,
  fig.showtext = TRUE
)
```

```{r setup}
require(tlf)
# Place default legend position above the plot for prettier time profile plots
setDefaultLegendPosition(LegendPositions$outsideTop)
```

# 1. Introduction

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.

# 2. Definition of the time profile functions and classes

## 2.1. `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`.

## 2.2. Data Mappings

### 2.2.1. `dataMapping` argument

For 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.

```r
TimeProfileDataMapping$new(
x,
y = NULL,
ymin = NULL,
ymax = NULL,
group = NULL
)
```

### 2.2.2. `observedDataMapping` argument

For 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.

```r
ObservedDataMapping$new(
x,
y,
error = NULL,
ymin = NULL,
ymax = NULL,
group = NULL,
mdv = NULL
)
```

### 2.2.3. Remaining TODOs

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.

# 3. Examples

For the examples presented in the sequel, two simulated and two observed datasets are created below:

```{r get simulated data for examples}
# 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"
)
```

```{r get observed data for examples}
# 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

## 3.1. Plot simulated data only

### 3.1.1 Single simulation

```{r examples single simulation}
# 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
)
```

### 3.1.2. Single simulation with confidence interval

```{r examples single simulation with confidence interval}
# 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
)
```

### 3.1.3. Multiple simulations

```{r examples multiple simulations no caption}
# 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
)
```

```{r examples multiple simulations}
# 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
)
```

### 3.1.4. Multiple simulations with confidence interval

```{r examples multiple simulations with confidence interval}
# 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
)
```

## 3.2. Plot observed data only

### 3.2.1. Single observed data set

```{r examples single observation}
# 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
)
```

### 3.2.2. Single simulation with confidence interval

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**

- For data that include errorbars only partially, `NA` values cannot be used and should be replaced by `0` for error variable or `y` for `ymin` and `ymax` variables. 
- For logarithmic plots, negative `ymin` values of errorbars should be replaced by `y` beforehand in order to still see the `ymax` range of the errorbars.

```{r examples single observation with confidence interval as error}
# 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.

```{r examples single observation with confidence interval as ymin-ymax}
# Define Data Mapping
obsDataMapping2 <- ObservedDataMapping$new(
  x = "time",
  y = "concentration",
  ymin = "minConcentration",
  ymax = "maxConcentration",
  group = "caption"
)

plotTimeProfile(
  observedData = obsData2,
  metaData = metaData,
  observedDataMapping = obsDataMapping2
)
```

### 3.2.3. Multiple observed data sets

```{r examples multiple observations no caption}
# 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
)
```

### 3.2.4. Multiple observed data sets with confidence interval

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.

```{r examples multiple observations with confidence interval}
# 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
)
```

### 3.2.4. Missing Dependent Variable (mdv)

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)


```{r examples mdv}
# 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
)
```


## 3.3. Plot simulated and observed data

By combining `data` and their `dataMapping` with `observedData` and their `observedDataMapping`, 
complex time profiles comparing observed and simulated data can be plotted.

### 3.3.1. Single simulation and observed data set

```{r combine simulated and observed, eval= getRversion() >= "4.0"}
# 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
)
```

### 3.3.2. Single simulation and observed data set with their confidence intervals

```{r combine simulated and observed with ci}
# 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
)
```

### 3.3.3. Multiple simulations and observed data sets

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.

```{r combine multiple simulated and observed}
# 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:

```{r get legend properties, results='asis'}
plotLegend <- getLegendCaption(simAndObsTimeProfile)
knitr::kable(as.data.frame(plotLegend$color))
```

```{r update legend properties}
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.

```{r combine simulated and observed sharing 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
)
```

### 3.3.4. Multiple simulations and observed data sets with their confidence intervals

```{r combine multiple simulated and observed with ci}
# 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
)
```


## 3.4. Plot Configuration

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.

```{r time profile plot configuration}
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.

```{r define time profile plot configuration properties}
# 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:

```{r time profile plot using plot configuration}
plotTimeProfile(
  data = rbind.data.frame(
    simData1,
    simData2
  ),
  observedData = rbind.data.frame(
    obsData1,
    obsData2
  ),
  metaData = metaData,
  dataMapping = simDataMapping,
  observedDataMapping = obsDataMapping,
  plotConfiguration = tpConfiguration
)
```


