Here we show how to estimate the full posterior distribution of genetic value using LDpred2 as described in Large uncertainty in individual PRS estimation impacts PRS-based risk stratification.

This tutorial assumes that you have already tuned the hyperparameters using grid search. Please refer to Computing polygenic scores using LDpred2 for tutorials on data preprocessing and LDpred2-grid.

Approximating the posterior distribution of genetic value

Given the set of tuned hyperparameters (see above), we want to approximate the full posterior distribution of an individual’s genetic value, \(\small{GV_i = \mathrm{\bf{x}}_i^T \boldsymbol\beta}\). From the posterior, one can obtain the posterior mean, \(\small{\widehat{PRS}_i \equiv \mathbb{E}(GV_i|\mathrm{Data})}\), and various metrics of uncertainty such as the posterior variance, \(\small{var(GV_i|\mathrm{Data})}\), where \(\small{GV_i}\) is the genetic value of individual \(i\) and \(\mathrm{\small{Data}}\) refers to a given GWAS, and \(\rho\)-credible interval, the interval within which the individual’s genetic value falls with probability \(\rho\).

An individual’s genetic value is their genotype vector multipled by the causal effect vector. We therefore need to obtain MCMC samples from the posterior of the causal effects. This is done by supplying the tuned hyperparameters (best_param in the example below) to snp_ldpred2_grid and setting return_sampling_betas = TRUE. In the example below, we return 500 MCMC samples from the posterior of the causal effects with the option num_iter = 500.

Note that, when using return_sampling_betas = TRUE, you must supply one set of parameters only, i.e. best_param must be a data frame with exactly 1 row (otherwise it will raise an error).

best_param <- data.frame(p = 0.0018, h2 = 0.1146, sparse = FALSE)
posterior_beta_samples <- snp_ldpred2_grid(
  corr, df_beta, best_param,
  return_sampling_betas = TRUE, num_iter = 500)
dim(posterior_beta_samples)
## [1] 52546   500

Posterior samples of the individual’s genetic value are then obtained by multiplying their genotype with the posterior samples of \(\boldsymbol{\beta}\).

posterior_gv_samples <- big_prodMat(G, posterior_beta_samples, ind.col = ind.chr2)
dim(posterior_gv_samples)
## [1] 559 500

The autocorrelation is generally weak, otherwise, you can perform some thinning.

acf(posterior_gv_samples[1, ], lag.max = 10, plot = TRUE)$acf

## , , 1
## 
##                [,1]
##  [1,]  1.0000000000
##  [2,] -0.0786463139
##  [3,]  0.0218726514
##  [4,]  0.0271931106
##  [5,] -0.0287941817
##  [6,]  0.0111070845
##  [7,] -0.0149171804
##  [8,]  0.0165611609
##  [9,]  0.0758901569
## [10,]  0.0171239973
## [11,]  0.0002881344
rowMeans(apply(posterior_gv_samples, 1, function(x) {
  acf(x, lag.max = 10, plot = FALSE)$acf
}))
##  [1]  1.000000000 -0.007897343  0.026445937 -0.012760849 -0.008629879
##  [6] -0.001409748 -0.013761490  0.005290774 -0.010247640 -0.019465620
## [11] -0.055678966

Estimating the posterior mean and posterior variance

From the posterior samples of genetic value (see above), we can compute summary statistics such as the posterior mean and posterior variance. The posterior mean can be interpreted as the individual’s polygenic (risk) score, i.e. \(\small{\widehat{PRS}_i \equiv \mathbb{E}(GV_i|\mathrm{Data})}\). The posterior variance, \(\small{var(GV_i|\mathrm{Data})}\), is one metric of uncertainty; credible intervals are discussed below.

samples <- posterior_gv_samples[1, ]
posterior_gv_mean <- mean(samples)
posterior_gv_var <- var(samples)

Constructing credible intervals of genetic value

Another way to quantify uncertainty in an individual’s polygenic score is by constructing a \(\rho\)-credible interval of the individual’s genetic value, i.e. the range of values that contains the individual’s true genetic value with probability \(\rho\).

The example below demonstrates how one can obtain a 95% credible interval of the individual’s genetic value.

rho <- 0.95
bound <- (1 - rho) / 2
samples <- posterior_gv_samples[1, ]
mean <- mean(samples)
lower_ci <- quantile(samples, bound)
upper_ci <- quantile(samples, 1 - bound)
hist(samples, main = "Posterior distribution of genetic value", xlab = NULL) 
abline(v = c(lower_ci, mean, upper_ci), col = c("blue", "red", "blue"), lty = c(2,1,2))
legend("topright", legend = c("Credible Interval", "PRS"),
       col = c("blue", "red"), lty = c(2,1), cex = 0.8)

To get for all individuals:

means <- rowMeans(posterior_gv_samples)
limits <- t(apply(posterior_gv_samples, 1, quantile, probs = c(bound, 1 - bound)))