Package: viprs
viprs: Variational Inference of Polygenic Risk Scoresviprsis a python package that implements scripts and utilities for running variational inference
algorithms on genome-wide association study (GWAS) data for the purposes polygenic risk estimation.Highlighted features:The coordinate ascent algorithms are written incythonfor improved speed and efficiency.The code is written in object-oriented form, allowing the user to extend and experiment with existing implementations.We provide scripts for fitting the model with different priors on the effect size:VIPRSAlpha,VIPRSMix, etc.We also provide scripts for different hyperparameter tuning strategies, including: Grid search, Bayesian optimization, Bayesian model averaging.NOTE: The codebase is still in active development and some of interfaces or data structures will be
replaced or modified in future releases.Table of ContentsInstallationGetting startedFeatures and Configurations(1) Evaluatingviprswith training and test data(2) Grid search and validationCommandline scriptsDownload LD matricesFrequently Asked Questions (FAQs)CitationsInstallationviprsis now available on the python package indexpypiand
can be minimally installed using the package installerpip:pipinstallviprsTo access the full functionalities ofviprs, however, it is recommended that
you install the full list of dependencies:pipinstallviprs[full]To useviprson a shared computing cluster, we recommend installing it in apythonvirtual environment. For example:moduleloadpython/3.8
python-mvenvviprs_envsourceviprs_env/bin/activate
pipinstall--upgradepip
pipinstallviprsFinally, if you wish to install the package from source,
you can directly clone the GitHub repository and install it locally
as follows:git clone https://github.com/shz9/viprs.git
cd viprs
make installGetting startedviprsis apythonpackage for fitting Bayesian Polygenic Risk Score (PRS) models to summary statistics
derived from Genome-wide Association Studies (GWASs). To showcase the interfaces and functionalities of the package
as well as the data structures that power it, we will start with a simple example.Generally, summary statistics-based PRS methods require access to(1)GWAS summary statistics for the
trait of interest and(2)Linkage-Disequilibrium (LD) matrices from an appropriately-matched reference panel (e.g.
the 1000G dataset). For the first item, we will use summary statistics for standing height from thefastGWAcatalogue.
For the second item, we will use genotype data on chromosome 22 for a subset of 378 European samples from the
1000G project. This small dataset is shipped with the python packagemagenpy.To start, let's import the requiredpythonmodules:importmagenpyasmgpimportviprsasvpThen, we will usemagenpyto read the 1000G genotype dataset andautomaticallymatch it with the GWAS
summary statistics fromfastGWA:# Load genotype and GWAS summary statistics data (chromosome 22):gdl=mgp.GWADataLoader(bed_files=mgp.tgp_eur_data_path(),sumstats_files=mgp.ukb_height_fastGWA_path(),sumstats_format="fastGWA")Once the genotype and summary statistics data are read bymagenpy, we can go ahead and compute
the LD (or SNP-by-SNP correlation) matrix:# Compute LD using the shrinkage estimator (Wen and Stephens 2010):gdl.compute_ld("shrinkage",output_dir="temp",genetic_map_ne=11400,# effective population size (Ne)genetic_map_sample_size=183,threshold=1e-3)Because of the small sample size of the reference panel, here we recommend using theshrinkageestimator
for LD from Wen and Stephens (2010). The shrinkage estimator results in compact and sparse LD matrices that are
more robust than the sample LD. The estimator requires access to information about the genetic map, such as
the position of each SNP in centi Morgan, the effective population size, and the sample size used to
estimate the genetic map.Given the LD information from the reference panel, we can next fit the VIPRS model to the summary statistics data:# Initialize VIPRS, passing it the GWADataLoader objectv=vp.VIPRS(gdl)# Invoke the .fit() method to obtain posterior estimatesv.fit()Once the model converges, we can generate PRS estimates for height for the 1000G samples by simply
invoking the.predict()method:v.predict()# array([ 0.01944202,  0.00597704,  0.07329462, ..., 0.06666187,  0.05251297,  0.00359018])To examine posterior estimates for the model parameters, you can simply invoke the.to_table()method:v.to_table()#       CHR         SNP A1 A2       PIP          BETA      VAR_BETA# 0       22    rs131538  A  G  0.006107 -5.955517e-06  1.874619e-08# 1       22   rs9605903  C  T  0.005927  5.527188e-06  1.774252e-08# 2       22   rs5746647  G  T  0.005015  1.194178e-07  1.120063e-08# 3       22  rs16980739  T  C  0.008331 -1.335695e-05  3.717944e-08# 4       22   rs9605923  A  T  0.006181  6.334971e-06  1.979157e-08# ...    ...         ... .. ..       ...           ...           ...# 15930   22   rs8137951  A  G  0.006367 -6.880591e-06  2.059650e-08# 15931   22   rs2301584  A  G  0.179406 -7.234545e-04  2.597197e-06# 15932   22   rs3810648  G  A  0.008000  1.222151e-05  3.399927e-08# 15933   22   rs2285395  A  G  0.005356  3.004282e-06  1.349082e-08# 15934   22  rs28729663  A  G  0.005350 -2.781053e-06  1.351239e-08## [15935 rows x 7 columns]Here,PIPis thePosteriorInclusionProbability under the variational density, whileBETAandVAR_BETAare the posterior mean and variance for the effect size, respectively.
For the purposes of prediction, we only need theBETAcolumn. You can also examine the
inferred hyperparameters of the model by invoking the.to_theta_table()method:v.to_theta_table()#           Parameter     Value# 0  Residual_variance  0.994231# 1       Heritability  0.005736# 2  Proportion_causal  0.015887# 3         sigma_beta  0.000021Note that here, the SNP heritability only considers the contribution of variants on
chromosome 22.Features and Configurations(1) Evaluatingviprswith training and test dataHere, we will go through an artificial example on simulated
phenotypes using a subset of 387 individuals from the 1000 Genomes dataset. To begin, let's use the python
packagemagenpyto simulate a quantitative trait where 1% of the variants
are causal and the SNP heritability set to0.8:g_sim=mgp.GWASimulator(mgp.tgp_eur_data_path(),pi=(.99,.01),h2=0.8)g_sim.simulate()g_sim.to_phenotype_table()#          FID      IID  phenotype# 0    HG00096  HG00096  -1.787443# 1    HG00097  HG00097   0.282521# 2    HG00099  HG00099   0.532999# 3    HG00100  HG00100   0.781203# 4    HG00101  HG00101   0.081900# ..       ...      ...        ...# 373  NA20815  NA20815   0.898588# 374  NA20818  NA20818   1.549989# 375  NA20819  NA20819  -0.259188# 376  NA20826  NA20826  -0.044187# 377  NA20828  NA20828   2.254222## [378 rows x 3 columns]To obtain a realistic setting where the training data is separate from the test data, next we are going to
randomly split the samples into 80% training and 20% testing:training_gdl,test_gdl=g_sim.split_by_samples(proportions=[.8,.2])print("Number of training samples:",training_gdl.sample_size)# Number of training samples: 314print("Number of test samples:",test_gdl.sample_size)# Number of test samples: 64Using the training data, we will compute LD matrices and perform GWAS
on the simulated phenotype:# Compute LD using the shrinkage estimator:training_gdl.compute_ld('shrinkage',output_dir='temp',genetic_map_ne=11400,# effective population size (Ne)genetic_map_sample_size=183,threshold=1e-3)# Perform GWAS on the simulated phenotype:training_gdl.perform_gwas()And that is all we need to fit theVIPRSmethod to the training data! To perform model fit,
simply pass theg_simobject toVIPRSand then invoke the.fit()method:v=vp.VIPRS(training_gdl)v.fit()If everything works as expected, the method will converge reasonably fast and you should be able to
use object to perform predictions on the test set. Once convergence is achieved, you can compute
polygenic risk scores (PRSs) for the test samples. This can be simply done by invoking the.predict()method and
passing thetest_gdlobject as an argument, as follows:test_prs=v.predict(test_gdl)For the sake of comparison, we will also include the PRS based on the marginal effect sizes from GWAS:naive_prs=test_gdl.predict(beta={c:ss.marginal_betaforc,ssintraining_gdl.sumstats_table.items()})To evaluate the accuracy of the polygenic score, you can use some of the provided metrics with theviprspackage.
For quantitative traits, it is customary to report the prediction R^2, or the proportion
of variance explained by the PRS:fromviprs.eval.metricsimportr2r2(test_prs,test_gdl.sample_table.phenotype)# 0.171429The prediction R^2 here is much lower than we would expect, given our simulation
scenario. This is again largely due to the small sample size that we are working with and
the noise this induces on the estimates of LD, GWAS summary statistics, etc. For reference,
we can compare this to the R^2 from the "naive" PRS estimator:r2(naive_prs,test_gdl.sample_table.phenotype)# 0.169567In this artificial setting,viprsresults in minor improvements in prediction accuracy
on the test set compared to the naive PRS estimator. However, as we show in our manuscript, this
improvement is more pronounced when we use larger sample sizes for both GWAS and the LD reference panel.(2) Grid search and validationIn our manuscript, we found that VIPRS paired with grid search with a held-out validation set
performs favorably compared to the vanilla model. Here, we will illustrate how to use the grid search functionalities
implemented in theviprspackage. To start, let's use the same simulation scenario as before:g_sim=mgp.GWASimulator(mgp.tgp_eur_data_path(),pi=(.99,.01),h2=0.8)g_sim.simulate()g_sim.to_phenotype_table()#         FID      IID  phenotype# 0    HG00096  HG00096  -0.283120# 1    HG00097  HG00097   0.060302# 2    HG00099  HG00099  -0.571485# 3    HG00100  HG00100   1.145548# 4    HG00101  HG00101   0.535409# ..       ...      ...        ...# 373  NA20815  NA20815   0.214542# 374  NA20818  NA20818  -0.986338# 375  NA20819  NA20819  -0.056678# 376  NA20826  NA20826   0.119752# 377  NA20828  NA20828   0.889052## [378 rows x 3 columns]Then, we will split this small dataset into 70% training, 15% validation, and 15% testing:training_gdl,validation_gdl,test_gdl=g_sim.split_by_samples(proportions=[.7,.15,.15])print("Number of training samples:",training_gdl.sample_size)# Number of training samples: 251print("Number of validation samples:",validation_gdl.sample_size)# Number of validation samples: 66print("Number of test samples:",test_gdl.sample_size)# Number of test samples: 61As before, we will compute the LD and perform GWAS on the training samples:# Compute LD using the shrinkage estimator:training_gdl.compute_ld('shrinkage',output_dir='temp',genetic_map_ne=11400,# effective population size (Ne)genetic_map_sample_size=183,threshold=1e-3)# Perform GWAS on the simulated phenotype:training_gdl.perform_gwas()For a simple baseline, we'll examine the performance of the standard VIPRS model
without any grid search:v=vp.VIPRS(training_gdl).fit()Once converged, for this simulation scenario, the standard VIPRS results in the following prediction R^2:fromviprs.eval.metricsimportr2r2(v.predict(test_gdl),test_gdl.sample_table.phenotype)0.168073To see how grid search would perform in comparison, first we construct a grid
for some of the hyperparameters of interest, includingpiandsigma_epsilon, the
proportion of causal variants and the residual variance, respectively:# Create a grid:grid=vp.HyperparameterGrid()# Generate a grid for pi using 5 equidistant grid points:grid.generate_pi_grid(steps=5,n_snps=training_gdl.n_snps)# Generate a grid for sigma epsilon using 5 equidistant grid points:grid.generate_sigma_epsilon_grid(steps=5)This will generate a grid of 5x5=25 hyperparameter settings that we will search over.
To fit the VIPRS model with this grid, we can simply use theGridSearchclass, passing it
the training data as well as the grid that we just created:vgv_gs=vp.VIPRSGridSearch(training_gdl,grid)vgv_gs=vgv_gs.fit()vgv.select_best_model(validation_gdl=validation_gdl,criterion='validation')Here, in order to make use of the held-out validation data, we pass it theGridSearchobject and
we also specify that the objective that we wish to optimize is thevalidationR^2. When we call.fit()on
theGridSearchobject, it will fit 25 different VIPRS models, each with a different hyperparameter setting
and then choose the one with the best predictive performance on the held-out validation set.Given this "best" model, we can using to make predictions and test prediction accuracy on the held-out test set:r2(vgv.predict(test_gdl),test_gdl.sample_table.phenotype)# 0.167938In this case, the prediction R^2 is actually marginally lower than standard VIPRS model, which can happen in some
cases, as illustrated in the simulation results in our manuscript. As our results showed, the main advantage of
theGridSearchmodule will be when applied on real phenotypes or when there are some mismatches between
the LD reference panel and the GWAS summary statistics.Commandline scriptsIf you are not comfortable programming inpythonand would like to access some of the functionalities
ofviprswith minimal interaction withpythoncode, we packaged a number of commandline
scripts that can be useful for some downstream applications.The binaries that are currently supported are:viprs_fit: For performing model fit and estimating the posterior for the effect sizes.viprs_score: For generating polygenic scores for new test samples.viprs_evaluate: For evaluating the predictive performance of PRS methods.Once you installviprsviapip, these scripts will be added to the systemPATHand you can invoke them directly from the commandline, as follows:$viprs_fit-h**********************************************_____________(_)__________________________|//__/_____\_____/_____/__|//_/__/_//_/_(__)_____//_/_.___//_//____//_/VariationalInferenceofPolygenicRiskScoresVersion:0.0.2|Releasedate:June2022Author:ShadiZabad,McGillUniversity**********************************************<FitVIPRSmodeltoGWASsummarystatistics>usage:viprs_fit[-h]-lLD_DIR-sSUMSTATS_PATH--output-fileOUTPUT_FILE[--sumstats-format{fastGWA,custom,plink,magenpy,COJO}][--snpSNP][--a1A1][--n-per-snpN_PER_SNP][--z-scoreZ_SCORE][--betaBETA][--seSE][--temp-dirTEMP_DIR][--validation-bedVALIDATION_BED][--validation-phenoVALIDATION_PHENO][--validation-keepVALIDATION_KEEP][-m{VIPRS,VIPRSMix,VIPRSAlpha}][--annealing-schedule{harmonic,linear,geometric}][--annealing-stepsANNEALING_STEPS][--initial-temperatureINITIAL_TEMPERATURE][--n-componentsN_COMPONENTS][--prior-multPRIOR_MULT][--hyp-search{EM,BMA,GS,BO}][--grid-metric{validation,pseudo_validation,ELBO}][--opt-paramsOPT_PARAMS][--pi-gridPI_GRID][--pi-stepsPI_STEPS][--sigma-epsilon-gridSIGMA_EPSILON_GRID][--sigma-epsilon-stepsSIGMA_EPSILON_STEPS][--sigma-beta-gridSIGMA_BETA_GRID][--sigma-beta-stepsSIGMA_BETA_STEPS][--h2-informed-grid][--compress][--genomewide][--backend{xarray,plink}][--max-attemptsMAX_ATTEMPTS][--use-multiprocessing][--n-jobsN_JOBS]CommandlineargumentsforfittingtheVIPRSmodels

optionalarguments:-h,--helpshowthishelpmessageandexit-lLD_DIR,--ld-panelLD_DIRThepathtothedirectorywheretheLDmatricesarestored.Canbeawildcardoftheformld/chr_*-sSUMSTATS_PATH,--sumstatsSUMSTATS_PATHThesummarystatisticsdirectoryorfile.Canbeawildcardoftheformsumstats/chr_*--output-fileOUTPUT_FILETheoutputfilewheretostoretheinferenceresults.Onlyincludetheprefix,theextensionswillbeaddedautomatically.--sumstats-format{fastGWA,custom,plink,magenpy,COJO}Theformatforthesummarystatisticsfile(s).--snpSNPThecolumnnamefortheSNPrsIDinthesummarystatisticsfile(customformats).--a1A1Thecolumnnamefortheeffectalleleinthesummarystatisticsfile(customformats).--n-per-snpN_PER_SNPThecolumnnameforthesamplesizeperSNPinthesummarystatisticsfile(customformats).--z-scoreZ_SCOREThecolumnnameforthez-scoreinthesummarystatisticsfile(customformats).--betaBETAThecolumnnameforthebeta(effectsizeestimate)inthesummarystatisticsfile(customformats).--seSEThecolumnnameforthestandarderrorinthesummarystatisticsfile(customformats).--temp-dirTEMP_DIRThetemporarydirectorywheretostoreintermediatefiles.--validation-bedVALIDATION_BEDTheBEDfilescontainingthegenotypedataforthevalidationset.Youmayuseawildcardhere(e.g."data/chr_*.bed")--validation-phenoVALIDATION_PHENOAtab-separatedfilecontainingthephenotypeforthevalidationset.Theexpectedformatis:FIDIIDphenotype(noheader)--validation-keepVALIDATION_KEEPAplink-stylekeepfiletoselectasubsetofindividualsforthevalidationset.-m{VIPRS,VIPRSMix,VIPRSAlpha},--model{VIPRS,VIPRSMix,VIPRSAlpha}ThePRSmodeltofit--annealing-schedule{harmonic,linear,geometric}Thetypeofscheduleforupdatingthetemperatureparameterindeterministicannealing.--annealing-stepsANNEALING_STEPSThenumberofdeterministicannealingstepstoperform.--initial-temperatureINITIAL_TEMPERATURETheinitialtemperatureforthedeterministicannealingprocedure.--n-componentsN_COMPONENTSThenumberofnon-nullGaussianmixturecomponentstousewiththeVIPRSMixmodel(i.e.excludingthespikecomponent).--prior-multPRIOR_MULTPriormultipliersonthevarianceofthenon-nullGaussianmixturecomponent.--hyp-search{EM,BMA,GS,BO}Thestrategyfortuningthehyperparametersofthemodel.OptionsareEM(Expectation-Maximization),GS(Gridsearch),BO(BayesianOptimization),andBMA(BayesianModelAveraging).--grid-metric{validation,pseudo_validation,ELBO}Themetricforselectingbestperformingmodelingridsearch.--opt-paramsOPT_PARAMSThehyperparameterstotuneusingGridSearch/BMA/Bayesianoptimization(comma-separated).Possiblevaluesarepi,sigma_beta,andsigma_epsilon.Oracombinationofthem.--pi-gridPI_GRIDAcomma-separatedgridvaluesforthehyperparameterpi(seealso--pi-steps).--pi-stepsPI_STEPSThenumberofstepsforthe(default)pigrid.Thiswillcreateanequidistantgridbetween1/Mand(M-1)/Monalog10scale,whereMisthenumberofSNPs.--sigma-epsilon-gridSIGMA_EPSILON_GRIDAcomma-separatedgridvaluesforthehyperparametersigma_epsilon(seealso--sigma-epsilon-steps).--sigma-epsilon-stepsSIGMA_EPSILON_STEPSThenumberofstepsforthe(default)sigma_epsilongrid.--sigma-beta-gridSIGMA_BETA_GRIDAcomma-separatedgridvaluesforthehyperparametersigma_beta(seealso--sigma-beta-steps).--sigma-beta-stepsSIGMA_BETA_STEPSThenumberofstepsforthe(default)sigma_betagrid.--h2-informed-gridConstructagridforsigma_epsilon/sigma_betabasedoninformedestimatesofthetraitheritability.--compressCompresstheoutputfiles--genomewideFitallchromosomesjointly--backend{xarray,plink}Thebackendsoftwareusedforcomputationsonthegenotypematrix.--max-attemptsMAX_ATTEMPTSThemaximumnumberofmodelrestarts(incaseofoptimizationdivergenceissues).--use-multiprocessingUsemultiprocessingwhereapplicable.Fornow,thismainlyaffectstheGridSearch/BayesianModelAveragingimplementations.--n-jobsN_JOBSThenumberofprocesses/threadstolaunchforthehyperparametersearch(defaultis1,butwerecommendincreasingthisdependingonsystemcapacity).$viprs_score-h**********************************************_____________(_)__________________________|//__/_____\_____/_____/__|//_/__/_//_/_(__)_____//_/_.___//_//____//_/VariationalInferenceofPolygenicRiskScoresVersion:0.0.2|Releasedate:June2022Author:ShadiZabad,McGillUniversity**********************************************<Computepolygenicscoresfortestsamples>

