Simulation & VPC

Overview

ferx_simulate() generates replicated datasets from the fitted model. The primary use case is the Visual Predictive Check (VPC): overlay observed data on simulation-based prediction intervals to assess model adequacy visually.

library(ferx)
library(ggplot2)
library(dplyr)

ex  <- ferx_example("warfarin")
obs <- read.csv(ex$data)
fit <- ferx_fit(ex$model, ex$data, method = "foce", covariance = TRUE,
                verbose = FALSE)
Mu-referencing detected for: ETA_CL, ETA_KA, ETA_V

Simulating from the fitted model

The typical VPC workflow passes fit to ferx_simulate() so that simulation uses the fitted theta, omega, and sigma rather than the model file’s initial values:

sim <- ferx_simulate(
  model  = ex$model,
  data   = ex$data,
  n_sim  = 500,
  seed   = 42,
  fit    = fit
)
head(sim)
  DRAW SIM ID TIME     IPRED    DV_SIM
1    1   1  1  0.5  4.392110  4.381504
2    1   1  1  1.0  7.228551  7.177091
3    1   1  1  2.0 10.200605 10.177057
4    1   1  1  4.0 11.726456 11.750829
5    1   1  1  8.0 11.342447 11.521931
6    1   1  1 12.0 10.603030 10.454153

n_sim = 500 replicates is sufficient for a standard VPC. Set seed for reproducibility.

What the simulation returns

Column Meaning
SIM Replicate index (1 to n_sim)
ID Subject ID (same population structure as observed data)
TIME Time (matched to observation schedule in data)
IPRED Individual prediction for this replicate (no residual error)
DV_SIM Simulated observation (IPRED + residual noise)

To propagate parameter uncertainty across draws, use ferx_simulate_with_uncertainty(), which adds a DRAW column indexing the population-parameter draw — see Overview.

Simulating at initial values (without a fit)

Omit fit to simulate at the initial estimates in the model file. Useful for a prior predictive check:

sim_prior <- ferx_simulate(ex$model, ex$data, n_sim = 200, seed = 1)

Propensity-score-matched simulation (RWD / pmVPC)

VPCs built from real-world data (RWD) can be biased by treatment adaptation: a subject’s observed dosing and sampling design is often a response to that subject’s own disposition. For example, high-clearance patients may be given longer dosing intervals. A naïve simulation draws etas independently of the design, so the simulated and observed populations no longer share the design–disposition coupling, and the VPC misleads.

The match = TRUE argument to ferx_simulate() addresses this. For each replicate, the drawn etas are reassigned to subjects by optimal Mahalanobis matching (under the model omega) against the subjects’ fitted (posthoc) etas. Each subject’s observed dosing/sampling design is thereby paired with a similar drawn eta, restoring the design–disposition coupling and producing a propensity-matched VPC (pmVPC). This requires data to be real observed data — every subject must have observations so its posthoc eta can be computed. When a fit is supplied, the posthoc etas are computed at the fitted parameters; otherwise at the model file’s initial values.

sim_pm <- ferx_simulate(
  model = ex$model,
  data  = ex$data,
  n_sim = 500,
  seed  = 42,
  fit   = fit,
  match = TRUE
)

The returned columns are identical to the unmatched call (SIM, ID, TIME, IPRED, DV_SIM); feed sim_pm into the same VPC construction below.

Building a VPC

Using the vpc package

library(vpc)

vpc(
  obs  = obs |> mutate(DV = as.numeric(DV)),
  sim  = sim,
  obs_cols = list(),
  sim_cols = list(dv = "DV_SIM")
)

(The vpc package is an optional dependency; install with install.packages("vpc").)

Population predictions

ferx_predict() computes population predictions (ETA = 0) without residual noise. Useful for overlaying the typical profile on observed data:

preds <- ferx_predict(ex$model, ex$data, fit = fit)
head(preds)
  ID TIME      PRED
1  1  0.5  3.917754
2  1  1.0  6.609725
3  1  2.0  9.695979
4  1  4.0 11.639355
5  1  8.0 11.504506
6  1 12.0 10.775094

ferx_predict() returns a data frame with ID, TIME, and PRED (population prediction at ETA = 0). For uncertainty-propagated predictions, see ferx_simulate_with_uncertainty() in Overview.

[simulation] block

For CLI-driven simulation without an external dataset, the .ferx model file can embed a trial design via the [simulation] block. This is a DSL feature rather than an R workflow — see the Model file reference in the ferx-core book for syntax and usage.