Level up your pharmacometrics skills with a hands-on workshop alongside ACoP 2026. Register here.
MonolixSuite in R

Automating dataset updates with lixoftConnectors

Download Monolix projects

When a dataset is updated (e.g. a new data cut with more subjects, additional observations, corrections, etc), every Monolix/PKanalix project usually has to be re-run on the new data. Doing this by hand (opening each project, replacing the dataset, checking the tagging, launching the run, saving) quickly becomes cumbersome and error-prone, especially with many models.

If the structure of the dataset does not change (same columns, only more rows), this can be fully automated with the R package lixoftConnectors. A single script then loads every project, swaps in the new dataset, re-runs it, and collects the results.

The example below is based on downloadable example projects. Only the dataset needs to be updated; running the script re-estimates all projects automatically.

R
rm(list = ls())
path.prog <- dirname(rstudioapi::getSourceEditorContext()$path)

library(lixoftConnectors)
library(dplyr)

# Initialize Monolix connectors
initializeLixoftConnectors(software = "monolix", force = TRUE)
#-------------------------------------------------------------------------------
# helper function to retrieve information criterion of project
get_info_row <- function(project_name) {
  ll_res <- getEstimatedLogLikelihood()
  
  if ("importanceSampling" %in% names(ll_res)) {
    ll <- ll_res$importanceSampling
    method_used <- "importanceSampling"
  } else if ("linearization" %in% names(ll_res)) {
    ll <- ll_res$linearization
    method_used <- "linearization"
  } else {
    stop(paste("No likelihood results found for", project_name))
  }
  
  data.frame(
    project = project_name,
    method  = method_used,
    OFV     = as.numeric(ll["OFV"]),
    BICc    = as.numeric(ll["BICc"]),
    data    = data_file)
}
#-------------------------------------------------------------------------------
# data tagging and obsid mapping must be the same for all projects
data_settings <- list(
  dataFile = data,
  headerTypes = c(
    "id",          # id
    "time",        # time
    "amount",      # amt
    "observation", # dv
    "obsid",       # dvid
    "contcov",     # wt
    "catcov",      # sex
    "contcov"),    # age
  observationTypes = list(
    "1" = "continuous",
    "2" = "continuous"))
#-------------------------------------------------------------------------------

data_file <- "warfarin_dataset_V01.csv"       # <-- data file can be updated
data <- paste0(path.prog,"/Data/",data_file)

# Each entry is one work dir + the projects it contains
work_dirs <- list(
  list(dir      = paste0(path.prog,"/PKmodeling/1_1_structuralmodel/"), # Structural PK model
       projects = c("r01_1cmt",
                    "r02_Tlag_1cmt",
                    "r03_Tlag_2cmt")),
  list(dir      = paste0(path.prog,"/1_PKmodeling/1_2_statisticalmodel/"),   # Statistical PK model
       projects = c("r02_1_Tlag_1cmt_comb2err",
                    "r02_2_Tlag_1cmt_comb2err_logwtV",
                    "r02_3_Tlag_1cmt_comb2err_logwtVCl",
                    "r02_4_Tlag_1cmt_comb2err_logwtVCl_logageCl")))

# Outer loop: work dirs / inner loop: projects in that work dir
res <- data.frame(NULL)
for (wd in work_dirs) {
  info_criteria <- data.frame()
  for (prj in wd$projects) {
    loadProject(paste0(wd$dir, prj, ".mlxtran"))
    setData(data_settings)              # setData only changes data file, other settings stay the same in monolix project
    runScenario()
    saveProject(paste0(wd$dir, prj))
    
    info_temp     <- get_info_row(prj)
    info_criteria <- rbind(info_criteria, info_temp)
  }
  res <- rbind(res, info_criteria) %>% arrange(BICc)  # collect all projects into one data frame
}

print(res)

Notes on the script

  • data_file is the only line to touch for an update: point it at the new dataset in the Data/ folder, then run the whole script.

  • get_info_row() reads the estimated log-likelihood of the currently loaded project and returns one row (OFV, BICc, method, data file) to build the summary table.

  • data_settings defines the column tagging and observation mapping once. Since these do not change from project to project, the same settings are applied to every project. setData() only replaces the data file; the model, initial values, and task configuration stored in each .mlxtran stay untouched.

  • work_dirs lists the working directories and the projects in each.

  • The two loops iterate over the working directories (outer) and their projects (inner). For each project the same calls run (loadProject, setData, runScenario, saveProject) and the
    information criterion is appended to res.

  • res is the final, BICc-sorted table with one row per re-estimated project on the new dataset.

Last updated: