mr_presso <- function(BetaOutcome, BetaExposure, SdOutcome, SdExposure, data, OUTLIERtest = FALSE, DISTORTIONtest = FALSE, SignifThreshold = 0.05, NbDistribution = 1000, seed = NULL){
if(!is.null(seed))
set.seed(seed)
if(SignifThreshold > 1)
stop("The significance threshold cannot be greater than 1")
if(length(BetaExposure) != length(SdExposure))
stop("BetaExposure and SdExposure must have the same number of elements")
if(class(data)[1] != "data.frame")
stop("data must be an object of class data.frame, try to rerun MR-PRESSO by conversing data to a data.frame \'data = as.data.frame(data)\'")
# Functions
"%^%" <- function(x, n) with(eigen(x), vectors %*% (values^n * t(vectors)))
getRSS_LOO <- function(BetaOutcome, BetaExposure, data, returnIV){
dataW <- data[, c(BetaOutcome, BetaExposure)] * sqrt(data[, "Weights"])
X <- as.matrix(dataW[ , BetaExposure])
Y <- as.matrix(dataW[ , BetaOutcome])
CausalEstimate_LOO <- sapply(1:nrow(dataW), function(i) {
(t(X[-i, ]) %*% X[-i, ])%^%(-1) %*% t(X[-i, ]) %*% Y[-i, ]
})
if(length(BetaExposure) == 1)
RSS <- sum((Y - CausalEstimate_LOO * X)^2, na.rm = TRUE)
else
RSS <- sum((Y - rowSums(t(CausalEstimate_LOO) * X))^2, na.rm = TRUE)
if(returnIV)
RSS <- list(RSS, CausalEstimate_LOO)
return(RSS)
}
getRandomData <- function(BetaOutcome, BetaExposure, SdOutcome, SdExposure, data){
mod_IVW <- lapply(1:nrow(data), function(i) lm(as.formula(paste0(BetaOutcome, " ~ -1 + ", paste(BetaExposure, collapse=" + "))), weights = Weights, data = data[-i, ]))
dataRandom <- cbind(eval(parse(text = paste0("cbind(", paste0("rnorm(nrow(data), data[, \'", BetaExposure, "\'], data[ ,\'", SdExposure, "\'])", collapse = ", "), ", sapply(1:nrow(data), function(i) rnorm(1, predict(mod_IVW[[i]], newdata = data[i, ]), data[i ,\'", SdOutcome,"\'])))"))), data$Weights)
colnames(dataRandom) <- c(BetaExposure, BetaOutcome, "Weights")
return(dataRandom)
}
# 0- Transforming the data + checking number of observations
data <- data[, c(BetaOutcome, BetaExposure, SdOutcome, SdExposure)]
data <- data[rowSums(is.na(data)) == 0, ]
data[, c(BetaOutcome, BetaExposure)] <- data[, c(BetaOutcome, BetaExposure)] * sign(data[, BetaExposure[1]])
data$Weights <- data$Weights <- 1/data[, SdOutcome]^2
if(nrow(data) <= length(BetaExposure) + 2)
stop("Not enough intrumental variables")
if(nrow(data) >= NbDistribution)
stop("Not enough elements to compute empirical P-values, increase NbDistribution")
# 1- Computing the observed residual sum of squares (RSS)
RSSobs <- getRSS_LOO(BetaOutcome = BetaOutcome, BetaExposure = BetaExposure, data = data, returnIV = OUTLIERtest)
# 2- Computing the distribtion of expected residual sum of squares (RSS)
randomData <- replicate(NbDistribution, getRandomData(BetaOutcome = BetaOutcome, BetaExposure = BetaExposure, SdOutcome = SdOutcome, SdExposure = SdExposure, data = data), simplify = FALSE)
RSSexp <- sapply(randomData, getRSS_LOO, BetaOutcome = BetaOutcome, BetaExposure = BetaExposure, returnIV = OUTLIERtest)
if(OUTLIERtest)
GlobalTest <- list(RSSobs = RSSobs[[1]], Pvalue = sum(RSSexp[1, ] > RSSobs[[1]])/NbDistribution)
else
GlobalTest <- list(RSSobs = RSSobs[[1]], Pvalue = sum(RSSexp > RSSobs[[1]])/NbDistribution)
# 3- Computing the single IV outlier test
if(GlobalTest$Pvalue < SignifThreshold & OUTLIERtest){
OutlierTest <- do.call("rbind", lapply(1:nrow(data), function(SNV){
randomSNP <- do.call("rbind", lapply(randomData, function(mat) mat[SNV, ]))
if(length(BetaExposure) == 1){
Dif <- data[SNV, BetaOutcome] - data[SNV, BetaExposure] * RSSobs[[2]][SNV]
Exp <- randomSNP[, BetaOutcome] - randomSNP[, BetaExposure] * RSSobs[[2]][SNV]
} else {
Dif <- data[SNV, BetaOutcome] - sum(data[SNV, BetaExposure] * RSSobs[[2]][, SNV])
Exp <- randomSNP[, BetaOutcome] - rowSums(randomSNP[, BetaExposure] * RSSobs[[2]][, SNV])
}
pval <- sum(Exp^2 > Dif^2)/length(randomData)
pval <- cbind.data.frame(RSSobs = Dif^2, Pvalue = pval)
return(pval)
}))
row.names(OutlierTest) <- row.names(data)
OutlierTest$Pvalue <- apply(cbind(OutlierTest$Pvalue*nrow(data), 1), 1, min) # Bonferroni correction
} else{
OUTLIERtest <- FALSE
}
# 4- Computing the test of the distortion of the causal estimate
mod_all <- lm(as.formula(paste0(BetaOutcome, " ~ -1 + ", paste(BetaExposure, collapse = "+"))), weights = Weights, data = data)
if(DISTORTIONtest & OUTLIERtest){
getRandomBias <- function(BetaOutcome, BetaExposure, SdOutcome, SdExposure, data, refOutlier){
indices <- c(refOutlier, replicate(nrow(data)-length(refOutlier), sample(setdiff(1:nrow(data), refOutlier))[1]))
mod_random <- lm(as.formula(paste0(BetaOutcome, " ~ -1 + ", paste(BetaExposure, collapse = "+"))), weights = Weights, data = data[indices[1:(length(indices) - length(refOutlier))], ])
return(mod_random$coefficients[BetaExposure])
}
refOutlier <- which(OutlierTest$Pvalue <= SignifThreshold)
if(length(refOutlier) > 0){
if(length(refOutlier) < nrow(data)){
BiasExp <- replicate(NbDistribution, getRandomBias(BetaOutcome = BetaOutcome, BetaExposure = BetaExposure, data = data, refOutlier = refOutlier), simplify = FALSE)
BiasExp <- do.call("rbind", BiasExp)
mod_noOutliers <- lm(as.formula(paste0(BetaOutcome, " ~ -1 + ", paste(BetaExposure, collapse=" + "))), weights = Weights, data = data[-refOutlier, ])
BiasObs <- (mod_all$coefficients[BetaExposure] - mod_noOutliers$coefficients[BetaExposure]) / abs(mod_noOutliers$coefficients[BetaExposure])
BiasExp <- (mod_all$coefficients[BetaExposure] - BiasExp) / abs(BiasExp)
BiasTest <- list(`Outliers Indices` = refOutlier, `Distortion Coefficient` = 100*BiasObs, Pvalue = sum(abs(BiasExp) > abs(BiasObs))/NbDistribution)
} else {
BiasTest <- list(`Outliers Indices` = "All SNPs considered as outliers", `Distortion Coefficient` = NA, Pvalue = NA)
}
} else{
BiasTest <- list(`Outliers Indices` = "No significant outliers", `Distortion Coefficient` = NA, Pvalue = NA)
}
}
# 5- Formatting the results
GlobalTest$Pvalue <- ifelse(GlobalTest$Pvalue == 0, paste0("<", 1/NbDistribution), GlobalTest$Pvalue)
if(OUTLIERtest){
OutlierTest$Pvalue <- replace(OutlierTest$Pvalue, OutlierTest$Pvalue == 0, paste0("<", nrow(data)/NbDistribution))
if(DISTORTIONtest){
BiasTest$Pvalue <- ifelse(BiasTest$Pvalue == 0, paste0("<", 1/NbDistribution), BiasTest$Pvalue)
res <- list(`Global Test` = GlobalTest, `Outlier Test` = OutlierTest, `Distortion Test` = BiasTest)
} else {
res <- list(`Global Test` = GlobalTest, `Outlier Test` = OutlierTest)
}
if(nrow(data)/NbDistribution > SignifThreshold)
warning(paste0("Outlier test unstable. The significance threshold of ", SignifThreshold, " for the outlier test is not achievable with only ", NbDistribution, " to compute the null distribution. The current precision is <", nrow(data)/NbDistribution, ". Increase NbDistribution."))
} else {
res <- list(`Global Test` = GlobalTest)
}
OriginalMR <- cbind.data.frame(BetaExposure, "Raw", summary(mod_all)$coefficients)
colnames(OriginalMR) <- c("Exposure", "MR Analysis", "Causal Estimate", "Sd", "T-stat", "P-value")
if(exists("mod_noOutliers")){
OutlierCorrectedMR <- cbind.data.frame(BetaExposure, "Outlier-corrected", summary(mod_noOutliers)$coefficients, row.names = NULL)
} else {
warning("No outlier were identified, therefore the results for the outlier-corrected MR are set to NA")
OutlierCorrectedMR <- cbind.data.frame(BetaExposure, "Outlier-corrected", t(rep(NA, 4)), row.names = NULL)
}
colnames(OutlierCorrectedMR) <- colnames(OriginalMR)
MR <- rbind.data.frame(OriginalMR, OutlierCorrectedMR)
row.names(MR) <- NULL
res <- list(`Main MR results` = MR, `MR-PRESSO results` = res)
return(res)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.