Skip to content

Commit aa4f3b2

Browse files
update exposure response vignette
update news
1 parent 14fa579 commit aa4f3b2

File tree

2 files changed

+81
-38
lines changed

2 files changed

+81
-38
lines changed

NEWS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* added possibility to add different ref line(s) by parameter via
77
`ref_value_by_panel` and `ref_value_by_panel_data` function arguments (not in the shiny, app)
88
* added and exported `expand_modelframe` function
9-
* added the possibility to select the shapes manually via `interval_shape` and `bsv_shape` (when shape is mapped to paramname or not)
9+
* added the possibility to select the shapes manually via `interval_shape` and `bsv_shape`
1010
* added capability for user to reverse color legend separately via `legend_color_reverse`
11-
* added capability for user to specify text for legend titles via `interval_legend_title` and `shape_legend_title` as well as text size via `legend_title_size`
11+
* added capability for user to specify text for legend titles via `interval_legend_title` and `shape_legend_title` as well as to control text size via `legend_title_size`
1212

1313
# coveffectsplot 1.0.3
1414
* fixed a bug when ref_value when not equal 1 affecting y axis limits

vignettes/Exposure_Response_Example.Rmd

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ vignette: >
1010
%\VignetteEngine{knitr::rmarkdown}
1111
%\VignetteEncoding{UTF-8}
1212
---
13-
Here we illustrate the approach using a Binary response linked to exposure (AUC) via a saturating EMAX function. Weight is a covariate on Clearance. We also have a disease severity categorical covariate on EMAX where patient with severe disease have a lower EMAX.
1413

1514
```{r, include = FALSE}
1615
knitr::opts_chunk$set(
@@ -35,6 +34,7 @@ library(patchwork)
3534
library(ggdist)
3635
library(ggrepel)
3736
library(Rcpp)
37+
library(egg)
3838
theme_set(theme_bw())
3939
nsim <- 100 # for vignette to make it run faster otherwise increase to 1000
4040
#utility function to simulate varying one covariate at a time keeping the rest at the reference
@@ -55,11 +55,14 @@ expit <- function(x) exp(x)/ (1 + exp(x) )
5555
5656
```
5757

58-
## Specifying an Exposure Response Model using `mrgsolve`
59-
We code a logistic regression model linked to AUC and Severity of disease.
58+
## Simulating an Exposure Response Model using `mrgsolve`
59+
Here we illustrate the communication of covariate effects of a Binary response endpoint (0/1) model. The response is linked to PK exposures represented by the area under the curve of PK concentrations (AUC). The AUCs act by a saturating Emax function on the logit scale while body weight has an effect on PK clearance (CL). Finally, the disease severity is an important covariate on Emax where patients with severe disease have a 50% lower Emax.
60+
61+
Key model equations are presented below:
62+
6063
The AUC equals Dose/Individual Pharmacokinetic Clearance (CLi) of the Drug. CLi is a function of the population Clearance (CL), the patient's specific Weight and a random between patients variability term.
61-
$$AUC = \left(\frac { \color{green}{Dose}} {\color{red}{CL} \times \left( \frac { \color{green}{Weight}} {70}\right)^{WTCL} \times exp(\eta{CL}) }\right)$$
62-
The Emax depends on Disease Severity:
64+
$$\color{blue}{AUC} = \left(\frac { \color{green}{Dose}} {\color{red}{CLi} }\right)= \left(\frac { \color{green}{Dose}} {\color{red}{CL} \times \left( \frac { \color{green}{Weight}} {70}\right)^{WTCL} \times exp(\eta{CL}) }\right)$$
65+
The Emax is reduced when the disease is severe:
6366

