Examples using R functions

This page provides pieces of code to perform some procedures with the R functions for Simulx. The code has to be adapted to use your projects and use the results as you wish in R.

Minimal examples based on demo projects are also included in the documentation of all Simulx functions.

Load a project and run the simulation

Simple example to load an existing project, run the simulation and look at the results :

# load and initialize the API
library(lixoftConnectors) 
initializeLixoftConnectors(software="simulx")

# Get the project
project <- paste0(getDemoPath(), "/2.models/longitudinal.smlx")
loadProject(projectFile = project)

# Run the simulation
runSimulation()

# The results are accessible through the function getSimulationResults()
# The results for the output is the list res and TS is one of the outputs
head(getSimulationResults()$res$TS)

 

  id time       TS
1  1    0 10.00000
2  1    1 11.04939
3  1    2 12.20862
4  1    3 13.48915
5  1    4 14.90359
6  1    5 16.46585

 

Import a Monolix project and simulate a new output variable with the EBEs



In this example, the Monolix demo theophylline_project.mlxtran is imported to Simulx. A new variable AUC is added to the model, and it is simulated on a regular grid using the EBEs estimated in the Monolix project. This requires that the tasks “Population parameters” and “EBEs” have run in the Monolix project.

# Import a Monolix project and simulate a new output variable with the EBEs  =====
# Load and initialize library =====
library(lixoftConnectors)
initializeLixoftConnectors(software = "monolix")

#Load project from Monolix Demos and run it to get EBEs estimated =====
MonolixProject <- paste0(getDemoPath(), "/1.creating_and_using_models/1.1.libraries_of_models/theophylline_project.mlxtran") 
loadProject(MonolixProject) 
runScenario() 
initializeLixoftConnectors(software = "simulx", force = TRUE) 

#IMPORT Monolix project and check that mlx_EBEs element has been created =====
importProject(MonolixProject) getIndividualElements() 

#SET ADDITIONAL formula in the model to compute the AUC =====
setAddLines("ddt_AUC = Cc") # define a new element output with a regular grid for the variable "AUC" 
defineOutputElement(name="AUCregular", element = list( data = data.frame( start = 0, interval = 1, final = 120), output = "AUC")) 
#CHECK the simulation group that already exists =====
getGroups() 
# => we currently have one group, called "simulationGroup1", which uses the following elements:
# - mlx_Pop (population parameter values) => need to be replaced by mlx_EBEs
# - mlx_Adm1 (treatment from monolix data set) => OK
# - mlx_CONC (output CONC with times as in monolix data set) => need to be replaced by AUC with new grid
# - size = 12 (same number of in original data set) => OK

#MODIFY the existing simulation group such that it uses EBEs and the new output element=====
setGroupElement(group = "simulationGroup1", 
                elements = c("mlx_EBEs", "AUCregular"))

# check the new simulation setup
getGroups()

#Set shared IDs to say the individuals in the treatment element and in the parameter element are the same and should alwyas be sampled together =====
setSharedIds(c("treatment","individual"))

# check the simulation setup
getGroups()
getSharedIds()

# - mlx_EBEs => ok
# - remaining parameters (error model parameters if residual error is simulated) => not needed as we output model predictions
# - mlx_Adm1 => ok
# - AUCregular => ok
# - size=12 => ok


#SAVE and RUN project =====
saveProject("simulx_api_example.smlx")

# run simulation
runSimulation()


#GET the results: simulated values for AUC and sampled individual parameters =====
simulatedParam <- getSimulationResults()$IndividualParameters$simulationGroup1
simulatedOutput <- getSimulationResults()$res$AUC


#PLOT the results =====
library(ggplot2)
ggplot(data = simulatedOutput, aes(x=time, y=AUC))+
  geom_line() + aes(color = factor(as.double(original_id)))+
  labs(colour="Original ID")


We get the following simulations for the new output variable. “Original_id” is the ID used in the original dataset loaded in the Monolix project.

Create a Simulx project from scratch and simulate covariate-dependent treatments

 

This script creates a project similar to the demo project treatment_weight_and_genotype_based.smlx, available in the folder 3.1.treatments of Simulx demos. The model file TMDDmodel.txt can be downloaded here.

While in the interface of Simulx it is possible to directly define distribution laws for covariates and covariate-dependent treatments that are computed at the simulation step, in R it is necessary to sample covariates from distributions and derive the corresponding doses before defining the elements for the simulation.

###############################################################
# Create a Simulx project from scratch and simulate covariate-dependent treatments
###############################################################

# load and initialize the API 
library(lixoftConnectors) 
initializeLixoftConnectors(software = "simulx")

# Create a new project based on the model
newProject(modelFile = "TMDDmodel.txt")