usage:viprs_score[-h]-fFIT_FILES--bed-filesBED_FILES--output-fileOUTPUT_FILE[--temp-dirTEMP_DIR][--keepKEEP][--extractEXTRACT][--backend{xarray,plink}][--n-threadsN_THREADS][--compress]Commandlineargumentsforgeneratingpolygenicscores

optionalarguments:-h,--helpshowthishelpmessageandexit-fFIT_FILES,--fit-filesFIT_FILESThepathtothefile(s)withtheoutputparameterestimatesfromVIPRS.Youmayuseawildcardhere(e.g."prs/chr_*.fit")--bed-filesBED_FILESTheBEDfilescontainingthegenotypedata.Youmayuseawildcardhere(e.g."data/chr_*.bed")--output-fileOUTPUT_FILETheoutputfilewheretostorethepolygenicscores(withnoextension).--temp-dirTEMP_DIRThetemporarydirectorywheretostoreintermediatefiles.--keepKEEPAplink-stylekeepfiletoselectasubsetofindividualsforthetestset.--extractEXTRACTAplink-styleextractfiletoselectasubsetofSNPsforscoring.--backend{xarray,plink}Thebackendsoftwareusedforcomputationswiththegenotypematrix.--n-threadsN_THREADSThenumberofthreadstouseforcomputations.--compressCompresstheoutputfile$viprs_evaluate-h**************************************************_____________(_)__________________________|//__/_____\_____/_____/__|//_/__/_//_/_(__)_____//_/_.___//_//____//_/VariationalInferenceofPolygenicRiskScoresVersion:0.0.2|Releasedate:June2022Author:ShadiZabad,McGillUniversity**************************************************<EvaluatethepredictiveperformanceofPRS>