6467
$$E_{max}= \color{red}{E_{max} \left(intercept \right)} + \color{red}{SevE_{max}}\times\left(\color{green}{Severity} = 1\right) $$
6568
The equation of the log(odds) becomes:
@@ -77,7 +80,7 @@ $PARAM @annotated
7780
TVCL : 10 : Clearance CL (L/h)
7881
WTCL : 0.75: Weight on CL (ref. 70 kg)
7982
TVEMAX : 5 : Maximum Drug Effect
80-
SEVEMAX : 3 : Severity Reduction of Drug Effect
83+
SEVEMAX : 2.5 : Severity Reduction of Drug Effect
8184
AUC50 : 7.5 : Area Under the Curve providing half maximal response
8285
TVBASEP : 0.1 : Baseline Probability of Response
8386
@@ -116,8 +119,8 @@ simout <- modexprespsim %>%
116119
mrgsim()%>%
117120
as.data.frame
118121
```
119-
## Probability of Cure
120-
From the simulation of N=1000 patients we plot the probability of disease Cure versus PK exposures (AUC) for a reference subject with Weight of 70 kg and clinical severity of Not Severe.
122+
## Visualizing Probability of Cure
123+
From the simulation of N=1000 patients we plot the probability of disease Cure versus PK exposures (AUC) for a reference subject with Weight of 70 kg and disease severity of "Not Severe".
121124

122125
```{r exprespmodeplotl, collapse=TRUE, fig.height= 7, fig.width= 9 }
123126
WT_names <- c(
@@ -128,9 +131,19 @@ SEV_names <- c(
128131
)
129132
simout$SEV_cat <- "Not Severe"
130133
131-
132134
vlines <- quantile(simout$AUC[simout$AUC>0],probs = c(0,1/4,0.5,3/4,1),na.rm = TRUE)
133135
136+
137+
simoutdataprobsdose <- simout %>%
138+
group_by(DOSE) %>%
139+
summarise(probs = mean(DV),
140+
n = n(),
141+
lower = probs - qnorm(1-0.05/2)*sqrt((1/n)*probs*(1-probs)),
142+
upper = probs + qnorm(1-0.05/2)*sqrt((1/n)*probs*(1-probs)),
143+
n0 = length(DV[DV==0]),
144+
n1= length(DV[DV==1]),
145+
medAUC= median(AUC))
146+
134147
simoutdataprobs <- simout %>%
135148
mutate(AUC_bins=table1::eqcut(AUC,4,withhold = list(PLACEBO=AUC==0))) %>%
136149
group_by(AUC_bins) %>%
@@ -168,6 +181,9 @@ probplot <- ggplot(simout, aes(AUC,DV,linetype=factor(SEV_cat))) +
168181
geom_text(data=simoutdataprobs, aes(x=medAUC,y=probs,
169182
label=paste(round(100*probs,1),"%")),
170183
inherit.aes = FALSE,hjust=-0.15,vjust=-0.15,size=4)+
184+
geom_point(data=simoutdataprobsdose %>% filter(DOSE!=0),
185+
aes(x=medAUC,y=probs,color=factor(DOSE)),
186+
inherit.aes = FALSE,size=4, shape="diamond")+
171187
geom_text_repel(data=simoutdataprobs, aes(x=medAUC,y=Inf,
172188
label=paste(n1,n,sep = "/")),
173189
inherit.aes = FALSE,direction="y")+
@@ -184,7 +200,7 @@ exposureplot <- ggplot(simout,
184200
geom_text(data=data.frame(vlines), aes(x=vlines,y=-Inf,
185201
label=paste(round(vlines,1))),
186202
inherit.aes = FALSE,vjust=-0.1,hjust=1)+
187-
stat_slab(scale = 1, alpha=0.5,
203+
stat_slab(scale = 1, alpha= 0.9,
188204
aes(fill_ramp = after_stat(
189205
ifelse(x<= vlines[2],"",
190206
ifelse(x>vlines[2] & x <=vlines[3],"50%",
@@ -200,16 +216,24 @@ exposureplot <- ggplot(simout,
200216
strip.background.x = element_blank(),
201217
strip.text.x = element_blank(),
202218
plot.background = element_blank())+
203-
labs(x = "AUC", y = "DOSE (mg)")
204-
(probplot+
219+
labs(x = "AUC", y = "DOSE (mg)")+
220+
scale_y_discrete(breaks= c(0,75),labels=c("PLB"," 75"))
221+
egg::ggarrange(
222+
(probplot+
205223
theme(axis.title.x.bottom = element_blank(),
206224
axis.text.x.bottom = element_blank(),
207-
axis.ticks.x = element_blank() ) )/exposureplot &
208-
plot_layout(heights=c(1,0.6))
225+
axis.ticks.x = element_blank(),
226+
plot.margin = unit(c(0,0,0,0), "cm"))) ,
227+
(exposureplot +
228+
theme(plot.margin = unit(c(0,0,0,0), "cm") )) ,heights = c(1,0.5))
229+
230+
209231
210232
211233
```
234+
This figure presents the relationship between the AUC and probability of Cure. The AUCs has been binned into placebo and quartiles. For the placebo and exposure bins, the probability of response was computed and communicated using text of the percentage and a pointinterval showing the 95% CI. The probability by dose level is also shown as diamonds. A fitted logistic regression line is superimposed. In a real data fitting situation, this can constitute a diagnostic plot on how well the proposed logistic fit is adequate to describe the data. The lower part of the figure shows the distributions of AUCs split by Dose level. The numbers constitutes the percent of patients falling into a bin (25% by definition in this plot). This information can become more useful when trying to compare multiple dose levels as illustrated later in this vignette. The number of responders over the total number of patients is shown for each bin using n/N notation. On the bottom axis we communicate the computed bin limits.
212235

236+
## Adding Between Subject Variability on Intercept and Showing its Impact on Probabilities
213237

214238
```{r bsvrangeplot, collapse=TRUE }
215239
@@ -229,7 +253,7 @@ simout <- left_join(simout,simoutbsvref)
229253
simoutbsvlong <- simout %>%
230254
group_by(DOSE)%>%
231255
mutate(P1std=P1/Pstdref) %>%
232-
gather(paramname, paramvalue,P1std)
256+
gather(paramname, paramvalue,P1std,P1)
233257
234258
235259
yvar_names <- c(
@@ -243,7 +267,7 @@ pbsvranges<- ggplot(simoutbsvlong %>%
243267
y = paramname,
244268
fill = factor(..quantile..),
245269
height = ..ndensity..)) +
246-
facet_wrap(paramname~DOSE , scales="free", ncol=1,
270+
facet_wrap(paramname~DOSE , scales="free_y", ncol=1,
247271
labeller=labeller(paramname=yvar_names) ) +
248272
stat_density_ridges(
249273
geom="density_ridges_gradient", calc_ecdf=TRUE,
@@ -262,11 +286,8 @@ pbsvranges<- ggplot(simoutbsvlong %>%
262286
axis.ticks.y = element_blank(),
263287
axis.title.y = element_blank()) +
264288
labs(x="Parameters", y="") +
265-
scale_x_log10() +
266-
coord_cartesian(expand=FALSE)
267-
268-
pbsvranges
269-
289+
scale_x_log10(breaks=c(0.5,0.8,1,1.25,2)) +
290+
coord_cartesian(expand=FALSE,xlim=c(0.2,2))
270291
271292
simoutbsvranges <- simoutbsvlong %>%
272293
group_by(paramname)%>%
@@ -277,11 +298,12 @@ simoutbsvranges <- simoutbsvlong %>%
277298
P75 = quantile(paramvalue, 0.75),
278299
P95 = quantile(paramvalue, 0.95))
279300
simoutbsvranges
301+
pbsvranges
280302
```
303+
The table and associated plot illustrated the impact of between subject variability on the probability of response on regular and standardized scales.
281304

282305
## Computing the Probabilities
283-
Here we show how the odds and probabilities can be computed. We already know that the distribution of AUC depends on the Dose and on the clearance distributions. The model had five parameters shown in <span style="color: red;">red</span>, the dose, disease severity and weight were covariates and are shown in <span style="color: green;">green</span>. A Change in body weight will trigger a change in Clearance which in turn will control the AUC. To define an odds ratio we need to define a reference odds with reference covariate values Severity = 0 and changes in covariate values for example Severity = 1 (everything else being equal). For nonlinear relationships, in addition to the covariate unit change e.g. 25 mg change of dose it is important to define what reference value we are using e.g. A change from Placebo = 0 mg to 25 mg is not the same as a change from the typical dose of 75 mg increasing it to 100 mg.
284-
306+
Next, we show how the odds and probabilities can be computed while varying the covariate values. We already know that the distribution of AUC depends on the Dose and on the clearance distributions. The model had five parameters shown in <span style="color: red;">red</span>, the dose, disease severity and weight were covariates and are shown in <span style="color: green;">green</span>. A Change in body weight will trigger a change in Clearance which in turn will change the AUC. First, we define a reference odds with reference covariate values Severity = 0, Weight = 70 and DOSE = 75. We then vary each covariate keep all the other covariate at reference. For nonlinear relationships (emax), in addition to the covariate unit change e.g. 25 mg change of dose it is important to define what reference value we are using e.g. A change from Placebo = 0 mg to 25 mg is not the same as a change from the typical dose of 75 mg increasing it to 100 mg.
285307

286308

287309
```{r, collapse=TRUE }
@@ -334,7 +356,6 @@ for(i in 1:nsim) {
334356
}
335357
```
336358

337-
338359
```{r, collapse=TRUE }
339360
340361
wt.labs <- c("weight: 50 kg","weight: 60 kg","weight: 70 kg","weight: 80 kg","weight: 90 kg","(all)")
@@ -353,6 +374,7 @@ stdprobplot<- ggplot(iter_sims, aes(DOSE,P1,col=factor(SEV) ) )+
353374
labs(y="Probability of Response", colour="Severity")
354375
stdprobplot
355376
```
377+
The above figure show the raw simulated probabilities split by covariate values. In the next section we show how we can standardize and summarize the probabilities.
356378

357379

358380
```{r, fig.height= 7, collapse=TRUE }
@@ -411,6 +433,7 @@ ggplot(iter_sims,aes(x=paramvalue,y=covvalue))+
411433
theme_bw()+
412434
theme(axis.title = element_blank(),strip.placement = "outside")
413435
```
436+
From the distributions plot we can appreciate that severity has the biggest impact on the probability of response followed by Dose which an effect that is saturating above 125 mg. Next, we prepare the data and present it using a forest plot.
414437

415438
```{r plot3, collapse=TRUE }
416439
coveffectsdatacovrep <- iter_sims %>%
@@ -465,6 +488,10 @@ forest_plot(coveffectsdatacovrepbsv,
465488
show_ref_value=TRUE,
466489
ref_legend_text = ref_legend_text,
467490
plot_table_ratio = 2,
491+
interval_shape = "circle small",
492+
bsv_shape = "triangle",
493+
legend_order = c("pointinterval","shape","area","ref"),
494+
combine_interval_shape_legend = TRUE,
468495
base_size = 12,
469496
table_text_size = 4,
470497
y_label_text_size = 12,
@@ -483,12 +510,12 @@ dev.off()
483510
![Covariate Effects Plot.](./Figure_8_4.png)
484511

485512
## Dose/Exposure Response Curves
486-
It is more common to construct a full range (every possible combination) of dose levels and covariate values simulations when visualizing dose/exposure/response curves. In this section we show how to construct such a curve:
513+
It is more common to construct a full range (every possible combination of dose levels and covariate values) simulations when visualizing dose/exposure/response curves. In this section we show how to construct such a curve:
487514

488515
```{r plot4, collapse=TRUE, fig.height= 6, fig.width= 12}
489516
covcombdr <- expand.grid(
490517
WT = c(50,70,90),
491-
DOSE = c(0,25,75,150),
518+
DOSE = c(0,25,50,75,100,150),
492519
SEV = c(0,1)
493520
)
494521
@@ -599,6 +626,16 @@ simoutdataprobs <- iter_sims %>%
599626
n1= length(DV[DV==1]),
600627
medAUC= median(AUC))
601628
629+
simoutdataprobsdose <- iter_sims %>%
630+
group_by(PLACEBO, WT,SEV,DOSE) %>%
631+
summarise(probs = mean(DV),
632+
n = n(),
633+
lower = probs - qnorm(1-0.05/2)*sqrt((1/n)*probs*(1-probs)),
634+
upper = probs + qnorm(1-0.05/2)*sqrt((1/n)*probs*(1-probs)),
635+
n0 = length(DV[DV==0]),
636+
n1= length(DV[DV==1]),
637+
medAUC= median(AUC))
638+
602639
603640
SEV_names <- c(
604641
'0'="Not Severe",
@@ -637,14 +674,13 @@ a <- ggplot(data=data.frame(iter_sims),aes(AUC, P1,
637674
strip.background.x = element_blank(),
638675
strip.text.x = element_blank())+
639676
labs(x = "AUC", y = "DOSE (mg)")
640-
a/b &
641-
plot_layout(heights=c(1,0.6))
677+
egg::ggarrange(a,b,heights=c(1,0.6))
642678
643679
```
644680
Finally, we color by dose level and compute percent of the AUC distribution by dose level that is within the overall AUC tertiles.
645681

646682

647-
```{r plot667, collapse=TRUE, include=TRUE, fig.height= 12, fig.width= 12}
683+
```{r plot667, collapse=TRUE, include=TRUE, fig.height= 16, fig.width= 12}
648684
649685
percentineachbreakcategory <- iter_sims %>%
650686
mutate(AUC_bins=table1::eqcut(AUC,3,withhold = list(PLACEBO=AUC==0))) %>%
@@ -658,22 +694,27 @@ percentineachbreakcategory <- iter_sims %>%
658694
659695
probplot<- ggplot(iter_sims , aes(AUC,DV)) +
660696
facet_grid(SEV ~WT,labeller=labeller(WT=wt.labs,SEV=SEV_names))+
697+
geom_smooth(data=iter_sims %>%
698+
filter(WT==70,SEV ==0) %>%
699+
mutate(SEV=NULL,WT=NULL),method = "glm",
700+
method.args = list(family = "binomial"),
701+
se = TRUE,color="white",fill="lightgray",alpha=0.3) +
661702
geom_vline(xintercept = vlines)+
662703
geom_point(position=position_jitter(height=0.02,width=0.1),size=1,alpha=0.2,
663704
aes(colour=factor(DOSE)))+
664705
geom_smooth(method = "glm",
665706
method.args = list(family = "binomial"),
666-
se = TRUE,color="black",fill="gray",
707+
se = TRUE,color="black",fill="blue",alpha=0.5,
667708
aes(linetype=as.factor(SEV))) +
668709
geom_pointrange(data=simoutdataprobs,aes(medAUC,probs,ymin=lower,ymax=upper),
669710
inherit.aes = TRUE,alpha=0.5)+
670711
geom_text(data=simoutdataprobs, aes(x=medAUC,y=probs,
671712
label=paste(paste(round(100*probs,1),"%")
672713
#, paste(n1,n,sep = "/"),sep = "\n"
673-
)
674-
675-
),
714+
)),
676715
inherit.aes = TRUE,hjust=-0.15,vjust=-0.15,size=4)+
716+
geom_point(data=simoutdataprobsdose, aes(x=medAUC,y=probs,colour=factor(DOSE)),
717+
inherit.aes = FALSE,shape="diamond",size=4,alpha=0.3)+
677718
theme_bw(base_size = 16)+
678719
theme(legend.position = "top")+
679720
labs(color="Dose (mg)",y="Probability of Response",
@@ -704,11 +745,13 @@ explot<- ggplot(iter_sims,aes(y = as.factor(DOSE),x = AUC,
704745
plot.background = element_blank())+
705746
labs(x = "AUC", y = "DOSE (mg)")
706747
707-
(probplot +
748+
egg::ggarrange(probplot +
708749
theme(axis.title.x.bottom = element_blank(),
709750
axis.text.x.bottom = element_blank(),
710-
axis.ticks.x = element_blank() ) )/explot &
711-
plot_layout(heights=c(1,0.8))
751+
axis.ticks.x = element_blank() ) , explot,
752+
heights=c(1,2))
753+
754+
712755
```
713756

714757

0 commit comments

Comments
 (0)