# Define vector of population parameters
definePopulationElement(name="PopParameters", 
                        element = data.frame(V_pop=3, omega_V=0.1 ,Cl_pop=0.1, omega_Cl=0.1, Q_pop=1, omega_Q=0.2,
                                             V2_pop=3, omega_V2=0.2, beta_V_logtWeight=1, KD_pop=0.01, omega_KD=0.01,
                                             R0_pop=0.2, omega_R0=0.3, kint_pop=50, omega_kint=0.2, kon_pop=10,
                                             omega_kon=0.01, ksyn_pop=10, omega_ksyn=0.2, beta_Cl_logtWeight=0.75,
                                             beta_Q_logtWeight=0.75, beta_V2_logtWeight=0.75, 
                                             beta_KD_Genotype_Heterozygous=1.3))

# Define covariates: in the interface it is possible to define distribution laws,
# but in R we have to sample from the distributions prior to the simulation.
# Here we define 3 tables for the 3 simulation groups
for (i in 1:3){
  covTable <- data.frame(id=1:250,
                         Weight = rlnorm(n=250, mean=log(70), sd=0.3),
                         Genotype=c("Homozygous", "Heterozygous")[sample(1:2, 250, replace=T)])
  write.csv(covTable, file = paste0("covTable",i,".csv"), quote=F, row.names=F)
  defineCovariateElement(name=paste0("covTable",i),
                         element = paste0("covTable",i,".csv"))
}

# Define common treatment
defineTreatmentElement(name="1000nmol", element=list(admID=1, data=data.frame(time=seq(0,by=21,length.out = 5), 
                                                                              amount=1000, tInf=0.208)))

# Define weight-based treatment: in the interface it can be done directly with a scaling formula,
# but in R we have to define a table of individual doses prior to the simulation.
# We use covTable2.csv for simulation group 2.
covTable <- read.csv("covTable1.csv")
trt_14nmolPerKg <- data.frame(id=rep(1:250, each=5),
                              time=rep(seq(0,by=21,length.out = 5), times=250),
                              amount=14*covTable$Weight, tInf = 0.208)
write.csv(trt_14nmolPerKg, file="trtTableWeight.csv", quote=F, row.names=F)
defineTreatmentElement(name="14nmolPerKg", element=list(admID=1, data="trtTableWeight.csv"))

# Define genotype-based treatment: in the interface it can be done directly with a scaling formula,
# but in R we have to define a table of individual doses prior to the simulation.
# We use covTable3.csv for simulation group 3.
covTable <- read.csv("covTable3.csv")
trt_genotype <- data.frame(id=rep(1:250, each=5),
                           time=rep(seq(0,by=21,length.out = 5), times=250),
                           amount=ifelse(covTable$Genotype=="Heterozygous", 1000, 800), tInf = 0.208)
write.csv(trt_genotype, file="trtTableGenotype.csv", quote=F, row.names=F)
defineTreatmentElement(name="1000nmolHomo_800nmolHetero", element=list(admID=1, data="trtTableGenotype.csv"))

# Define outputs
defineOutputElement(name="Concentration", element = list(output="L", data=data.frame(time=seq(0,96,by=1))))
defineOutputElement(name="TargetOccupancy", element = list(output="TO", data=data.frame(time=seq(0,96,by=1))))

# By default there is a single simulation group named simulationGroup1,
# change its name and set all elements for the simulation
setGroupSize("simulationGroup1", 250)
setGroupElement("simulationGroup1", elements = c("PopParameters","14nmolPerKg","Concentration","TargetOccupancy", "covTable1"))
renameGroup("simulationGroup1", "Weight_based")

# Define the two other simulation groups with different covariates and treatments.
# By default the other elements are the same as the first group
addGroup("Flat_dose")
setGroupElement("Flat_dose", elements = c("1000nmol","covTable2"))
addGroup("Genotype_based")
setGroupElement("Genotype_based", elements = c("1000nmolHomo_800nmolHetero","covTable3"))

# Save project and run the simulation
saveProject(projectFile = "treatment_weight_and_genotype_based.smlx") 
runSimulation()

# Get simulation results: a table of individual parameters for each group, and a table of simulated values for each output
sim <- getSimulationResults()
names(sim$IndividualParameters) # "Weight_based", "Flat_dose", "Genotype_based"
names(sim$res) # "L", "TO"


Import a Monolix project and simulate with new output times

 

Monolix simulates the original dataset with replicates to generate the VPC. It can be useful to perform the same simulations in Simulx with modified observation times, for example to correct the bias in VPC due to dropout. The example below shows this step, and uses the project PDTTE_dropout. The full example to correct a VPC for dropout is detailed on this page.

In this example the Monolix project with the biased VPC is named PD_TTE.mlxtran, the new regular measurement times are similar to the measurements in the original dataset, but are not impacted by dropout, and the number of replicates is 500 (as the default number of simulations for the VPC in Monolix). The random variable capturing dropouts in the model used in Monolix is called survival.

###############################################################
# Import a Monolix project and simulate with new output times
###############################################################

library(lixoftConnectors)
initializeLixoftConnectors(software = "simulx")

