## R code to generate figures for Cook/Zea SIM paper ## "Missing Data and Sensitivity Analysis for Binary Data with Implications ## for Sample Size and Power of Randomized Clinical Trials." ## Define some functions: ## find MLE of common p under null hypothesis. Uses trigonometric solution ## to cubic equation to make it efficiently vectorizable. pHat <- function(R=c(1,1), X){ ## X should have dim 2x3 or 2x3xk if(length(dim(X))==2){if(all(dim(X)==c(2,3))) dim(X) <- c(2,3,1) else stop("Dimension of X is not 2x3")} else if(length(dim(X))==3 && !all(dim(X)[1:2]==c(2,3))) stop("Dimension of X is not 2x3xk") R <- matrix(R,nrow=2) m <- X[,3,] if(!is.matrix(m)) dim(m) <- c(2,1) Xsums <- colSums(X) n <- colSums(Xsums) y <- Xsums[1,] ## coefficients of cubic equation: ax^3 + bx^2 + cx + d: coef <- cbind(-n*(R[1,]-1)*(R[2,]-1), (R[1,]-1)*(R[2,]-1)*y - n*(R[2,]+ R[1,]-2) + m[2,]*R[2,]*(R[1,]-1) + m[1,]*R[1,]*(R[2,]-1), (colSums(R)-2)*y + m[2,]*R[2,] + m[1,]*R[1,] - n, y) ## transform to "depressed form": t^3+p*t+q=0 p <- (3*coef[,1]*coef[,3]-coef[,2]^2)/(3*coef[,1]^2) q <- (2*coef[,2]^3-9*coef[,1]*coef[,2]*coef[,3]+27*coef[,1]^2*coef[,4])/(27*coef[,1]^3) ## t is trigonometric solution t <- 2*sqrt(-p/3)*cos(outer(1/3*acos(3*q/2/p*sqrt(-3/p)),2*pi*0:2/3,"-"))-coef[,2]/coef[,1]/3 ##tq is solution to quadratic if a = 0 (and b!=0) tq <- -(coef[,3]+sqrt(ifelse(coef[,1]==0, coef[,3]^2-4*coef[,2]*coef[,4],0)))/2/coef[,2] ## vector "one" guarantees that the "test" in ifelse has the right length one <- rep(1,length(y)) ifelse(coef[,1]==0&coef[,2]==0|m[1,]+m[2,]==0, ## case a=b=0, linear y/(n-m[1,]*R[1,]-m[2,]*R[2,]), ifelse(coef[,1]==0,tq, ## case a=0, b!=0, quadratic ifelse(R[1,]>one&R[2,]>one, t[,1], ## now pick the correct cubic root. ifelse(R[1,]1) stop("No. of columns of R does't match number of tables in X") k <- pmax(k,dim(R)[2]) }} else if(length(R)!=2) stop("R has the wrong length/structure") phat <- pHat(R,X) phat <- rbind(phat,phat) m <- X[,3,] y <- X[,1,] n <- apply(X,c(1,3),sum)[,] u <- y -n*phat + m*R*phat/(1-phat+phat*R) v <- (n-m*R/(1-phat+R*phat)^2)*phat*(1-phat) v <- 1/(colSums(1/v)) ifelse(v<=0&abs(u[1,])<.Machine$double.eps^.7, 0, u[1,]/sqrt(pmax(v,0))) } ## ez calculates expected Z given its arguments ez <- function(R, n, q, p, R0=R){ ## R is the assumed R for analysis ## R0 is the true R for the underlying data ## p is the true p for the underlying data, length 1 or 2 ## n is the sample size per group, length 1 or 2 ## q is the marginal missingness probability per group, length 1 or 2 if(length(R)==1) R <- c(R,R) if(length(R0)==1) R0 <- c(R0,R0) if(length(p)==1) p <- c(p,p) if(length(n)==1) n <- c(n,n) pp0 <- (1-p+p*R0) pii <- q/pp0 Ey <- p*(1-R0*pii)*n Em <- pp0*pii*n ## Create array of expected values and pass to zTest to get expected Z X <- array(c(Ey,n-Ey-Em,Em),c(2,length(Ey)/2,3)) X <- aperm(X,c(1,3,2)) zTest(R,X) } ## function to generate boundary points for ellipse ## t is between 0 and 1 with angle from x-axis = pi*(1/4+2*t) ## a is x=y at t=0 ("major axis") ## e = eccentricity of ellipse Ellipse <- function(t, a,e){ a <- log(a) t <- 2*pi*t x <- exp(a*(cos(t) - sqrt(1-e^2)*sin(t))) y <- exp(a*(cos(t) + sqrt(1-e^2)*sin(t))) cbind(x=x,y=y) } ## function to generate boundary points for diamond shaped region ## between (e,e), (-d,d), (-e,-e), (d,-d). Diamond <- function(t,d,e) {t <- 4*(t%%1) e <- log(e) d <- log(d) x <- ifelse(t<=1, e-t*(e+d), ifelse(t<=2, -d+(t-1)*(d-e), ifelse(t<=3, -e+(t-2)*(e+d), d-(t-3)*(d-e)))) y <- ifelse(t<=1, e-t*(e-d), ifelse(t<=2, d-(t-1)*(d+e), ifelse(t<=3, -e+(t-2)*(e-d), -d+(t-3)*(d+e)))) cbind(x=exp(x),y=exp(y))} ## Compute inflation factor and locations where minimum exp Z is achieved ## rFun is function to generate boundary of of plausible region (e.g., "Ellipse") ## n is sample size per group ## q is the marginal missingness probability per group, length 1 or 2 ## p is the true p for the underlying data, length 1 (pc) or 2 (pc,pt) ## if length(p)==1, calculate alterative pt