## Last updated: 2021-11-11 14:40
library(rms)
Let’s write a function to simulate data from \(Normal(\mu_1,\sigma_1)\) and \(Normal(\mu_2,\sigma_2)\) and conduct a t-test for \[\begin{align*} H_0: \mu_1 = \mu_2\\ H_1: \mu_1 > \mu_2 \end{align*}\]
Inputs are n1, mu1, sig1, n2, mu2, sig2, and B. (n1 and n2 are the sample sizes, and B is the simulation size.)
Output is the p-value.
<- function(n1, mu1, sig1, n2=n1, mu2, sig2, B){
sim1a # n1 and n2 are sample sizes.
# two-sample t-test, one-sided alternative.
<- NULL
pv for(i in 1:B){ ## or seq_len(B)
## Generate data using rnorm().
<- rnorm(n1,mu1,sig1)
x1 <- rnorm(n2,mu2,sig2)
x2 ## Conduct t-test using t.test(... ..., alt='greater').
## Retain the p-value.
<- t.test(x1,x2, alt='greater')$p.value
out <- append(pv, out)
pv
}
pv }
Let’s assume \(\sigma_1 = \sigma_2 = 20\), \(\mu_1=\mu_2=100\) under \(H_0\) and \(\mu_1=105\), \(\mu_2=100\) under \(H_1\).
<- sim1a(n1=120, mu1=100, sig1=20, mu2=100, sig2=20, B=1000)
sim1aNul <- sim1a(n1=120, mu1=105, sig1=20, mu2=100, sig2=20, B=1000) sim1aAlt
To compute type I error rate and power, we can
table( sim1aNul < 0.025 )
##
## FALSE TRUE
## 983 17
table( sim1aAlt < 0.025 )
##
## FALSE TRUE
## 542 458
power.t.test(n=120, delta=5, sd=20, power=NULL)
##
## Two-sample t test power calculation
##
## n = 120
## delta = 5
## sd = 20
## sig.level = 0.05
## power = 0.48752
## alternative = two.sided
##
## NOTE: n is number in *each* group
Question 1
How many simulations do we need to run?
<- 1000
simSize binconf(x=0.025*simSize, n=simSize, method='all')
## PointEst Lower Upper
## Exact 0.025 0.0162425 0.0366848
## Wilson 0.025 0.0169901 0.0366453
## Asymptotic 0.025 0.0153235 0.0346765
We can use the width of a confidence interval to derive the necessary sample size (simulation size).
\[\begin{align*} \hat{p} \pm 1.96 \frac{\sqrt{\hat{p}(1-\hat{p})}}{\sqrt{B}} \end{align*}\]
If we want the width to be shorter than \(w\), then \[\begin{align*} (2^2)(1.96^2)(0.5^2) &< w^2B\\ 3.8416/w^2 &< B \end{align*}\] for \(w=0.01\), \(B > 38416\).
<- 38416
simSize binconf(x=0.025*simSize, n=simSize, method='all')
## PointEst Lower Upper
## Exact 0.025 0.0234617 0.0266106
## Wilson 0.025 0.0234856 0.0266094
## Asymptotic 0.025 0.0234388 0.0265612
binconf(x=0.5*simSize, n=simSize, method='all')
## PointEst Lower Upper
## Exact 0.5 0.494987 0.505013
## Wilson 0.5 0.495000 0.505000
## Asymptotic 0.5 0.495000 0.505000
Question 2
What’s the sample size for power = 90%?
<- sim1a(n1=240, mu1=105, sig1=20, mu2=100, sig2=20, B=1000)
sim1bAlt
table(sim1bAlt < 0.025)
##
## FALSE TRUE
## 211 789
For this simple situation, we don’t need to run a simulation.
power.t.test(n=NULL, delta=5, sd=20, power=0.90)
##
## Two-sample t test power calculation
##
## n = 337.201
## delta = 5
## sd = 20
## sig.level = 0.05
## power = 0.9
## alternative = two.sided
##
## NOTE: n is number in *each* group
<- sim1a(n1=340, mu1=105, sig1=20, mu2=100, sig2=20, B=1000)
sim1cAlt
table(sim1cAlt < 0.025)
##
## FALSE TRUE
## 95 905
Question 3 What if we want \(n_1 = 2n_2\)?
<- 340
N1 <- sim1a(n1=N1, mu1=105, sig1=20, n2=N1/2, mu2=100, sig2=20, B=1000)
sim1dAlt
table(sim1dAlt < 0.025)
##
## FALSE TRUE
## 224 776
<- 400
N1 <- sim1a(n1=N1, mu1=105, sig1=20, n2=N1/2, mu2=100, sig2=20, B=1000)
sim1eAlt
table(sim1eAlt < 0.025)
##
## FALSE TRUE
## 179 821
To maintain the same power, we need the harmonic mean of \(n_1\) and \(n_2\) to be \(340\). \[\begin{align*} \frac{2}{\frac{1}{2n_2}+\frac{1}{n_2}} = 340 \end{align*}\]
<- function(v) 1/mean(1/v)
harmonic.mean
harmonic.mean( c(400,200))
## [1] 266.667
<- function(n2) harmonic.mean(c(2*n2, n2))
hm2 <- function(n2) hm2(n2)-340
f uniroot(f=f, lower=0, upper=1000)
## $root
## [1] 255
##
## $f.root
## [1] 0
##
## $iter
## [1] 1
##
## $init.it
## [1] NA
##
## $estim.prec
## [1] 745
Thus, \(n_2=255\) and \(n_1=255\times 2=510\).
<- 510
N1 <- sim1a(n1=N1, mu1=105, sig1=20, n2=N1/2, mu2=100, sig2=20, B=1000)
sim1fAlt
table(sim1fAlt < 0.025)
##
## FALSE TRUE
## 100 900
Sometimes, you may want to repeat the simulation. It is possible by using the same random seed.
<- function(n1, mu1, sig1, n2=n1, mu2, sig2, B, SEED=NULL){
sim2a # n1 and n2 are sample sizes.
# two-sample t-test, one-sided alternative.
<- NULL
pv if(is.null(SEED)) SEED <- sample(1:100000, 1)
set.seed(SEED)
for(i in 1:B){ ## or seq_len(B)
## Generate data using rnorm().
<- rnorm(n1,mu1,sig1)
x1 <- rnorm(n2,mu2,sig2)
x2 ## Conduct t-test using t.test(... ..., alt='greater').
## Retain the p-value.
<- t.test(x1,x2, alt='greater')$p.value
out <- append(pv, out)
pv
}list(pv, SEED)
}
<- 510
N1 <- sim2a(n1=N1, mu1=105, sig1=20, n2=N1/2, mu2=100, sig2=20, B=1000, SEED=2525)
sim2aAlt
table( sim2aAlt[[1]] < 0.025)
##
## FALSE TRUE
## 107 893
<- sim2a(n1=N1, mu1=105, sig1=20, n2=N1/2, mu2=100, sig2=20, B=1000, SEED=2525)
sim2bAlt
table( sim2bAlt[[1]] < 0.025)
##
## FALSE TRUE
## 107 893
Practice: Let’s make the following changes to sim2a function.
<- function(n1, mu1, sig1, n2=n1, mu2, sig2, B, SEED=NULL){
sim3a # n1 and n2 are sample sizes.
# two-sample t-test, one-sided alternative.
<- pv.w <- seed <- NULL
pv.t if(is.null(SEED)) SEED <- sample(1:100000, 1)
set.seed(SEED)
<- sample(1:100000, B, replace=FALSE)
seed for(i in 1:B){ ## or seq_len(B)
## Generate data using rnorm().
set.seed(seed[i])
<- round( rnorm(n1,mu1,sig1) )
x1 <- round( rnorm(n2,mu2,sig2) )
x2 ## Conduct t-test using t.test(... ..., alt='greater').
## Retain the p-value.
<- t.test(x1,x2, alt='greater')$p.value
out.t <- suppressWarnings(wilcox.test(x1,x2, alt='greater')$p.value)
out.w <- append(pv.t, out.t)
pv.t <- append(pv.w, out.w)
pv.w
}data.frame(pv.t, pv.w, seed)
}
<- 10
N1 <- sim3a(n1=N1, mu1=100, sig1=20, n2=N1/2, mu2=100, sig2=20, B=1000, SEED=2525) sim3aNul
Let’s compare the p-values of t-test and Wilcoxon rank sum test.
plot(0, xlim=c(0,0.1), ylim=c(0,0.1), xlab='t test', ylab='Wilcoxon test',
panel.first=grid(nx=NULL,ny=NULL), type='n')
abline(0,1, col=grey(0.9))
points(sim3aNul$pv.t, sim3aNul$pv.w, pch=19, cex=0.9, col='#77777788')
abline(h=0.025, col=grey(0.6))
abline(v=0.025, col=grey(0.6))
Let’s identify the two data sets that produced very different p-values between t-test and Wilcoxon rank sum test.
<- which((sim3aNul$pv.t < 0.025 & sim3aNul$pv.w > 0.05) | (sim3aNul$pv.t > 0.05 & sim3aNul$pv.w < 0.025)) ) ( check
## [1] 513 915
sim3aNul[check,]
## pv.t pv.w seed
## 513 0.0198693 0.0613689 44925
## 915 0.0566123 0.0158910 25793
Because we know the random seeds that produced these data, we can investigate the data further.
set.seed(sim3aNul$seed[check[1]])
<- round( rnorm(n=10,mean=100,sd=20) )
x1 <- round( rnorm(n= 5,mean=100,sd=20) )
x2 <- t.test(x1,x2, alt='greater')$p.value
pvt1 <- suppressWarnings( wilcox.test(x1,x2, alt='greater')$p.value )
pvw1
list(x1=sort(x1), x2=sort(x2), p.val=c(pvt1,pvw1))
## $x1
## [1] 93 93 95 99 101 104 116 118 118 119
##
## $x2
## [1] 84 93 95 95 105
##
## $p.val
## [1] 0.0198693 0.0613689
set.seed(sim3aNul$seed[check[2]])
<- round( rnorm(n=10,mean=100,sd=20) )
y1 <- round( rnorm(n= 5,mean=100,sd=20) )
y2 <- t.test(y1,y2, alt='greater')$p.value
pvt2 <- suppressWarnings( wilcox.test(y1,y2, alt='greater')$p.value )
pvw2
list(x1=sort(y1), x2=sort(y2), p.val=c(pvt2,pvw2))
## $x1
## [1] 93 97 99 105 109 109 110 114 114 126
##
## $x2
## [1] 47 92 94 100 102
##
## $p.val
## [1] 0.0566123 0.0158910
Question: If we use a simple randomization, how much discrepancy is expected if N=20?
<- function(N,B, SEED=NA){
simple.rand if(is.na(SEED)) SEED <- sample(1:100000,1)
set.seed(SEED)
<- matrix(
dat sample(c(-1,1),N*B,rep=TRUE), ncol=N
)abs(apply(dat, 1, sum))
}
<- simple.rand(N=20, B=10000, SEED=NA)
sr
table(sr)
## sr
## 0 2 4 6 8 10 12 14 16
## 1761 3218 2379 1473 761 290 90 23 5
Compare the result with \(Binomial(20, 0.5)\)
table(sr) / length(sr)
## sr
## 0 2 4 6 8 10 12 14 16
## 0.1761 0.3218 0.2379 0.1473 0.0761 0.0290 0.0090 0.0023 0.0005
<- seq(0,12,2) ## discrepancy
disc <- dbinom(10,20,0.5)
p0 <- dbinom(9:4,20,0.5) * 2
p1 data.frame(disc, p=c(p0,p1))
## disc p
## 1 0 0.1761971
## 2 2 0.3203583
## 3 4 0.2402687
## 4 6 0.1478577
## 5 8 0.0739288
## 6 10 0.0295715
## 7 12 0.0092411
Question: If we use a simple randomization (N=200) and force even split (100/100), what is the biggest discrepancy in the entire process?
<- function(N=200, B=1000, SEED=NA){
evenSplit <- numeric(B)
max.disc if(is.na(SEED)) SEED <- sample(1:100000,1)
set.seed(SEED)
<- sample(1:100000, B, rep=FALSE)
each.seed for(i in seq_len(B)){
set.seed(each.seed[i])
<- sample( rep(c(-1,1),N/2), N, rep=FALSE )
dat <- max(abs(cumsum(dat)))
max.disc[i]
} cbind(max.disc, each.seed)
}
<- evenSplit(N=200, B=1000, SEED=747)
eS
table(eS[,1])
##
## 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
## 6 29 61 98 112 103 98 94 102 79 56 48 40 21 25 12 3 1 5 4
## 25 27
## 2 1
<- 200
N set.seed(58579)
<- sample( rep(c(-1,1),N/2), N, rep=FALSE )
dat
plot(cumsum(dat), ylab='Discrepancy', type='l')
Question: If we use a simple randomization (N=200) and force even split (100/100), what is the longest assignment to the same group?
## Hint
<- function(N=200, B=1000, SEED=NA){
RunHT <- numeric(B)
max.runs if(is.na(SEED)) SEED <- sample(1:100000,1)
set.seed(SEED)
<- sample(1:100000, B, rep=FALSE)
each.seed for(i in seq_len(B)){
set.seed(each.seed[i])
<- sample( rep(c(-1,1),N/2), N, rep=FALSE )
dat <- max(rle(dat)$lengths)
max.runs[i]
} cbind(max.runs, each.seed)
}
.1 <- RunHT(N=200, B=1000, SEED=113)
runspar(lend=1)
plot( table(runs.1[,1]), lwd=10, ylab='Longest run', yaxs='i')
An R program for block randomization.
<- function(N=400, SEED=NULL){
withinStrat ## For each stratum, generate a sequence of length N.
if(!length(SEED) %in% c(0,2)) stop('SEED must be NULL or of length 2.')
if(is.null(SEED)) SEED <- sample(1:10000, 2, FALSE)
set.seed(SEED[1])
<- sample(2*(1:3), N/2, replace=TRUE, prob=c(0.2,0.6,0.2))
Block.Size <- min(which(cumsum(Block.Size) >= N))
includ <- Block.Size[1:includ]
Block.Size
<- NULL
out set.seed(SEED[2])
for(i in 1:length(Block.Size)){
<- c(out, sample(rep(c('A','B'), Block.Size[i]/2), Block.Size[i], rep=FALSE))
out
}<- rep(1:length(Block.Size), Block.Size)
block.id <- rep(Block.Size, Block.Size)
block.size
<- data.frame(Block_Id=block.id, Block_Size=block.size, Treatment_Arm=out, Rand_Seq=1:length(out))
tab list(tab, SEED)
}
It is generally believed that a chi-square test is not appropriate when some cells are small because it is based on a large-sample theory. Let’s conduct a simulation study to compare chisq.test with and without Yate’s correction, and fisher.test.
We will generate data under the null hypothesis of no association and compute frequency of rejection.
<- function(n, px, py, B, SEED=NULL){
chiSqSim ## n is total sample size.
## px = P[X1=x1] (marginal probability 1)
## py = P[Y1=y1] (marginal probability 2)
## px and py are length 1.
## B is the number of simulations.
## n <- 100 ; px <- 0.2 ; py <- 0.1 ; B <- 10 ; SEED <- NULL
<- suppressWarnings
sw <- outer(c(px,1-px), c(py,1-py), FUN='*')
p4 if(is.null(SEED)) SEED <- sample(1:10000,1)
set.seed(SEED)
<- rmultinom(B, size=n, prob=p4)
d <- function(v){
three.tests <- cbind(v[1:2],v[3:4])
m <- fisher.test(m)$p.val
p.fish <- sw(chisq.test(m, correct=FALSE))$p.val
p.chsq <- sw(chisq.test(m, correct=TRUE ))$p.val
p.chcc c(p.fish, p.chsq, p.chcc)
}<- t( apply(d, MARGIN=2, FUN=three.tests) )
out <- data.frame(out)
out names(out) <- c('Fish','Chsq','Chcc')
list(d,out)
}
<- function(css){
css.format ## css is output of chiSqSim.
<- css[[2]]
pva <- css[[1]]
dat <- pva < 0.05
rej <- apply(rej, 2, function(v) sum(v) / length(v))
typeI
typeI
}
<- chiSqSim(n=200, px=0.1, py=0.6, B=1000)
sim4a css.format(sim4a)
## Fish Chsq Chcc
## 0.033 0.040 0.028
<- chiSqSim(n=20, px=0.1, py=0.6, B=1000)
sim4b css.format(sim4b)
## Fish Chsq Chcc
## 0.009 NA NA
<- chiSqSim(n=20, px=0.05, py=0.6, B=1000)
sim4c css.format(sim4c)
## Fish Chsq Chcc
## 0 NA NA
What are these NA’s?
<- function(css){
css.format ## css is output of chiSqSim.
<- css[[2]]
pva <- css[[1]]
dat <- pva < 0.05
rej <- apply(rej, 2, function(v) sum(v, na.rm=TRUE) / length(v))
typeI <- apply(rej, 2, function(v) sum(is.na(v)) / length(v))
Fail list(typeI=typeI, Fail=Fail)
}
css.format(sim4a)
## $typeI
## Fish Chsq Chcc
## 0.033 0.040 0.028
##
## $Fail
## Fish Chsq Chcc
## 0 0 0
css.format(sim4b)
## $typeI
## Fish Chsq Chcc
## 0.009 0.027 0.001
##
## $Fail
## Fish Chsq Chcc
## 0.000 0.124 0.124
css.format(sim4c)
## $typeI
## Fish Chsq Chcc
## 0.00 0.01 0.00
##
## $Fail
## Fish Chsq Chcc
## 0.000 0.377 0.377
Consider the following 3-stage adaptive hypothesis testing strategy: Take a random sample of size \(n_1=20\) from Normal(\(\mu\),\(1\)) and test \(H_0: \mu=0\), \(H_1: \mu>0\) at \(\alpha=0.025\). If \(H_0\) is rejected, the study is finished; if not, take another random sample of size \(n_2=20\) and test again using all \(n_1+n_2\) data. If \(H_0\) is still not rejected, take another random sample of size \(n_3=20\) and test again using all the data. Use Z-test.
Questions
Programming plans
<- function(n1,n2,n3, mu, sig=1, B, SEED=NULL, alp=0.025){
adap3stage ## Three stage design
## Repeat Z > qnorm(1-alp) until H0 is rejected.
# Generate data #
<- matrix(rnorm(n=(n1+n2+n3)*B, mean=mu, sd=sig), ncol=B)
X <- round(X, 1)
X
<- function(v,m,s,n1,n2,n3, alp=0.025){
compute.p.xbar # v is a (n1+n2+n3) by 1 vector of the data
<- function(w,m,s){
compute.xz <- length(w)
n <- mean(w)
xbar <- (xbar-m) / (s/sqrt(n))
zval <- 1-pnorm(zval)
pval c(n,xbar,pval)
}<- cbind(
out compute.xz(v[1:n1],m,s),
compute.xz(v[1:(n1+n2)],m,s),
compute.xz(v[1:(n1+n2+n3)],m,s)
)<- min(c(which(out[3,] < alp),3))
pick.this
out[,pick.this]
}
<- t(apply(X, MARGIN=2, FUN=compute.p.xbar, m=mu,s=sig,n1=n1,n2=n2,n3=n3) )
out <- data.frame(out)
out names(out) <- c('n','z','p')
out
}
<- adap3stage(n1=40,n2=20,n3=20, mu=0, sig=1, B=10000) a1
コントロール群の奏効割合が50%と仮定して、新薬の効果を検定する臨床試験を計画する。
\[\begin{align*} H_0:& p_t = p_c \\ H_1:& p_t > p_c \end{align*}\]
Type I error rate は 2.5%、検出力は新薬の奏効割合が80%の時0.8とする。シンプルな1ステージデザインのサンプルサイズは:
power.prop.test(n=NULL, p1=0.50, p2=0.80, sig.level=0.025, power=0.80, alternative='one.sided')
##
## Two-sample comparison of proportions power calculation
##
## n = 38.48
## p1 = 0.5
## p2 = 0.8
## sig.level = 0.025
## power = 0.8
## alternative = one.sided
##
## NOTE: n is number in *each* group
各群40とする。
各群のサンプルサイズが20の時に一度データを見て、その結果によってサンプルサイズを各群60まで増やせるとする。また中間解析時にp値が0.005未満であったら帰無仮説を棄却して終了とする。
z値を計算するプログラム:
<- function(xC, xT, n, CORRECT=!TRUE){
compute.z ## One-sided x>y (doesn't matter if CORRECT=FALSE)
## p2 > p1 under the alternative.
## same as prop.test( c(x1,x2), c(n,n), correct=CORRECT)$stat
<- xT/n
phatT <- xC/n
phatC <- (xT+xC)/(n+n)
pp <- ifelse(CORRECT, (1/2)*(1/n+1/n), 0)
correction <- (phatT-phatC-correction) / sqrt(pp*(1-pp)*(1/n+1/n))
z %in% c(0,1)] <- 0
z[pp
z }
xcとxtが与えられた場合のp値を計算する。
## n <- 20
<- xt <- 0:20
xc
<- outer(xc,xt, FUN=compute.z, n=20)
Zvalue <- 1-pnorm(Zvalue)
Pvalue
plot(0, type='n', xlim=c(0,20), ylim=c(0,20), xlab='Control', ylab='Treatment')
title(main='Red = "p.value < 0.005"', font.main=3, adj=0)
for(i in 1:21){
<- Pvalue[i,]
pv <- ifelse(pv<0.005, 'red','blue')
cc points(rep(i-1,21), 0:20, col=cc, cex=0.4, pch=19)
}
第1ステージで帰無仮説を棄却しない場合(青い点)、第2ステージで棄却する確率 (conditional power) を計算する。まず、サンプルサイズを変えない場合。(n2=20)
<- function(n1=20, n2=20, n.add=0, pC=0.50, pT=0.80, xC, xT, alp2, CORRECT=FALSE){
CondPow ## xT and xC are the Stage 1 data.
## alp2 is the type I error rate for the second stage (constant)
## Stage 2
<- seq(0, n2+n.add)
yT <- seq(0, n2+n.add)
yC
## Stage 2 p-value
<- outer(xC+yC, xT+yT, FUN=compute.z, n=n1+n2+n.add, CORRECT=CORRECT)
all.pairs <- all.pairs > qnorm(1-alp2)
all.rejec <- rep(yT,each=length(yC))
C.all <- rep(yC,length(yT))
T.all <- data.frame(Con=C.all[t(all.rejec)], Trt=T.all[t(all.rejec)], Z=all.pairs[all.rejec])
REJ $joint <- dbinom(REJ$Con, n2+n.add, pC) * dbinom(REJ$Trt, n2+n.add, pT)
REJ
sum(REJ$joint)
}
例えば、第1ステージのデータが xC=8, xT=10 だったとすると、conditional power は
CondPow(n1=20,n2=20, n.add=0, pC=0.50, pT=0.80, xC=8, xT=10, alp2=0.02)
## [1] 0.311489
サンプルサイズを変えない場合の conditional power.
<- 0:20
xC <- 0:20
xT
## cpf <- function(xC,xT,n1,n2,n.add,pC,pT,alp2) CondPow(n1,n2,n.add,pC,pT,xC,xT,alp2)
## cp <- outer(xC, xT, FUN=cpf, n1=20,n2=20,n.add=0,pC=0.50,pT=0.80,alp2=0.02)
<- outer(xC,xT, '+')
conp for(i in 0:20){
for(j in 0:20){
+1,j+1] <- CondPow(n1=20,n2=20,n.add=0, pC=0.5,pT=0.8, xC=xC[i+1],xT=xT[j+1], alp2=0.02)
conp[i
}
}
plot(0, type='n', xlim=c(0,20), ylim=c(0,20), xlab='Control: Stage 1', ylab='Treatment: Stage 1',
panel.first=grid(nx=0,ny=0))
title(main='Conditional Power (n2=20)', font.main=3, adj=0)
for(i in 0:20){
for(j in 0:20){
text(i,j, round(conp[i+1,j+1]*100), cex=0.8)
} }
例えば Xc=10, Xt=12 の場合、conditional power は 37% しかない。これをサンプルサイズを最大 (n2=40) まで増やすと conditional power は下図のようになる。
<- outer(xC,xT, '+')
conp for(i in 0:20){
for(j in 0:20){
+1,j+1] <- CondPow(n1=20,n2=20,n.add=20, pC=0.5,pT=0.8, xC=xC[i+1],xT=xT[j+1], alp2=0.02)
conp[i
}
}
plot(0, type='n', xlim=c(0,20), ylim=c(0,20), xlab='Control: Stage 1', ylab='Treatment: Stage 1',
panel.first=grid(nx=0,ny=0))
title(main='Conditional Power (n2=40)', font.main=3, adj=0)
for(i in 0:20){
for(j in 0:20){
text(i,j, round(conp[i+1,j+1]*100), cex=0.8)
} }
Xc=10, Xt=12 の場合の conditional power は 79% となった。仮に Xc=10, Xt=5 の場合はサンプルサイズを増やしても conditional power は 14% でしかないので早期打ち切りが妥当である。またサンプルサイズを増やさなくても conditional power が 高い時は \(n_2\)=20とする。
最終的に、以下のようなデザインができる。
library(ssanv)
<- function(n1=20,n2=20, pC=0.5,pT=0.8, xC,xT, alp2=0.02){
compute.n2 ## Conditional power が 80% になるような n2 を計算する。
<- function(n.add, n1,n2, pC,pT, xC,xT, alp2){
f round(CondPow(n1,n2, n.add, pC,pT, xC,xT, alp2) - 0.80, 3)
}uniroot.integer(f, lower=0, upper=100,n1=n1,n2=n2,pC=pC,pT=pT,xC=xC,xT=xT, alp2=alp2)$root
}
<- 0:20
xC <- 0:20
xT <- data.frame(expand.grid(xC,xT))
st1names(st1) <- c('xC','xT')
for(i in 1:nrow(st1)){
$zvalue[i] <- compute.z(st1[i,1], st1[i,2], n=20)
st1
}$pvalue <- round(1-pnorm(st1$zvalue),4)
st1$rej1 <- st1$pvalue < 0.005
st1<- st1[ !st1$rej, ]
st1.con row.names(st1.con) <- NULL
<- cond.pow40 <- nFor80 <- rep(NA, nrow(st1.con))
cond.pow20 for( i in 1:nrow(st1.con)){
<- CondPow(n1=20,n2=20,n.add=00, pC=0.5,pT=0.8,
cond.pow20[i] xC=st1.con[i,1], xT=st1.con[i,2], alp2=0.02)
<- CondPow(n1=20,n2=20,n.add=20, pC=0.5,pT=0.8,
cond.pow40[i] xC=st1.con[i,1], xT=st1.con[i,2], alp2=0.02)
if(cond.pow20[i] < 0.8 & cond.pow40[i] > 0.8){
<- compute.n2(n1=20,n2=20, pC=0.5,pT=0.8,
nFor80[i] xC=st1.con[i,1], xT=st1.con[i,2], alp2=0.02)
}
} $cp20 <- round(100*cond.pow20)
st1.con$cp40 <- round(100*cond.pow40)
st1.con$n2 <- nFor80 + 20
st1.con
<- st1.con[ st1.con$cp40 > 50,]
st1.con2 row.names(st1.con2) <- NULL
<- 20 + 20*(st1.con2$cp20 < 80)
n2x
$cp <- ifelse(st1.con2$cp20>=80, st1.con2$cp20, st1.con2$cp40)
st1.con2$cp[ !is.na(st1.con2$n2) ] <- 80
st1.con2$n2[is.na(st1.con2$n2)] <- n2x[is.na(st1.con2$n2)]
st1.con2
plot(0, type='n', xlim=c(0,20), ylim=c(0,20), xlab='Control: Stage 1', ylab='Treatment: Stage 1',
panel.first=grid(nx=0,ny=0))
title(main='Conditional Power', font.main=3, adj=0)
for(i in 1:nrow(st1.con2)){
text(st1.con2$xC[i], st1.con2$xT[i], st1.con2$cp[i], cex=0.8)
}
plot(0, type='n', xlim=c(0,20), ylim=c(0,20), xlab='Control: Stage 1', ylab='Treatment: Stage 1',
panel.first=grid(nx=0,ny=0))
title(main='Stage 2 sample size', font.main=3, adj=0)
for(i in 1:nrow(st1.con2)){
text(st1.con2$xC[i], st1.con2$xT[i], st1.con2$n2[i], cex=0.8)
}