# I HELP FUNCTIONS==============================================================
#.CritC_condS
.CritC_condS <- function(M,N,alpha) {
RHO_0 <- 1
NN <- M + N
CritC <- matrix(0,NN+1,2)
CritC[1,1] <- 0
CritC[1,2] <- alpha
for (S in 1:(NN-1))
{ X <- min(S,M)
PX <- 1-pFNCHypergeo(X-1,M,N,S,RHO_0)
while (PX <= alpha && X >= max(0,S-N)+1)
{ X <- X-1
PX <- 1
if (X > max(0,S-N))
PX <- 1-pFNCHypergeo(X-1,M,N,S,RHO_0) }
C <- X
PCPL <- 1-pFNCHypergeo(C,M,N,S,RHO_0)
GAM <- (alpha-PCPL)/(PX-PCPL)
CritC[S+1,1] <- C
CritC[S+1,2] <- GAM }
CritC[NN+1,1] <- M
CritC[NN+1,2] <- alpha
return(CritC)
}
# Ende .CritC_condS
# .CritfctXY
.CritfctXY <- function(M,N,CritC) {
phi <- matrix(rep(0,((M+1)*(N+1))),ncol=N+1)
for (x in 0:M)
{ for (y in 0:N)
{ s <- x+y
if (x < CritC[s+1,1])
phi[x+1,y+1] <- 0
if (x == CritC[s+1,1])
phi[x+1,y+1] <- CritC[s+1,2]
if (x > CritC[s+1,1])
phi[x+1,y+1] <- 1 } }
return(phi)
}
# Ende .CritfctXY
# .KX_Y
.KX_Y <- function(M,N,phi) {
kx_y <- c(rep(1,N+1))
phi <- floor(phi)
for (Y_ in 1:(N+1))
{ X_ <- 1
while (phi[X_,Y_] == 0 && X_ <= M)
{ X_ <- X_+1 }
kx_y[Y_] <- X_-1
if (kx_y[Y_] == M && phi[M+1,Y_] == 0)
{ kx_y[Y_] <- M+1 } }
return(kx_y)
}
# Ende .KX_Y
# .POW_EX_NR
.POW_EX_NR <- function(M,N,kx_y,CritC,P1,P2) {
PBY <- c(rep(1,N+1))
POWNR <- 0
pi2 <- P2
PBY[1] <- pbinom(0,N,pi2)
for (Y_ in 2:(N+1))
{ Y <- Y_-1
PBY[Y_] <- pbinom(Y,N,pi2) - pbinom(Y-1,N,pi2) }
pi1 <- P1
for (Y_ in 1:(N+1))
{ Y <- Y_-1
if (kx_y[Y_] == 0) PBXGEK_Y <- 1
if (kx_y[Y_] == M+1) PBXGEK_Y <- 0
if (kx_y[Y_] >= 1 && kx_y[Y_] <= M)
{ PBXGEK_Y <- 1-pbinom(kx_y[Y_]-1,M,pi1) }
PBYEQY <- PBY[Y_]
if (min(PBXGEK_Y,PBYEQY) <= 0) INCR <- 0
if (min(PBXGEK_Y,PBYEQY) > 0)
{ LPBX <- log(PBXGEK_Y)
LPBY <- log(PBYEQY)
INCR <- exp(LPBX+LPBY) }
POWNR <- POWNR + INCR }
pow <- POWNR
pow <- pow + CritC[1,2]*pbinom(0,M,pi1)*pbinom(0,N,pi2)
NN <- M+N
for (S in 1:NN)
{ x0 <- CritC[S+1,1]
gam <- CritC[S+1,2]
y0 <- S-x0
pow <- pow + gam*(pbinom(x0,M,pi1)-pbinom(max(0,x0-1),M,pi1)*sign(x0)) *
(pbinom(y0,N,pi2)-pbinom(max(0,y0-1),N,pi2)*sign(y0)) }
pow_ex_nr <- matrix(c(0,0),ncol=2)
pow_ex_nr[1,1] <- pow
pow_ex_nr[1,2] <- POWNR
return(pow_ex_nr)
}
# Ende .POW_EX_NR
.Napprox_score <- function(alpha,P1,P2,POW0,lambda) {
p0 <- lambda*P1 + (1-lambda)*P2
sig_H <- sqrt(p0*(1-p0)*(1/lambda + 1/(1-lambda)))
sig_A <- sqrt(P1*(1-P1)/lambda + P2*(1-P2)/(1-lambda))
napprox <- (sig_H*qnorm(1-alpha) + sig_A*qnorm(POW0))**2/(P1-P2)**2
napprox <- ceiling(napprox)
N1 <- round(lambda*napprox + 10**-6)
N2 <- round((1-lambda)/lambda*N1 + 10**-6)
Napprox <- matrix(c(0,0),ncol=2)
Napprox[1,1] <- N1
Napprox[1,2] <- N2
return(Napprox)
}
# Ende .Napprox_score
# .FINDSIZE
.FINDSIZE <- function(M,N,SW,kx_y) {
PBY <- matrix(1,N+1,1)
SIZE<-0
SIZE_ <- 0
PI2_ <- 0
pi2<- 0
while (pi2 <= 1 -SW) {
pi2 <- pi2+SW
pi1 <- pi2/(1-pi2)
pi1 <- pi1/(1+pi1)
PBY[1]=pbinom(0,N,pi2)
for(Y_ in 2:(N+1)){
Y<- Y_-1
PBY[Y_]<- pbinom(Y,N,pi2) - pbinom(Y-1,N,pi2)
}
PROBRJ<-0
for(Y_ in 1:(N+1)) {
Y<- Y_-1
if (kx_y[Y_]==0) {
PBXGEK_Y<-1 }
if (kx_y[Y_]== M+1) {
PBXGEK_Y<-0 }
if (kx_y[Y_] >= 1 & kx_y[Y_] <= M) {
PBXGEK_Y<- 1-pbinom(kx_y[Y_]-1,M,pi1)}
PBYEQY<- PBY[Y_]
if (min(PBXGEK_Y,PBYEQY) <= 0) {
INCR = 0 }
if (min(PBXGEK_Y,PBYEQY) > 0) {
LPBX<- log(PBXGEK_Y)
LPBY<- log(PBYEQY)
INCR<- exp(LPBX+LPBY) }
PROBRJ<- PROBRJ+INCR }
SIZE<-max(SIZE_,PROBRJ)
if (SIZE > SIZE_) {
SIZE_ <- SIZE
PI2_ <- pi2 }
}
RESULTS_SIZE <- matrix(1,2,1)
RESULTS_SIZE[1] <- SIZE
RESULTS_SIZE[2] <- PI2_
return(RESULTS_SIZE)
}
# Ende .FINDSIZE
# .POW
.POW <- function(M,N,kx_y,P1,P2) {
PBY <- matrix(1,N+1,1)
pow <- 0
pi2 <- P2
PBY[1] <- pbinom(0,N,pi2)
for (Y_ in 2:(N+1))
{ Y <- Y_ - 1
PBY[Y_] <- pbinom(Y,N,pi2) - pbinom(Y-1,N,pi2) }
pi1 <- P1
for (Y_ in 1: (N+1))
{
Y <- Y_-1
if (kx_y[Y_] == 0) PBXGEK_Y <- 1
if (kx_y[Y_] == (M+1)) PBXGEK_Y <- 0
if (kx_y[Y_] >= 1) PBXGEK_Y <- 1 - pbinom(kx_y[Y_]-1,M,pi1)
PBYEQY <- PBY[Y_]
if(min(PBXGEK_Y,PBYEQY) <= 0) INCR <- 0
if(min(PBXGEK_Y,PBYEQY) > 0)
{
LPBX <- log(PBXGEK_Y)
LPBY <- log(PBYEQY)
INCR <- exp(LPBX+LPBY)
}
pow <- pow + INCR
}
return(pow)
}
# Ende .POW
# .CORRECTNOMLEV
.CORRECTNOMLEV <- function(M,N,alpha,SW,MAXH) {
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
RES_SIZE <- .FINDSIZE(M,N,SW,kx_y)
SIZE_UNC <- RES_SIZE[1]
alpha2 <- alpha
SIZE <- SIZE_UNC
while(SIZE <= alpha)
{
alpha2 <- alpha2 + .01
CritC <- .CritC_condS(M,N,alpha2)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
RES_SIZE <- .FINDSIZE(M,N,SW,kx_y)
SIZE <- RES_SIZE[1]
}
alpha1 <- alpha2 - .01
IT <- 0
while(IT <= MAXH)
{
alpha0 <- (alpha1+alpha2) / 2
IT <- IT + 1
CritC <- .CritC_condS(M,N,alpha0)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
RES_SIZE <- .FINDSIZE(M,N,SW,kx_y)
SIZE <- RES_SIZE[1]
if(SIZE < alpha)
{
alpha1 <- alpha0
SIZE1 <- SIZE
} else
alpha2 <- alpha0
}
alpha0 <- alpha1
SIZE0 <- SIZE1
phi_final <- .CritfctXY(M,N,CritC)
kx_y_final <- .KX_Y(M,N,phi)
CORRNLres <- matrix(0,1,3)
CORRNLres[1] <- alpha0
CORRNLres[2] <- SIZE0
CORRNLres[3] <- SIZE_UNC
return(CORRNLres)
}
# Ende .CORRECTNOMLEV
# II SAMPLE SIZE FUNCTION=======================================================
#' Sample Size Calculation for the Fisher-Boschloo-Test
#'
#' \code{n_fisher_boschloo} performs the Sample Size calculation for two
#' independent samples with binary data using the absolute rate
#' difference quantifying the effect of an intervention.
#' The method used here is written by S. Wellek.
#' See [2] for further details.
#'
#' \describe{
#' \item{Null Hypothesis:}{\eqn{p_Y - p_X = 0}}
#' \item{Alternative Hypothesis:}{\eqn{|p_Y - p_X| \ge \Delta_A}}
#' }
#'
#' @param p_Y Event rate of Group Y on the alternative.
#' @param p_X Event rate of Group X on the alternative.
#' @param alpha Significance level \eqn{\alpha}.
#' @param power Desired Power \eqn{1-\beta}.
#' @param r Quotient of Sample sizes \eqn{r = n_Y / n_X}.
#' @param exact Default = TRUE. If set to FALSE the approximative
#' sample size is calculated. On TRUE the exact sample size is calculated.
#' @param SW Default = .0001. Step width.
#' @param MAXH Default = 10.
#'
#' @return \code{n_fisher_boschloo} returns an object of type list. The resulting
#' Sample Sizes are located in entrys named \code{n_X}, \code{n_Y}, \code{n}.
#' The resulting power is named \code{power_out}.
#'
#' @examples
#' n_fisher_boschloo(p_Y = .5, p_X = .3, alpha = .05, power = .8, r = 2)
#' n_fisher_boschloo(p_Y = .5, p_X = .3, alpha = .05, power = .8, r = 2)$n
#' n_fisher_boschloo(p_Y = .5, p_X = .3, alpha = .05, power = .8, r = 2, exact = FALSE)
#'
#' @details [2] S. Wellek: Nearly exact sample size calculation for powerful
#' non-randomized tests for differences between binomial proportions [2015],
#' Statistica Neerlandica
n_fisher_boschloo <- function(p_Y, p_X, alpha, power, r, exact = TRUE, SW = .0001, MAXH = 10) {
mton_a <- .get_fraction(r)$numerator
mton_b <- .get_fraction(r)$denominator
if ( (r %% 1) == 0 ) {mton_b <- 1}
P1 <- p_Y
P2 <- p_X
POW0 <- power
#cat(" ALPHA = ", alpha, " SW = ", SW, " MAXH = ", MAXH,
# "\n","\n","P1 = ", P1," P2 = ", P2," POW0 = ", POW0," mton_a = ", mton_a," mton_b = ", mton_b,
# file="boschloo_output.txt", append=FALSE)
mton_ab <- mton_a + mton_b
lambda <- mton_a/mton_ab
Nstart <- .Napprox_score(alpha,P1,P2,POW0,lambda)
M <- Nstart[1,1]
N <- Nstart[1,2]
M <- round((M+N)/mton_ab + 10**-6) * mton_a
N <- round((M+N)/mton_ab + 10**-6) * mton_b
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWEX <- pow_ex_nr[1,1]
mstart <- M
nstart <- N
#cat("\n","\n","mstart = ",mstart," nstart = ", nstart,"\n","\n","POW_EX_NR = ", pow_ex_nr,
# file="boschloo_output.txt", append=TRUE)
incr_m <- mton_a
incr_n <- mton_b
incr5_m <- 5*incr_m
incr5_n <- 5*incr_n
if (POWEX < POW0)
{
repeat
{
M <- M + incr5_m
N <- N + incr5_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWEX <- pow_ex_nr[1,1]
if(POWEX >= POW0) break
}
M <- M - incr5_m
N <- N - incr5_n
repeat
{
M <- M + incr_m
N <- N + incr_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWEX <- pow_ex_nr[1,1]
if(POWEX >= POW0) break
}
} else
{
repeat
{
M <- M - incr5_m
N <- N - incr5_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWEX <- pow_ex_nr[1,1]
if(POWEX < POW0) break
}
repeat
{
M <- M + incr_m
N <- N + incr_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWEX <- pow_ex_nr[1,1]
if(POWEX >= POW0) break
}
}
Mex <- M
Nex <- N
#cat("\n","\n","Mex = ",Mex," Nex = ",Nex," POWEX = ", POWEX,
# file="boschloo_output.txt", append=TRUE)
POWNR <- pow_ex_nr[1,2]
mstart <- Mex
nstart <- Nex
if(POWNR < POW0)
{
repeat
{
M <- M + incr5_m
N <- N + incr5_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWNR <- pow_ex_nr[1,2]
if(POWNR >= POW0) break
}
M <- M - incr5_m
N <- N - incr5_n
repeat
{
M <- M + incr_m
N <- N + incr_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWNR <- pow_ex_nr[1,2]
if(POWNR >= POW0) break
}
} else
{
repeat
{
M <- M - incr5_m
N <- N - incr5_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWNR <- pow_ex_nr[1,2]
if(POWNR < POW0) break
}
repeat
{
M <- M + incr_m
N <- N + incr_n
CritC <- .CritC_condS(M,N,alpha)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow_ex_nr <- .POW_EX_NR(M,N,kx_y,CritC,P1,P2)
POWNR <- pow_ex_nr[1,2]
if(POWNR >= POW0) break
}
}
Mnr <- M
Nnr <- N
#cat("\n","\n","Mnr = ",Mnr,"Nnr = ", Nnr," POWNR = ", POWNR,
# file="boschloo_output.txt", append=TRUE)
m0 <- (Mex + Mnr)/2
n0 <- (Nex + Nnr)/2
M <- round((m0+n0)/mton_ab)*mton_a
N <- round((m0+n0)/mton_ab)*mton_b
CORRNOMLEV <- .CORRECTNOMLEV(M,N,alpha,SW,MAXH)
alpha0 <- CORRNOMLEV[1,1]
CritC <- .CritC_condS(M,N,alpha0)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow <- .POW(M,N,kx_y,P1,P2)
pow_approx <- pow
mstart <- M
nstart <- N
#cat("\n","\n","MStart = ",mstart," NStart = ", nstart," alpha0 = ", alpha0," POW = ", pow,
# file="boschloo_output.txt", append=TRUE)
incr_m <- mton_a
incr_n <- mton_b
incr5_m <- 2*incr_m
incr5_n <- 2*incr_n
if(pow < POW0)
{
repeat
{
M <- M + incr5_m
N <- N + incr5_n
CORRNOMLEV <- .CORRECTNOMLEV(M,N,alpha,SW,MAXH)
alpha0 <- CORRNOMLEV[1,1]
SIZE <- CORRNOMLEV[1,2]
CritC <- .CritC_condS(M,N,alpha0)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow <- .POW(M,N,kx_y,P1,P2)
if(pow >= POW0) break
}
M <- M - incr5_m
N <- N - incr5_n
repeat
{
M <- M + incr_m
N <- N + incr_n
CORRNOMLEV <- .CORRECTNOMLEV(M,N,alpha,SW,MAXH)
alpha0 <- CORRNOMLEV[1,1]
SIZE <- CORRNOMLEV[1,2]
CritC <- .CritC_condS(M,N,alpha0)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow <- .POW(M,N,kx_y,P1,P2)
if(pow >= POW0) break
}
} else
{
repeat
{
M <- M - incr5_m
N <- N - incr5_n
CORRNOMLEV <- .CORRECTNOMLEV(M,N,alpha,SW,MAXH)
alpha0 <- CORRNOMLEV[1,1]
SIZE <- CORRNOMLEV[1,2]
CritC <- .CritC_condS(M,N,alpha0)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow <- .POW(M,N,kx_y,P1,P2)
if(pow < POW0) break
}
repeat
{
M <- M + incr_m
N <- N + incr_n
CORRNOMLEV <- .CORRECTNOMLEV(M,N,alpha,SW,MAXH)
alpha0 <- CORRNOMLEV[1,1]
SIZE <- CORRNOMLEV[1,2]
CritC <- .CritC_condS(M,N,alpha0)
phi <- .CritfctXY(M,N,CritC)
kx_y <- .KX_Y(M,N,phi)
pow <- .POW(M,N,kx_y,P1,P2)
if(pow >= POW0) break
}
}
MBo <- M
NBo <- N
#cat("\n","\n","MBo = ",MBo," NBo = ", NBo," alpha0 = ", alpha0," SIZE = ", SIZE," POW = ", pow,
# file="boschloo_output.txt", append=TRUE)
#file.show("boschloo_output.txt")
# Organize Output for presentation
if (exact == TRUE){
n_Y = MBo
n_X = NBo
n = n_Y + n_X
power_out = pow
} else {
n_Y = mstart
n_X = nstart
n = n_Y + n_X
power_out = pow_approx
}
n.results <- list(
n_X = n_X,
n_Y = n_Y,
n = n_X + n_Y,
power_out = power_out
)
input_list <- list(
alpha = alpha,
power = power,
exact = exact,
r = r,
p_Y = p_Y,
p_X = p_X,
SW = SW
)
results <- c(input_list, n.results)
class(results) <- c("n_fisher_boschloo", class(results))
return(results)
}
# III POWER FUNCTION============================================================
#' Power Calculation for the Fisher-Boschloo-Test
#'
#' \code{power_fisher_boschloo} performs the exact power calculation for the
#' fisher-boschloo test.
#' The method used here is written by S. Wellek.
#' See [2] for further details.
#'
#' @param p_Y Event rate of Group Y on the alternative.
#' @param p_X Event rate of Group X on the alternative.
#' @param n_Y Sample size of Group Y.
#' @param n_X Sample size of Group X.
#' @param alpha Significance level \eqn{\alpha}.
#' @param SW Default = .001. Step width.
#' @param MAXH Default = 10.
#'
#' @return \code{n_fisher_boschloo} returns an object of type list. The resulting
#' Sample Sizes are located in entrys named \code{n_X}, \code{n_Y}, \code{n}.
#' The resulting power is named \code{power_out}.
#'
#' @examples
#' power_fisher_boschloo(p_Y = .5, p_X = .3, n_Y = 70, n_X = 40, alpha = .025)
#'
#' @details [2] S. Wellek: Nearly exact sample size calculation for powerful
#' non-randomized tests for differences between binomial proportions [2015],
#' Statistica Neerlandica
power_fisher_boschloo <- function(p_Y, p_X, n_Y, n_X, alpha,
SW = .0001,
MAXH = 10
){
CORRNOMLEV <- .CORRECTNOMLEV(n_Y, n_X, alpha, SW, MAXH)
alpha0 <- CORRNOMLEV[1,1]
SIZE <- CORRNOMLEV[1,2]
CritC <- .CritC_condS(n_Y, n_X, alpha0)
phi <- .CritfctXY(n_Y, n_X, CritC)
kx_y <- .KX_Y(n_Y, n_X, phi)
pow <- .POW(n_Y, n_X, kx_y, p_Y, p_X)
# retrn power
return(pow)
}
#IV PRINT FUNCTION==============================================================
print.n_fisher_boschloo <- function(x, ...){
cat("Sample size calculation for the Fisher-Boschloo test for two independent\n")
cat("samples with binary data using the absolute rate difference.\n")
if(x$exact == TRUE) {cat("Exact calculation performed. \n\n")}
else {cat("Approximative calculation performed. \n\n")}
cat(sprintf(
"Input Parameters \n
Rate intervention group : %.3f
Rate control group : %.3f
Significance level : %.4f
Desired power : %.2f %%
Step width : %.4f
Allocation : %.2f \n
Results of sample size calculation \n
n intervention group : %i
n control group : %i
n total : %i
Actual power : %.5f %%",
x$p_Y,
x$p_X,
x$alpha,
x$power*100,
x$SW,
x$r,
x$n_Y,
x$n_X,
x$n,
x$power_out*100
)
)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.