## CAMERA.R interGeneCorrelation <- function(y, design) # Estimate variance-inflation factor for means of correlated genes # Gordon Smyth and Di Wu # Created 2007. Last modified 11 Feb 2012 { m <- nrow(y) qrdesign <- qr(design) y <- qr.qty(qrdesign, t(y))[-(1:qrdesign$rank),] # Gives same result as the following # ny <- t(y) / sqrt(colSums(y^2)) # cormatrix <- tcrossprod(ny) # correlation <- mean(cormatrix[lower.tri(cormatrix)]) # 1+correlation*(n-1) y <- t(y) / sqrt(colMeans(y^2)) vif <- m * mean(colMeans(y)^2) correlation <- (vif-1)/(m-1) list(vif=vif,correlation=correlation) } camera <- function(y,...) UseMethod("camera") camera.default <- function(y,index,design=NULL,contrast=ncol(design),weights=NULL,use.ranks=FALSE,allow.neg.cor=FALSE,inter.gene.cor=0.01,trend.var=FALSE,sort=TRUE,directional=TRUE,...) # Competitive gene set test allowing for correlation between genes # Gordon Smyth and Di Wu # Created 2007. Last modified 4 Mar 2025. { # Issue warning if extra arguments found dots <- names(list(...)) if(length(dots)) warning("Extra arguments disregarded: ",sQuote(dots)) # Extract components from y y <- getEAWP(y) G <- nrow(y$exprs) n <- ncol(y$exprs) ID <- rownames(y$exprs) if(G<3) stop("Too few genes in dataset: need at least 3") # Check index if(!is.list(index)) index <- list(set1=index) nsets <- length(index) if(nsets==0L) stop("index is empty") # Check design if(is.null(design)) design <- y$design if(is.null(design)) stop("design matrix not specified") else { design <- as.matrix(design) if(mode(design) != "numeric") stop("design must be a numeric matrix") } if(nrow(design) != n) stop("row dimension of design matrix must match column dimension of data") p <- ncol(design) df.residual <- n-p if(df.residual < 1) stop("No residual df: cannot compute t-tests") # Check weights if(is.null(weights)) weights <- y$weights # Check inter.gene.cor fixed.cor <- !(is.na(inter.gene.cor) || is.null(inter.gene.cor)) # Check directional if(!directional) { if(!use.ranks) { use.ranks <- TRUE warning("Setting `use.ranks=TRUE` for non-directional tests",call.=FALSE) } } # Set df for camera tests if(fixed.cor) { if(use.ranks) df.camera <- Inf else df.camera <- G-2L } else { df.camera <- min(df.residual,G-2L) } # Reduce to numeric expression matrix y <- y$exprs # Check weights if(!is.null(weights)) { if(any(weights<=0)) stop("weights must be positive") if(length(weights)==n) { sw <- sqrt(weights) y <- t(t(y)*sw) design <- design*sw weights <- NULL } } if(!is.null(weights)) { if(length(weights)==G) weights <- matrix(weights,G,n) weights <- as.matrix(weights) if(any( dim(weights) != dim(y) )) stop("weights not conformal with y") } # Reform design matrix so that contrast of interest is last column if(is.character(contrast)) { contrast <- which(contrast==colnames(design)) if(length(contrast)==0) stop("coef ",contrast," not found") } if(length(contrast)==1) { j <- c((1:p)[-contrast], contrast) if(contrast<p) design <- design[,j] } else { QR <- qr(contrast) design <- t(qr.qty(QR,t(design))) if(sign(QR$qr[1,1]<0)) design[,1] <- -design[,1] design <- design[,c(2:p,1)] } # Compute effects matrix if(is.null(weights)) { QR <- qr(design) if(QR$rank<p) stop("design matrix is not of full rank") effects <- qr.qty(QR,t(y)) unscaledt <- effects[p,] if(QR$qr[p,p]<0) unscaledt <- -unscaledt } else { effects <- matrix(0,n,G) colnames(effects) <- ID unscaledt <- rep_len(0,length.out=G) names(unscaledt) <- ID sw <- sqrt(weights) yw <- y*sw for (g in 1:G) { xw <- design*sw[g,] QR <- qr(xw) if(QR$rank<p) stop("weighted design matrix not of full rank for gene ",g) effects[,g] <- qr.qty(QR,yw[g,]) unscaledt[g] <- effects[p,g] if(QR$qr[p,p]<0) unscaledt[g] <- -unscaledt[g] } } # Standardized residuals U <- effects[-(1:p),,drop=FALSE] sigma2 <- colMeans(U^2) U <- t(U) / sqrt(pmax(sigma2,1e-8)) # Moderated t if(trend.var) A <- rowMeans(y) else A <- NULL sv <- squeezeVar(sigma2,df=df.residual,covariate=A) modt <- unscaledt / sqrt(sv$var.post) if(use.ranks) { Stat <- modt if(!directional) Stat <- abs(Stat) } else { df.total <- min(df.residual+sv$df.prior, G*df.residual) Stat <- zscoreT(modt, df=df.total, approx=TRUE, method="hill") } # Global statistics if(!use.ranks) { meanStat <- mean(Stat) varStat <- var(Stat) } tab <- matrix(0,nsets,5) rownames(tab) <- names(index) colnames(tab) <- c("NGenes","Correlation","Down","Up","TwoSided") for (i in 1:nsets) { iset <- index[[i]] if(is.character(iset)) iset <- which(ID %in% iset) StatInSet <- Stat[iset] m <- length(StatInSet) m2 <- G-m if(fixed.cor) { correlation <- inter.gene.cor vif <- 1+(m-1)*correlation } else { if(m>1) { Uset <- U[iset,,drop=FALSE] vif <- m * mean(colMeans(Uset)^2) correlation <- (vif-1)/(m-1) } else { vif <- 1 correlation <- NA } } tab[i,1] <- m tab[i,2] <- correlation if(use.ranks) { if(!allow.neg.cor) correlation <- max(0,correlation) tab[i,3:4] <- rankSumTestWithCorrelation(iset,statistics=Stat,correlation=correlation,df=df.camera) } else { if(!allow.neg.cor) vif <- max(1,vif) meanStatInSet <- mean(StatInSet) delta <- G/m2*(meanStatInSet-meanStat) varStatPooled <- ( (G-1)*varStat - delta^2*m*m2/G ) / (G-2) two.sample.t <- delta / sqrt( varStatPooled * (vif/m + 1/m2) ) tab[i,3] <- pt(two.sample.t,df=df.camera) tab[i,4] <- pt(two.sample.t,df=df.camera,lower.tail=FALSE) } } tab[,5] <- 2*pmin(tab[,3],tab[,4]) # Asemble into data.frame tab <- data.frame(tab,stringsAsFactors=FALSE) if(directional) { # New column names (Jan 2013) Direction <- rep_len("Up",length.out=nsets) Direction[tab$Down < tab$Up] <- "Down" tab$Direction <- Direction tab$PValue <- tab$TwoSided tab$Down <- tab$Up <- tab$TwoSided <- NULL } else { tab$Down <- tab$TwoSided <- NULL names(tab)[3] <- "PValue" } # Remove correlation column if it was not estimated if(fixed.cor) tab$Correlation <- NULL # Add FDR if(nsets>1) tab$FDR <- p.adjust(tab$PValue,method="BH") # Sort by p-value if(sort && nsets>1) { o <- order(tab$PValue) tab <- tab[o,] } tab }