PRS-CS
Single-population continuous-shrinkage scoring
PRS-CS infers posterior SNP effect sizes from one GWAS using an external LD reference panel and a continuous-shrinkage prior.
Prefer a reusable file? Download the PRS-CS shell template, edit its inputs, and begin with the chromosome 22 test.
Install and check
git clone https://github.com/getian107/PRScs.git
python -m pip install numpy scipy h5py
python PRScs/PRScs.py --helpDownload and extract the required LD panel from the official PRS-CS repository. Available reference populations include AFR, AMR, EAS, EUR, and SAS for the 1000 Genomes and UK Biobank panels.
For PRS-CS, --ref_dir points directly to one population-specific folder, such as ldblk_1kg_eur. Match this folder to the discovery GWAS ancestry.
Define the inputs
PRSCS_DIR=/path/to/PRScs
REF_DIR=/path/to/reference/ldblk_1kg_eur
TARGET_PREFIX=/path/to/target/target_genotypes
SUMSTATS=/path/to/gwas/sumstats.txt
OUTPUT_PREFIX=/path/to/results/prscs_eur
GWAS_N=200000
mkdir -p /path/to/results
# Avoid taking every available thread on a shared compute node.
export MKL_NUM_THREADS=1
export NUMEXPR_NUM_THREADS=1
export OMP_NUM_THREADS=1TARGET_PREFIX is the PLINK prefix without .bed, .bim, or .fam.
Test chromosome 22
Starting with chromosome 22 makes it easier to check paths, formatting, and output names before launching the complete analysis.
python "${PRSCS_DIR}/PRScs.py" \
--ref_dir="${REF_DIR}" \
--bim_prefix="${TARGET_PREFIX}" \
--sst_file="${SUMSTATS}" \
--n_gwas="${GWAS_N}" \
--chrom=22 \
--out_dir="${OUTPUT_PREFIX}"If --phi is omitted, PRS-CS learns the global shrinkage parameter from the data. The official documentation notes that this usually works best for very large GWAS. For smaller GWAS, fixed values or a validation-set grid search may perform better.
Run the autosomes
Run each chromosome as a separate scheduler job when possible. A simple sequential shell loop is:
for CHR in $(seq 1 22); do
python "${PRSCS_DIR}/PRScs.py" \
--ref_dir="${REF_DIR}" \
--bim_prefix="${TARGET_PREFIX}" \
--sst_file="${SUMSTATS}" \
--n_gwas="${GWAS_N}" \
--chrom="${CHR}" \
--out_dir="${OUTPUT_PREFIX}"
doneCombine posterior effects
Inspect the filenames first because the middle portion records the prior settings:
ls "${OUTPUT_PREFIX}"_pst_eff_*_chr*.txt
cat "${OUTPUT_PREFIX}"_pst_eff_*_chr*.txt \
> "${OUTPUT_PREFIX}"_pst_eff_all.txtThe output columns are chromosome, SNP, base-pair position, A1, A2, and posterior effect size.
Calculate individual scores
plink \
--bfile "${TARGET_PREFIX}" \
--score "${OUTPUT_PREFIX}_pst_eff_all.txt" 2 4 6 sum center \
--out "${OUTPUT_PREFIX}_score"If scores are calculated separately by chromosome, retain PLINK’s sum modifier before combining them.
Principal components
plink \
--bfile "${TARGET_PREFIX}" \
--pca 10 \
--out "${TARGET_PREFIX}_pca10"Basic evaluation in R
library(dplyr)
library(ggplot2)
score <- read.table("prscs_eur_score.profile", header = TRUE) |>
transmute(FID, IID, PRS = SCORE)
fam <- read.table("target_genotypes.fam", header = FALSE)
names(fam) <- c("FID", "IID", "PAT", "MAT", "SEX", "PHENO")
pcs <- read.table("target_genotypes_pca10.eigenvec", header = FALSE)
names(pcs) <- c("FID", "IID", paste0("PC", 1:10))
analysis <- score |>
inner_join(fam, by = c("FID", "IID")) |>
inner_join(pcs, by = c("FID", "IID")) |>
mutate(
PRS_z = as.numeric(scale(PRS)),
case = factor(PHENO, levels = c(1, 2), labels = c("Control", "Case"))
)
ggplot(analysis, aes(PRS_z, fill = case)) +
geom_density(alpha = 0.45) +
labs(x = "Standardized polygenic score", y = "Density", fill = NULL) +
theme_classic()
null_model <- glm(
case ~ PC1 + PC2 + PC3 + PC4 + PC5 + PC6 + PC7 + PC8 + PC9 + PC10,
family = binomial(),
data = analysis
)
full_model <- update(null_model, . ~ . + PRS_z)
summary(full_model)Do not tune phi, select covariates, or report predictive performance in the same sample used for the final evaluation. The appropriate model and metrics depend on the study design and outcome.