-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMCP-Mod.Rmd
504 lines (375 loc) · 14 KB
/
MCP-Mod.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
---
title: "MCP-Mod"
author: "liuc"
date: "2023-05-25"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## MCP-Mod
> https://www.fda.gov/media/99313/download
> https://cran.r-project.org/web/packages/DoseFinding/vignettes/faq.html
> https://cran.r-project.org/web/packages/DoseFinding/vignettes/analysis_normal.html
> https://blog.statsols.com/mcp-mod
> https://www.lexjansen.com/pharmasug-cn/2019/CC/Pharmasug-China-2019-CC26.pdf
MCP-Mod (Multiple Comparisons Procedure - Modelling) is an increasingly popular statistical methodology. It can generate superior statistical evidence from Phase II trials with regards to dose selection. The FDA & EMA both recently approved this as fit-for-purpose (FFP).
It is noteworthy that the power of the MCP-Mod method was shown to be sensitive to the initial ‘guesses’ of model shapes. A minimum of 2 doses in addition to placebo is proposed but more than the minimum is desired.
Minimum: 2 active doses (for the MCP-step), 3 active doses (Mod step);
Recommendations (rules of thumb): 4-7 active doses, >10-fold dose range;
To gain most information on the compound, one should evaluate a dose-range that is as large as feasible in terms of lowest and highest dose. As a rule of thumb at minimum a dose-range of > 10-fold should be investigated (i.e., the ratio of highest versus lowest dose should be > 10).
*一般的理解:*
一个二期临床试验,一般的目的在于:
1. Establish that the drug works as intended
2. Determine the appropriate doses for Phase III testing
MCP-Mod方法,可以通过多重比对的方法,evaluate dose-response的信号是否存在(传统上多采用趋势检验、Bonferroni检验);
然后用最优的模型去拟合一个参数分布,找到对与III期而言最好的dose,或者最efficacy的dose。
MCP: Rust, 但是受限于dose as nominal变量;
Mod: 灵活,但是受限于模型的选择;
而,MCP-Mod将此二者结合起来,一般的,其分析过程分为三个阶段:1,Design Stage,选择备选的模型,并为其的参数设置一个初始值
,以及生成optimal test;2,MCP Stage,选择最优模型,合适的contrasts,是否存在dose response这一趋势;3,Mod Stage,构建最优模型并find target dose.
*MCP-Mod trial design: *
首先是一般的二期试验的设计内容,包括受试人群的选择、研究终点的设置等等;依据相似化合物、mode of action等选择预设的 dose-response 曲线模型,优化模型shape;样本量计算、dose determination;然后一句trail得到的数据进行MCP-Mod分析。
- Define a suitable study population to represent the underlying true dose-response shape.
- Pre-Specify the candidate dose response models based on available information. These are based on assessment of relevant metrics such as type I error rate, power to detect a significant dose-response shape, and the power to find the minimal effective dose.
- Dose determination and sample size calculation to achieve targeted performance characteristics.
```{r, include=FALSE}
library(tidyverse)
library(DoseFinding)
```
### 官网示例,小小的模仿一下
```{r}
data(IBScovars)
str(IBScovars)
```
#### 第一步:
确定模型和猜测对应的模型的参数。
对于placebo的Eff值,可以考虑通过散点图查看其的中位值,或是均值来确定。
而对于每一个函数的参数的猜测,一般的:
- 对于Emax模型
```{r}
ggplot(IBScovars, aes(factor(dose), resp)) +
geom_boxplot() +
geom_jitter() +
labs(title = "resp by dose (jittered horizontally)") +
xlab("dose [μg]") + ylab("response [l]")
```
```{r}
# The function “guesst” will pass the parameters of each model to function “Mods”, which will then produce a list that contains all information about candidate models.
?guesst
quad <- guesst(d = 1, p = 0.9, model = 'quadratic')
```
```{r}
## ?Mods
## perform (model based) multiple contrast test
## define candidate dose-response shapes
## 首先是设定你所欲考虑的模型。这里的问题在于the guesstimates for the theta2 parameters的值怎么去猜测?
## 还有关于placebo的部分需要注意,其默认的;两个值为0,1, placEff=0, maxEff=1
models <- Mods(linear = NULL,
linlog = NULL,
emax = c(0.2, 1),
quadratic = -0.17,
doses = c(0, 1, 2, 3, 4) # 需要加上placebo,也就是0
)
## plot models
## It’s always a good idea to perform a visual sanity check of the functional relationships implied by the guesstimates.
plotMods(models)
```
Calculate optimal contrasts
```{r}
optC <- optContr(models, w=1)
print(optC)
plot(optC)
```
#### 第二步:
进行多重比对检验。Detect a significant dose response signal using a trend test.
Note that the type parameter defaults to type="normal", which means that we assume a homoscedastic ANOVA model for resp.
Model-specific contrast tests with null hypotheses of a flat dose-response curve is performed by 'MCTtest' function.
```{r}
## perform multiple contrast test
## functions powMCT and sampSizeMCT provide tools for sample size
## calculation for multiple contrast tests
## 对所定义的每个模型都做一遍多重比对检验
## 对于此例而言,依照t值来说emax模型似乎拟合的最好。
test <- MCTtest(dose = dose, resp = resp, data = IBScovars,
models=models,# 上文定义的模型
addCovars = ~ gender,# 协变量
type = 'normal'
)
test
```
*general test*
Generalization to non-normal data
```{r}
# fit a anova
fitlm <- lm(resp ~ factor(dose) - 1, data = IBScovars)
# get anova results
mu_hat <- coef(fitlm)
S_hat <- vcov(fitlm)
anova_df <- fitlm$df.residual
doses <- c(0,1,2,3,4)
test_general <- MCTtest(dose = doses,
resp = mu_hat,
S = S_hat,
df = anova_df,
models = models,
type = "general"
)
print(test_general)
```
#### 第三步:
下面选择Emax模型进行拟合,最小二乘法,Dose treated as continuous variable.
```{r}
## ?fitMod
fitemax <- fitMod(dose = dose, resp = resp, data=IBScovars,
model="emax",
bnds = c(0.01,5)# upper and lower bound
)
## display fitted dose-effect curve
plot(fitemax, CI=TRUE, plotData="meansCI",level=0.95)
```
*interpret: *从图上的结果来看的话dose等于2的时候,resp就可以了。
```{r}
fitemax |> summary()
```
*The `MCPMod` function simultaneously conducts both steps. *
```{r}
set.seed(42, "L'Ecuyer")
res <- DoseFinding::MCPMod(dose = doses, resp = mu_hat,
# data = IBScovars,
S = S_hat,
models = models,
type = 'general',
critV = TRUE,
alpha = 0.05,
alternative = 'two.sided',
Delta = 1,
selModel = 'AIC'
)
res$selMod
```
```{r}
res$doseEst
```
```{r}
doseVec <- seq(1,10,by=1)
pred <- predict(fitemax, se.fit = TRUE, predType = 'ls-means')
pred |> str()
```
### kx826 1002 实战
很明显的,本二期临床试验采用的是传统的pairwise comparisons方法衡量的dose,而且其所设置的dose剃度仅为2+1个。 有一些模型不是很适合。
对于MMRM类的数据,似乎可以通过MMRM分析,比如R包`mmrm::mmrm`或SAS`PROC Mixed`提取对应的协方差矩阵等。
```{r, include=FALSE}
final_data_wider <- read_csv(
'~/OneDrive/kintor/Daily_Work/KX826_CN1002/final_data_wider.csv') %>%
mutate(dislevel = case_when(
dislevel == 'IIIvertex' ~ 'III',
dislevel == 'Ⅴ级' ~ 'V',
dislevel == 'Ⅳ级' ~ 'IV'
))
```
```{r}
demo <- final_data_wider %>%
filter(arm %in% c('placeboBID', '2.5mgBID','5mgBID')) %>%
mutate(resp = dW24, dose = case_when(
arm == "placeboBID" ~ 0,
arm == "2.5mgBID" ~ 2.5,
arm == "5mgBID" ~ 5,
TRUE ~ NA_integer_
)) %>%
drop_na(resp) %>% as.data.frame()
demo
```
```{r}
ggplot(demo, aes(factor(dose), resp)) +
geom_boxplot() +
geom_jitter() +
labs(title = "resp by dose (jittered horizontally)") +
xlab("dose [μg]") + ylab("response [chg]")
```
```{r}
quad <- guesst(d = 3, p = 0.9, model = 'quadratic')
exp <- guesst(d = 3, p = 0.9, model = 'exponential', Maxd = 3)
emax <- guesst(d = 3, p = 0.9, model = 'emax')
sigemax <- guesst(d = c(2.5, 4), p = c(0.3, 0.9), model = 'sigEmax')
logis <- guesst(d = c(2.5, 4), p = c(0.3, 0.9), model = 'logistic')
```
```{r}
mods <- Mods(
linear = NULL,
linlog = NULL,
emax = c(emax, 2.5),
quadratic = quad,
exponential = exp,
# sigEmax = sigemax,
# logistic = logis,
doses = c(0, 2.5, 5)
)
plot(mods)
```
```{r}
optC <- optContr(mods, w=1)
print(optC)
plot(optC)
```
```{r}
test <- MCTtest(dose = dose, resp = resp, data = demo,
models=mods,
# addCovars = ~ gender,# 协变量
type = 'normal'
)
test
```
*结果不妙:*从设定好的p值来看的话,不具有计量的量效关系。
```{r}
fit_best <- fitMod(dose = dose, resp = resp, data=demo,
model="exponential"
)
## display fitted dose-effect curve
plot(fit_best, CI=TRUE, plotData="meansCI",level=0.95)
```
```{r}
# fit an anova
fitlm <- lm(resp ~ factor(dose) - 1, data = demo)
# get anova results
mu_hat <- coef(fitlm)
S_hat <- vcov(fitlm)
anova_df <- fitlm$df.residual
doses <- c(0, 2.5, 5)
test_general <- MCTtest(dose = doses,
resp = mu_hat,
S = S_hat,
df = anova_df,
models = mods,
type = "general"
)
print(test_general)
```
```{r}
set.seed(42, "L'Ecuyer")
res <- DoseFinding::MCPMod(dose = doses, resp = mu_hat,
# data = IBScovars,
S = S_hat,
models = mods,
type = 'general',
critV = TRUE,
alpha = 0.025,
alternative = 'one.sided',
Delta = 1,
selModel = 'AIC'
)
res$selMod
```
### kx826 cn1004
```{r}
fake_data <- read_csv(
'~/OneDrive/kintor/Daily_Work/KX826_CN1004/data_wider.csv'
) %>%
filter(ARM %in% c('placeboQD','2.5mgQD','5mgQD')) %>%
# filter(ARM %in% c('placeboBID','2.5mgBID','5mgBID')) %>%
mutate(resp = dW24,
dose = case_when(ARM == "placeboQD" ~ 0,
ARM == "2.5mgQD" ~ 2.5,
ARM == "5mgQD" ~ 5,
TRUE ~ NA_integer_),
# dose = case_when(ARM == "placeboBID" ~ 0,
# ARM == "2.5mgBID" ~ 2.5,
# ARM == "5mgBID" ~ 5,
# TRUE ~ NA_integer_)
) %>% drop_na(resp) %>% as.data.frame()
fake_data
```
```{r}
ggplot(fake_data, aes(factor(dose), resp)) +
geom_boxplot() +
geom_jitter() +
labs(title = "resp by dose (jittered horizontally)") +
xlab("dose [μg]") + ylab("response [chg]")
```
```{r}
quad <- guesst(d = c(2.5, 5), p = 0.9, model = 'quadratic')
exp <- guesst(d = 3, p = 0.9, model = 'exponential', Maxd = 3)
emax <- guesst(d = 3, p = 0.9, model = 'emax')
sigemax <- guesst(d = c(2.5, 4), p = c(0.3, 0.9), model = 'sigEmax')
logis <- guesst(d = c(2.5, 4), p = c(0.3, 0.9), model = 'logistic')
mods <- Mods(
linear = NULL,
linlog = NULL,
emax = c(emax, 2.5),
quadratic = quad,
exponential = exp,
# sigEmax = sigemax,
# logistic = logis,
doses = c(0, 2.5, 5)
)
plot(mods)
```
```{r}
test <- MCTtest(dose = dose, resp = resp, data = fake_data,
models=mods,
# addCovars = ~ gender,# 协变量
type = 'normal'
)
test
```
拟合最优模型:
```{r}
fit_best <- fitMod(dose = dose, resp = resp, data=fake_data,
model="exponential"
)
## display fitted dose-effect curve
plot(fit_best, CI=TRUE, plotData="meansCI",level=0.95)
```
*use general: *
```{r}
# fit an anova
fitlm <- lm(resp ~ factor(dose) - 1, data = fake_data)
# get anova results
mu_hat <- coef(fitlm)
S_hat <- vcov(fitlm)
anova_df <- fitlm$df.residual
doses <- c(0, 2.5, 5)
test_general <- MCTtest(dose = doses,
resp = mu_hat,
S = S_hat,
df = anova_df,
models = mods,
type = "general"
)
print(test_general)
```
```{r}
set.seed(42, "L'Ecuyer")
res <- DoseFinding::MCPMod(dose = doses, resp = mu_hat,
# data = IBScovars,
S = S_hat,
models = mods,
type = 'general',
critV = TRUE,
alpha = 0.025,
alternative = 'one.sided',
Delta = 1,
selModel = 'maxT'
)
res$selMod
```
```{r}
res$doseEst
```
```{r}
DoseFinding::ED(fit_best, p = 0.9)
DoseFinding::TD(fit_best)
```
所选出的最佳剂量反应曲线模型,可通过试验结果数据估计模型的参数,进而由逆回归方法推估感兴趣的剂量:
inverse regression equation
```{r}
# 也即是通过Y的值去推测X的值
# 以下对线形模型适用,但对此模型似乎不行
# xval <- approx(x = fit_best$fitted, y = x, xout = 9.1)$y
# points(xval, 9.1, col = "blue", lwd = 5)
ypredict <- 9.1
fun <- function(x1, y1, mod) {
predict(mod, newdata=data.frame(days=x1)) - y1
}
(xpred <- sapply(ypredict, function(z) uniroot(fun, interval=c(0, 500), mod=fit_best, y1=z)$root))
```