usage:viprs_evaluate[-h]--prs-filePRS_FILE--phenotype-filePHENO_FILE[--phenotype-likelihood{binomial,gaussian}]--output-fileOUTPUT_FILE[--covariate-fileCOVARIATE_FILE]Commandlineargumentsforevaluatingpolygenicscoreestimates

optionalarguments:-h,--helpshowthishelpmessageandexit--prs-filePRS_FILEThepathtothePRSfile(expectedformat:FIDIIDPRS,tab-separated)--phenotype-filePHENO_FILEThepathtothephenotypefile.Theexpectedformatis:FIDIIDphenotype(noheader),tab-separated.--phenotype-likelihood{binomial,gaussian}Thephenotypelikelihood("gaussian"forcontinuous,"binomial"forcase-control).--output-fileOUTPUT_FILETheoutputfilewheretostoretheevaluationmetrics(withnoextension).--covariate-fileCOVARIATE_FILEAfilewithcovariatesforthesamplesincludedintheanalysis.Thistab-separatedfileshouldnothaveaheaderandthefirsttwocolumnsshouldbetheFIDandIIDofthesamples.Download LD matricesTo run theviprssoftware, you may need access to pre-computed LD matrices. Here, we provide access to pre-computed LD matrices
from the UK Biobank (UKB) dataset:LD matrices derived from the "White British" cohort (N=50,000) in the UKB:You can also use thedata_utilsmodule from theviprspackage to download those
data sources. For example, to download the UKB LD matrices listed above, you can do
the following:importviprsasvp# Download all matrices from Zenodo:vp.download_ukb_wb_ld_matrix('data/ld/')# Download LD matrices for chromosomes 21 and 22:vp.download_ukb_wb_ld_matrix('data/ld/',chromosome=[21,22])This will download the LD matrix to a sub-folder in the working directory calleddata/ld. We will add more utilities
like this in the future to streamline PRS model training and testing.Frequently Asked Questions (FAQs)How do I create my own LD matrices and store them in formats compatible withviprs?You can use the software packagemagenpyto compute LD matrices
from any reference genotype dataset.magenpyprovides interfaces for computing LD matrices
using 3 commonly used LD estimators:shrinkage,windowed(or banded), andblock-based LD matrices. Check the relevant documentationhere.CitationsShadi Zabad, Simon Gravel, Yue Li.Fast and Accurate Bayesian Polygenic Risk Modeling with Variational Inference. (2022)@article{Zabad2022.05.10.491396,author={Zabad, Shadi and Gravel, Simon and Li, Yue},title={Fast and Accurate Bayesian Polygenic Risk Modeling with Variational Inference},elocation-id={2022.05.10.491396},year={2022},doi={10.1101/2022.05.10.491396},publisher={Cold Spring Harbor Laboratory},URL={https://www.biorxiv.org/content/early/2022/05/11/2022.05.10.491396},journal={bioRxiv}}