importProject(projectFile = "PDTTE_dropout.mlxtran")
defineOutputElement(name="y1_alltimes", element = list(output="y1", data=data.frame(time=seq(-100,1500,by=100))))
setGroupElement("simulationGroup1", elements = c("y1_alltimes","mlx_survival"))
setNbReplicates(500) 
saveProject(projectFile = "PD_TTE_alltimes.smlx")
setPreferences(exportsimulationfiles=T)
runSimulation()

 

Run a simulation with different sample sizes and plot the power of the study based on an endpoint

 

This piece of code uses the demo project OutcomeEndpoint_PKPD_changeFromBaseline.smlx (demo folder 6.1), with the goal of comparing two treatment arms (400mg Q4W and 800mg Q8W) with a range of different sample sizes (number of simulated individuals per arm). The power of the clinical trial is defined as the chance of having a significant difference in the mean change from baseline between the two arms. This chance is computed over 200 replicates for each sample size. The script then plots the power of the clinical trial with respect to the sample size, with a target power of 85% displayed as a horizontal line.

Note that Simulx versions prior to 2023 did not provide connectors to define the outcomes and endpoints like in the interface.

library(ggplot2)

#initialize LixoftConnectors with Simulx
library(lixoftConnectors)
initializeLixoftConnectors(software="simulx")

# load Simulx demo project comparing 3 treatment arms
project <- paste0(getDemoPath(), "/6.outcome_endpoints/6.1.outcome_endpoints/OutcomeEndpoint_PKPD_changeFromBaseline.smlx") loadProject(projectFile = project) # check the groups defined in the project. # We are interested comparing the two groups 400mg_q4w_ and 800mg_q8w_ for the output "Cholesterol_measured_atCtrough" # -> remove other groups and outputs
getGroups()
removeGroup("400mg_q8w_")
removeGroupElement("400mg_q4w_", "Cholesterol_prediction")
removeGroupElement("800mg_q8w_", "Cholesterol_prediction")
removeGroupElement("400mg_q4w_", "mAbTotal_prediction")
removeGroupElement("800mg_q8w_", "mAbTotal_prediction")

# check the endpoints defined in the project. We are interested in the endpoint mean_changeFromBaseline
getEndpoints()
deleteEndpoint("percentChangeFromBaseline")


#enable group comparison since it is disabled in the demo project
setGroupComparisonSettings(referenceGroup = "400mg_q4w_",enable = TRUE)


# gather endpoints for trials of several sample sizes
Nrep <- 200
sample_sizes <- c(15, 30, 50, 75, 100)
PowerDF = data.frame(sampleSize=sample_sizes,Power=rep(0,length(sample_sizes)))


meanChFromBL <-NULL
for(indexN in 1:length(sample_sizes)){
  
  N <- sample_sizes[indexN]
  # change the number of individuals for each arm
  setGroupSize("400mg_q4w_", N)
  setGroupSize("800mg_q8w_", N)
  setNbReplicates(Nrep)
  
  # run the simulation and endpoints
  runScenario()
  
  # optional - gathering endpoints to plot them for each group and sample size
  meanChFromBL_N <- getEndpointsResults()$endpoints$mean_changeFromBaseline
  meanChFromBL <- rbind(meanChFromBL, cbind(meanChFromBL_N, sampleSize=N))
  
  # gathering success for each replicate
  successForAllRep <- as.logical(getEndpointsResults()$groupComparison$mean_changeFromBaseline$success)
  PowerDF$Power[indexN]=sum(successForAllRep)/Nrep*100
  
}

# to plot the results (no connectors available yet for Simulx plots)

library(ggplot2)

ggplot(meanChFromBL, aes(group, arithmeticMean)) + 
        geom_boxplot(aes(fill = group)) + 
        facet_grid(. ~ sampleSize) + 
        ylab("Mean change from baseline at Ctrough")+
        xlab("")+
        theme(axis.text.x = element_blank(), axis.ticks = element_blank())

ggplot(PowerDF)+geom_line(aes(x=sampleSize,y=Power, color="red"))+ 
  ylab("Power (%)")+xlab("Number of subjects per arm")+geom_hline(yintercept = 85)+
  ylim(c(0,100))+ theme(legend.position = "none")

 

Handling of warning/error/info messages

Error, warning and info messages from Simulx are displayed in the R console when performing actions on a Simulx project. They can be hidden via the R options. Set lixoft_notificationOptions$errors, lixoft_notificationOptions$warnings and lixoft_notificationOptions$info to 1 or 0 to respectively hide or show the messages.

Example

op = options()
op$lixoft_notificationOptions$warnings = 1   #hide the warning messages
options(op)

Force software switch

By default, function initializeLixoftConnectors() prompts users to confirm that they want to proceed with the software switch, in order to avoid losing unsaved changes in the currently loaded project. To override this behavior, force argument can be set to TRUE when calling the function. However, the default behavior can be changed globally as well.

Example

op = options()
op$lixoft_lixoftConnectors_forceSoftwareSwitch <- TRUE
options(op)

modal close image