๐Ÿงฌ GWAS
HOME

๐Ÿง  GWAS Fundamentals

๐ŸŽฏ What is GWAS?

GWAS (Genome-Wide Association Study) is a method to identify genetic variants (usually SNPs) associated with a trait or disease. It tests millions of SNPs across the genome for statistical association with a phenotype, using large cohorts of individuals. GWAS has revolutionized our understanding of the genetic basis of complex traits and diseases, from height and BMI to diabetes and schizophrenia.

Why GWAS Matters

Discovery: Find risk variants. Biology: Understand mechanisms. Prediction: Polygenic scores. Drugs: Target identification. Personalized: Precision medicine. Populations: Evolutionary insights.

๐Ÿ“Œ SNP๐Ÿ“Œ Association๐Ÿ“Œ Structure๐Ÿ“Œ LD
# GWAS & Population Genetics โ€” the big picture # Finding genetic variants associated with traits. print(""" === GWAS & Population Genetics === What it is: - Genome-wide association study - Millions of SNPs - Trait / disease - Statistical association Why it matters: - Discover risk variants - Understand biology - Polygenic scores - Drug targets - Precision medicine - Evolutionary insights Key concepts: - SNP (single nucleotide polymorphism) - Linkage disequilibrium (LD) - Hardy-Weinberg equilibrium (HWE) - Population structure - Association testing - Mixed models - Manhattan plot - QQ plot - Fine-mapping - Imputation Applications: - Disease genetics - Complex traits - Agriculture - Evolutionary biology - Pharmacogenomics - Personalized medicine """) print("=== Historical Context ===") print(""" 2005: First GWAS (age-related macular degeneration) 2007: Wellcome Trust Case Control Consortium 2010: Thousands of GWAS 2015: Biobanks (UK Biobank) 2020: Millions of individuals 2025: Multi-omics, diverse populations, PRS """) print("=== Why It's Hard ===") print(""" 1. MULTIPLE TESTING - Millions of SNPs - Bonferroni: 5e-8 - False positives 2. POPULATION STRUCTURE - Ancestry differences - Spurious associations - Correction needed 3. LINKAGE DISEQUILIBRIUM - Correlated SNPs - Fine-mapping hard - Causal variants 4. EFFECT SIZE - Small effects - Large samples needed - Missing heritability 5. RARE VARIANTS - Not on arrays - Sequencing needed - Statistical power """)

๐ŸŽฏ Why GWAS Matters

๐ŸŽฏ The Value of GWAS

GWAS has identified thousands of genetic variants associated with hundreds of traits and diseases, transforming our understanding of complex genetics.

Key Benefits

Discovery: Risk variants. Biology: Mechanisms. Prediction: PRS. Drugs: Targets. Precision: Medicine. Evolution: Selection.

๐ŸŽฏ Discovery๐ŸŽฏ Biology๐ŸŽฏ Prediction๐ŸŽฏ Drugs
# Why GWAS Matters print("=== 1. Discovery of Risk Variants ===") print(""" Discovery: - Thousands of associations - Many traits - Many diseases Example: Height: ~10,000 SNPs BMI: ~1,000 SNPs Schizophrenia: ~300 loci Diabetes: ~400 loci """) print("\n=== 2. Understanding Biology ===") print(""" Biology: - Mechanisms - Pathways - Drug targets Example: PCSK9 โ†’ cholesterol โ†’ Drug (evolocumab) โ†’ Approved """) print("\n=== 3. Polygenic Scores ===") print(""" PRS: - Polygenic risk score - Sum of effect sizes - Prediction Example: Height PRS โ†’ Predict height โ†’ ~80% accuracy """) print("\n=== 4. Drug Discovery ===") print(""" Drug: - Target identification - Validation - Repurposing Example: GWAS โ†’ target โ†’ Drug โ†’ Clinical trial """) print("\n=== 5. Precision Medicine ===") print(""" Precision: - Personalized risk - Treatment - Prevention Example: Breast cancer โ†’ BRCA1/2 โ†’ Screening """) print("\n=== 6. Evolutionary Insights ===") print(""" Evolution: - Selection - Adaptation - Migration Example: Lactase persistence โ†’ Selection โ†’ Europeans """) print("\n=== 7. Agriculture ===") print(""" Agriculture: - Crop traits - Breeding - Food security Example: Rice yield โ†’ GWAS โ†’ Breeding """) print("\n=== 8. Pharmacogenomics ===") print(""" Pharmacogenomics: - Drug response - Adverse effects - Dosing Example: CYP2C19 โ†’ Clopidogrel โ†’ Dosing """) print("\n=== 9. Missing Heritability ===") print(""" Missing heritability: - GWAS explains some - Rare variants - Epistasis - Epigenetics Example: Height: ~80% heritable GWAS: ~50% explained Missing: ~30% """) print("\n=== 10. Multi-Omics ===") print(""" Multi-omics: - GWAS + transcriptomics - GWAS + proteomics - GWAS + metabolomics Example: eQTL โ†’ Gene expression โ†’ Mechanism """)

๐Ÿ”ฌ SNPs & Genotyping

๐ŸŽฏ Single Nucleotide Polymorphisms

SNPs (Single Nucleotide Polymorphisms) are the most common genetic variants, occurring at ~1 in 1,000 bases. They are the primary markers used in GWAS.

SNPs

Definition: Single base change. Frequency: >1%. Genotyping: Arrays. Imputation: Fill missing. MAF: Minor allele. HWE: Equilibrium.

๐Ÿ”ฌ SNP๐Ÿ”ฌ MAF๐Ÿ”ฌ Genotyping๐Ÿ”ฌ Imputation
# SNPs & Genotyping print("=== What is a SNP? ===") print(""" SNP: - Single base change - A โ†’ T, C โ†’ G, etc. - > 1% frequency - Most common variant Example: Individual 1: ...ACGT... Individual 2: ...ATGT... โ†’ SNP at position 2 (C/T) """) print("\n=== SNP Types ===") print(""" Types: - Transition: Aโ†”G, Cโ†”T - Transversion: Aโ†”C, Aโ†”T, Gโ†”C, Gโ†”T - Synonymous: no amino acid change - Non-synonymous: amino acid change Example: rs123456 โ†’ A/G """) print("\n=== Genotyping ===") print(""" Genotyping: - Microarray - Sequencing - TaqMan - MassArray Arrays: - Illumina - Affymetrix - ~500K-2M SNPs Example: Illumina Global Screening Array โ†’ 700K SNPs """) print("\n=== Genotype Calls ===") print(""" Genotype: - 0/0: homozygous reference - 0/1: heterozygous - 1/1: homozygous alternate Example: rs123456 Individual 1: 0/0 (AA) Individual 2: 0/1 (AG) Individual 3: 1/1 (GG) """) print("\n=== MAF ===") print(""" MAF: - Minor allele frequency - Frequency of minor allele - 0 to 0.5 Example: Allele A: 70% Allele G: 30% MAF = 0.3 """) print("\n=== HWE ===") print(""" HWE: - Hardy-Weinberg equilibrium - Expected genotype frequencies - pยฒ + 2pq + qยฒ = 1 Example: p = 0.7 (A) q = 0.3 (G) AA: 0.49 AG: 0.42 GG: 0.09 """) print("\n=== QC ===") print(""" QC: - Call rate - MAF - HWE - Heterozygosity - Relatedness Example: Call rate > 95% MAF > 1% HWE p > 1e-6 """) print("\n=== Imputation ===") print(""" Imputation: - Fill missing genotypes - Reference panel - Increases SNPs Example: Array: 700K SNPs Imputed: 10M SNPs """) print("\n=== Python Genotype ===") print(""" import numpy as np def genotype_stats(genotypes): \"\"\"Compute genotype statistics.\"\"\" n = len(genotypes) ref = genotypes.count(0) + genotypes.count('0/0') het = genotypes.count(1) + genotypes.count('0/1') alt = genotypes.count(2) + genotypes.count('1/1') maf = (2 * alt + het) / (2 * n) return { 'ref': ref, 'het': het, 'alt': alt, 'maf': maf } # Example genotypes = [0, 0, 1, 1, 2, 1, 0, 1] stats = genotype_stats(genotypes) print(stats) """) print("\n=== Use Cases ===") print(""" Use cases: - GWAS - Population genetics - Forensics - Ancestry - Pharmacogenomics - Breeding """)

๐Ÿ”— Linkage Disequilibrium

๐ŸŽฏ Non-Random Association

Linkage disequilibrium (LD) is the non-random association of alleles at different loci. High LD means alleles are inherited together more often than expected by chance.

LD

Definition: Non-random. D: Disequilibrium. D': Normalized. rยฒ: Correlation. Blocks: Haplotypes. Fine-mapping: Use LD.

๐Ÿ”— D๐Ÿ”— D'๐Ÿ”— rยฒ๐Ÿ”— Blocks
# Linkage Disequilibrium print("=== What is LD? ===") print(""" LD: - Non-random association - Alleles at different loci - Inherited together - More than expected Example: Locus A: A/G Locus B: C/T If A always with C: โ†’ LD """) print("\n=== Measures ===") print(""" D: - Disequilibrium coefficient - D = P(AB) - P(A)P(B) - Range: -0.25 to 0.25 D': - Normalized D - Range: 0 to 1 - D' = 1: complete LD rยฒ: - Correlation - Range: 0 to 1 - rยฒ = 1: perfect LD - Most common """) print("\n=== LD Example ===") print(""" Example: Locus A: A (p=0.7), G (q=0.3) Locus B: C (p=0.6), T (q=0.4) Observed: AC: 0.5 AT: 0.2 GC: 0.1 GT: 0.2 Expected (no LD): AC: 0.7*0.6 = 0.42 AT: 0.7*0.4 = 0.28 GC: 0.3*0.6 = 0.18 GT: 0.3*0.4 = 0.12 D = 0.5 - 0.42 = 0.08 โ†’ LD """) print("\n=== LD Blocks ===") print(""" LD blocks: - Regions of high LD - Haplotype blocks - Fewer tag SNPs Example: Block 1: SNPs 1-10 Block 2: SNPs 11-20 Block 3: SNPs 21-30 """) print("\n=== Tag SNPs ===") print(""" Tag SNPs: - Representative SNPs - Capture LD - Reduce genotyping Example: Block with 10 SNPs โ†’ 2 tag SNPs โ†’ Capture all """) print("\n=== LD and GWAS ===") print(""" LD in GWAS: - Association signal - Tag SNP - Causal variant Example: Causal variant not genotyped โ†’ Tag SNP in LD โ†’ Association """) print("\n=== LD and Fine-Mapping ===") print(""" Fine-mapping: - Identify causal variant - LD structure - Credible set Example: Association at rs123 โ†’ LD with rs456 โ†’ Causal? """) print("\n=== LD Calculation ===") print(""" rยฒ: rยฒ = Dยฒ / (p_A * q_A * p_B * q_B) Example: D = 0.08 p_A = 0.7, q_A = 0.3 p_B = 0.6, q_B = 0.4 rยฒ = 0.08ยฒ / (0.7 * 0.3 * 0.6 * 0.4) = 0.0064 / 0.0504 = 0.127 """) print("\n=== Python LD ===") print(""" import numpy as np def ld_r2(geno_a, geno_b): \"\"\"Compute rยฒ between two SNPs.\"\"\" a = np.array(geno_a) b = np.array(geno_b) # Allele frequencies p_a = a.mean() / 2 p_b = b.mean() / 2 # D d = np.mean((a - 2*p_a) * (b - 2*p_b)) / 2 # rยฒ r2 = d**2 / (p_a * (1-p_a) * p_b * (1-p_b)) return r2 # Example a = [0, 0, 1, 1, 2, 1, 0, 1] b = [0, 1, 1, 0, 2, 1, 0, 0] print(f"rยฒ = {ld_r2(a, b):.3f}") """) print("\n=== Use Cases ===") print(""" Use cases: - GWAS - Fine-mapping - Imputation - Tag SNPs - Haplotypes - Selection """)

โš–๏ธ Hardy-Weinberg Equilibrium

๐ŸŽฏ HWE

Hardy-Weinberg Equilibrium (HWE) states that allele and genotype frequencies in a population remain constant from generation to generation in the absence of evolutionary forces.

HWE

Equation: pยฒ + 2pq + qยฒ = 1. p: Allele 1. q: Allele 2. Equilibrium: No evolution. Deviation: QC. Test: Chi-square.

โš–๏ธ pยฒ + 2pq + qยฒโš–๏ธ Chi-squareโš–๏ธ QCโš–๏ธ Deviation
# Hardy-Weinberg Equilibrium print("=== HWE Equation ===") print(""" HWE: - pยฒ + 2pq + qยฒ = 1 - p: frequency of allele A - q: frequency of allele a - p + q = 1 Genotypes: AA: pยฒ Aa: 2pq aa: qยฒ """) print("\n=== Example ===") print(""" Example: p = 0.7 (A) q = 0.3 (a) AA: 0.7ยฒ = 0.49 Aa: 2*0.7*0.3 = 0.42 aa: 0.3ยฒ = 0.09 Total: 1.00 """) print("\n=== HWE Test ===") print(""" Test: - Chi-square - Observed vs expected - p-value Example: Observed: AA: 50 Aa: 40 aa: 10 Expected (p=0.7): AA: 49 Aa: 42 aa: 9 Chi-square = 0.20 p = 0.90 โ†’ HWE """) print("\n=== HWE in GWAS ===") print(""" HWE in GWAS: - QC filter - p > 1e-6 - Remove deviant SNPs Reasons for deviation: - Genotyping error - Population structure - Selection - Copy number """) print("\n=== HWE Python ===") print(""" from scipy.stats import chisquare import numpy as np def hwe_test(obs_aa, obs_ab, obs_bb): \"\"\"Test HWE.\"\"\" n = obs_aa + obs_ab + obs_bb p = (2 * obs_aa + obs_ab) / (2 * n) q = 1 - p exp_aa = p**2 * n exp_ab = 2 * p * q * n exp_bb = q**2 * n chi2, pval = chisquare([obs_aa, obs_ab, obs_bb], [exp_aa, exp_ab, exp_bb]) return chi2, pval # Example chi2, p = hwe_test(50, 40, 10) print(f"Chi2: {chi2:.3f}, p: {p:.3f}") """) print("\n=== Deviation ===") print(""" Deviation: - Excess heterozygotes - Deficiency - Reasons Excess het: - Genotyping error - Population structure Deficiency: - Inbreeding - Selection - Null alleles """) print("\n=== QC Filter ===") print(""" Filter: - HWE p > 1e-6 - Remove deviant - Per SNP Example: PLINK: --hwe 1e-6 """) print("\n=== Use Cases ===") print(""" Use cases: - QC - Population genetics - Selection - Genotyping error - Association """)

๐Ÿ—บ๏ธ Population Structure

๐ŸŽฏ Population Structure

Population structure refers to systematic differences in allele frequencies between subpopulations. It can cause spurious associations in GWAS if not corrected.

Structure

PCA: Principal components. ADMIXTURE: Ancestry. LMM: Mixed models. Correction: Essential. Spurious: False positives. Ancestry: Differences.

๐Ÿ—บ๏ธ PCA๐Ÿ—บ๏ธ ADMIXTURE๐Ÿ—บ๏ธ LMM๐Ÿ—บ๏ธ Correction
# Population Structure print("=== What is Population Structure? ===") print(""" Structure: - Allele frequency differences - Subpopulations - Ancestry Example: European: A allele 70% African: A allele 30% Asian: A allele 50% """) print("\n=== PCA ===") print(""" PCA: - Principal Component Analysis - Dimensionality reduction - Population structure Example: PC1: African vs European PC2: Asian vs European PC3: ... """) print("\n=== ADMIXTURE ===") print(""" ADMIXTURE: - Ancestry estimation - Admixture proportions - K populations Example: Individual 1: 80% EUR, 20% AFR Individual 2: 50% EUR, 50% AFR """) print("\n=== Correction Methods ===") print(""" Methods: 1. PCA correction - Include PCs as covariates 2. Linear mixed models (LMM) - Random effect - Kinship matrix 3. Genomic control - Inflation factor 4. Structured association - ADMIXTURE """) print("\n=== PCA in PLINK ===") print(""" PLINK: --pca 10 โ†’ 10 PCs Example: plink --bfile data --pca 10 --out pca โ†’ pca.eigenvec โ†’ pca.eigenval """) print("\n=== LMM ===") print(""" LMM: - Linear mixed model - Fixed effects: SNPs, covariates - Random effects: kinship - Corrects structure Example: y = Xฮฒ + Zu + e u ~ N(0, ฯƒยฒK) e ~ N(0, ฯƒยฒI) """) print("\n=== Kinship Matrix ===") print(""" Kinship: - Relatedness matrix - Genetic similarity - Random effect Example: K = (1/n) * ฮฃ (G_i - 2p_i)(G_i - 2p_i)^T """) print("\n=== Genomic Control ===") print(""" GC: - Inflation factor ฮป - ฮป = median(ฯ‡ยฒ) / 0.456 - Correct p-values Example: ฮป = 1.0: no inflation ฮป = 1.1: mild inflation ฮป = 1.5: severe inflation """) print("\n=== Python PCA ===") print(""" import numpy as np from sklearn.decomposition import PCA def population_pca(genotypes): \"\"\"PCA on genotypes.\"\"\" pca = PCA(n_components=10) pcs = pca.fit_transform(genotypes) return pcs, pca.explained_variance_ratio_ # Example # genotypes = load_genotypes() # pcs, var = population_pca(genotypes) # print(f"PC1: {var[0]:.2%}") """) print("\n=== Use Cases ===") print(""" Use cases: - GWAS correction - Ancestry inference - Admixture mapping - Population genetics - Evolutionary studies """)

๐Ÿ“Š Association Testing

๐ŸŽฏ Testing SNP-Trait Associations

Association testing tests each SNP for statistical association with a trait. It's the core of GWAS.

Association

Linear: Quantitative. Logistic: Binary. Covariates: Age, sex, PCs. Test: t-test, chi-square. Threshold: 5e-8. Effect: Beta, OR.

๐Ÿ“Š Linear๐Ÿ“Š Logistic๐Ÿ“Š Covariates๐Ÿ“Š Threshold
# Association Testing print("=== Types of Traits ===") print(""" Quantitative: - Height, BMI, blood pressure - Linear regression Binary: - Disease (case/control) - Logistic regression """) print("\n=== Linear Regression ===") print(""" Linear: y = ฮผ + ฮฒ*SNP + covariates + e Test: - H0: ฮฒ = 0 - H1: ฮฒ โ‰  0 - t-test - p-value Example: y = height SNP = 0, 1, 2 ฮฒ = effect size """) print("\n=== Logistic Regression ===") print(""" Logistic: logit(P(y=1)) = ฮผ + ฮฒ*SNP + covariates Test: - H0: ฮฒ = 0 - H1: ฮฒ โ‰  0 - Wald test - p-value Example: y = disease (0/1) SNP = 0, 1, 2 OR = exp(ฮฒ) """) print("\n=== Covariates ===") print(""" Covariates: - Age - Sex - PCs (population structure) - Batch - BMI Example: y ~ SNP + age + sex + PC1 + PC2 + ... """) print("\n=== Multiple Testing ===") print(""" Multiple testing: - Millions of SNPs - Bonferroni: 0.05 / 1M = 5e-8 - FDR Threshold: - Genome-wide: 5e-8 - Suggestive: 1e-5 Example: p < 5e-8: significant p < 1e-5: suggestive """) print("\n=== PLINK Association ===") print(""" PLINK: --linear (quantitative) --logistic (binary) Example: plink --bfile data --linear --pheno pheno.txt --covar covar.txt --out assoc plink --bfile data --logistic --pheno pheno.txt --covar covar.txt --out assoc """) print("\n=== Output ===") print(""" Output: CHR: chromosome SNP: SNP ID BP: position A1: effect allele TEST: test NMISS: samples BETA: effect size STAT: statistic P: p-value OR: odds ratio (logistic) Example: 1 rs123 100 A ADD 1000 0.05 3.2 0.001 """) print("\n=== Python Association ===") print(""" import numpy as np from scipy import stats import statsmodels.api as sm def association_test(genotype, phenotype, covariates=None): \"\"\"Test SNP-phenotype association.\"\"\" # Design matrix X = genotype.reshape(-1, 1) if covariates is not None: X = np.hstack([X, covariates]) X = sm.add_constant(X) # Linear regression model = sm.OLS(phenotype, X).fit() beta = model.params[1] pval = model.pvalues[1] return beta, pval # Example # genotype = np.array([0, 0, 1, 1, 2, 1, 0, 1]) # phenotype = np.array([1.2, 1.5, 1.8, 1.9, 2.1, 1.7, 1.3, 1.6]) # beta, pval = association_test(genotype, phenotype) # print(f"Beta: {beta:.3f}, p: {pval:.3e}") """) print("\n=== Use Cases ===") print(""" Use cases: - GWAS - Candidate gene - QTL mapping - PheWAS - Rare variant """)

๐Ÿงฎ Mixed Models (LMM)

๐ŸŽฏ Linear Mixed Models

Linear mixed models (LMM) account for population structure and relatedness by including a random effect based on a kinship matrix. They are the gold standard for GWAS.

LMM

Fixed: SNPs, covariates. Random: Kinship. Corrects: Structure. Power: High. Tools: GCTA, BOLT-LMM, GEMMA. Standard: GWAS.

๐Ÿงฎ Fixed๐Ÿงฎ Random๐Ÿงฎ Kinship๐Ÿงฎ Corrects
# Mixed Models print("=== LMM Equation ===") print(""" y = Xฮฒ + Zu + e y: phenotype X: fixed effects (SNP, covariates) ฮฒ: fixed effects Z: random effects design u: random effects (kinship) e: residuals u ~ N(0, ฯƒยฒ_g K) e ~ N(0, ฯƒยฒ_e I) """) print("\n=== Kinship Matrix ===") print(""" Kinship: - Relatedness - Genetic similarity - Random effect K = (1/n) * ฮฃ (G_i - 2p_i)(G_i - 2p_i)^T Example: K[i,j] = relatedness between i and j """) print("\n=== Variance Components ===") print(""" Variance: - ฯƒยฒ_g: genetic variance - ฯƒยฒ_e: environmental variance - hยฒ = ฯƒยฒ_g / (ฯƒยฒ_g + ฯƒยฒ_e) Example: hยฒ = 0.5 โ†’ 50% heritability """) print("\n=== Tools ===") print(""" 1. GCTA - Mixed model - Heritability - Fast 2. BOLT-LMM - Large-scale - Fast - UK Biobank 3. GEMMA - Mixed model - Multiple phenotypes - Bayesian 4. SAIGE - Binary traits - Related individuals - Scalable 5. REGENIE - Whole-genome regression - Fast - Large-scale """) print("\n=== GCTA ===") print(""" GCTA: - Genome-wide Complex Trait Analysis - Heritability - Mixed model Commands: gcta --bfile data --make-grm --out grm gcta --grm grm --pheno pheno.txt --reml --out h2 gcta --grm grm --mlma --bfile data --out assoc """) print("\n=== BOLT-LMM ===") print(""" BOLT-LMM: - Large-scale - Fast - UK Biobank Command: bolt --bfile=data --phenoFile=pheno.txt --lmm --LDscoresFile=LD.txt """) print("\n=== GEMMA ===") print(""" GEMMA: - Mixed model - Multiple phenotypes Commands: gemma -bfile data -gk 1 -o kinship gemma -bfile data -k kinship.cXX.txt -lmm 1 -o assoc """) print("\n=== SAIGE ===") print(""" SAIGE: - Binary traits - Related individuals - Scalable Command: saige --bfile=data --phenoFile=pheno.txt --phenoCol=disease """) print("\n=== REGENIE ===") print(""" REGENIE: - Whole-genome regression - Fast - Large-scale Commands: regenie --step 1 --bfile data --phenoFile pheno.txt regenie --step 2 --bfile data --phenoFile pheno.txt --pred pred.list """) print("\n=== Heritability ===") print(""" Heritability: - SNP heritability - hยฒ_SNP - Variance explained Example: hยฒ = 0.5 โ†’ 50% variance explained """) print("\n=== Use Cases ===") print(""" Use cases: - GWAS - Heritability - Prediction - Related individuals - Biobanks """)

๐Ÿ“ˆ Manhattan & QQ Plots

๐ŸŽฏ Visualizing GWAS Results

Manhattan plots and QQ plots are the standard visualizations for GWAS results. They reveal significant associations and assess model quality.

Plots

Manhattan: Genome-wide. QQ: p-value distribution. ฮป: Inflation. Peaks: Associations. Threshold: 5e-8. Colors: Chromosomes.

๐Ÿ“ˆ Manhattan๐Ÿ“ˆ QQ๐Ÿ“ˆ ฮป๐Ÿ“ˆ Peaks
# Manhattan & QQ Plots print("=== Manhattan Plot ===") print(""" Manhattan: - X: chromosome - Y: -log10(p) - Points: SNPs - Colors: alternating - Threshold: 5e-8 Example: Peak at chr6 โ†’ HLA region โ†’ Association """) print("\n=== QQ Plot ===") print(""" QQ: - X: expected -log10(p) - Y: observed -log10(p) - Diagonal: null - Deviation: signal Example: Points on diagonal โ†’ Well-calibrated """) print("\n=== Inflation Factor ===") print(""" ฮป: - Genomic inflation - ฮป = median(ฯ‡ยฒ) / 0.456 - ฮป = 1: no inflation - ฮป > 1: inflation Example: ฮป = 1.05 โ†’ Mild inflation """) print("\n=== Python Manhattan ===") print(""" import matplotlib.pyplot as plt import numpy as np import pandas as pd def manhattan(df, output="manhattan.png"): \"\"\"Manhattan plot.\"\"\" df = df.sort_values(['CHR', 'BP']) df['ind'] = range(len(df)) df['logp'] = -np.log10(df['P']) fig, ax = plt.subplots(figsize=(15, 5)) colors = ['#a855f7', '#6366f1'] x_labels = [] x_pos = [] for i, (chrom, group) in enumerate(df.groupby('CHR')): ax.scatter(group['ind'], group['logp'], c=colors[i % 2], s=5, alpha=0.7) x_labels.append(str(chrom)) x_pos.append(group['ind'].mean()) ax.axhline(-np.log10(5e-8), color='red', linestyle='--') ax.set_xticks(x_pos) ax.set_xticklabels(x_labels) ax.set_xlabel("Chromosome") ax.set_ylabel("-log10(p)") ax.set_title("Manhattan Plot") plt.tight_layout() plt.savefig(output) plt.show() # Use # manhattan(df) """) print("\n=== Python QQ ===") print(""" from scipy import stats def qq_plot(pvals, output="qq.png"): \"\"\"QQ plot.\"\"\" n = len(pvals) expected = -np.log10(np.arange(1, n+1) / (n+1)) observed = -np.log10(np.sort(pvals)) plt.figure(figsize=(6, 6)) plt.scatter(expected, observed, s=5, alpha=0.7, color='purple') plt.plot([0, max(expected)], [0, max(expected)], 'r--', label='Null') plt.xlabel("Expected -log10(p)") plt.ylabel("Observed -log10(p)") plt.title("QQ Plot") plt.legend() plt.tight_layout() plt.savefig(output) plt.show() # Use # qq_plot(df['P']) """) print("\n=== R qqman ===") print(""" library(qqman) # Manhattan manhattan(gwasResults, chr="CHR", bp="BP", snp="SNP", p="P", suggestiveline=FALSE, genomewideline=-log10(5e-8)) # QQ qq(gwasResults$P) """) print("\n=== Tools ===") print(""" Tools: - qqman (R) - ggplot2 (R) - matplotlib (Python) - LocusZoom - FUMA - Manhattan plot web tools """) print("\n=== Interpretation ===") print(""" Interpretation: - Peaks: associations - Height: significance - Width: LD - ฮป: inflation Example: Peak at chr6 โ†’ HLA โ†’ Immune """) print("\n=== Use Cases ===") print(""" Use cases: - GWAS results - Visualization - Quality control - Publication - Communication """)

๐Ÿ”Ž Fine-Mapping & Imputation

๐ŸŽฏ From Association to Causality

Fine-mapping identifies the causal variant(s) within an associated locus. Imputation fills in missing genotypes to increase resolution.

Fine-Mapping

Goal: Causal variant. LD: Correlated SNPs. Methods: Bayesian. Credible set: 95%. Imputation: Fill missing. Tools: FINEMAP, SuSiE, CAVIAR.

๐Ÿ”Ž Causal๐Ÿ”Ž Credible set๐Ÿ”Ž Imputation๐Ÿ”Ž FINEMAP
# Fine-Mapping & Imputation print("=== Fine-Mapping ===") print(""" Fine-mapping: - Identify causal variant - Within associated locus - LD structure - Bayesian methods Goal: - Credible set - 95% probability - Causal variant """) print("\n=== Methods ===") print(""" 1. FINEMAP - Bayesian - Shotgun stochastic search - Fast 2. SuSiE - Sum of single effects - Bayesian - Multiple causal variants 3. CAVIAR - Bayesian - Credible set - Fast 4. PAINTOR - Functional annotation - Bayesian - Integration """) print("\n=== Credible Set ===") print(""" Credible set: - 95% probability - Contains causal variant - Smallest set Example: Credible set: 5 SNPs โ†’ 95% chance causal is here """) print("\n=== Imputation ===") print(""" Imputation: - Fill missing genotypes - Reference panel - Increase SNPs Reference panels: - 1000 Genomes - HRC - TOPMed - UK Biobank Tools: - IMPUTE2 - IMPUTE5 - Beagle - Minimac4 - Michigan Imputation Server """) print("\n=== IMPUTE5 ===") print(""" IMPUTE5: - Fast - Reference panel - Phasing Command: impute5 --haps ref.hap --legend ref.legend --g ref.gen \ --h target.hap --l target.legend --g target.gen \ --o output --r chr --m map.txt """) print("\n=== Beagle ===") print(""" Beagle: - Phasing - Imputation - Fast Command: java -jar beagle.jar gt=data.vcf ref=ref.vcf out=imputed """) print("\n=== Minimac4 ===") print(""" Minimac4: - Fast - Reference panel - Michigan Imputation Server Command: minimac4 --refHaps ref.m3vcf --haps target.vcf --prefix output """) print("\n=== Quality ===") print(""" Quality: - INFO score - > 0.8: good - > 0.9: excellent - DR2 (mach) - Rยฒ (beagle) Example: INFO = 0.95 โ†’ High quality """) print("\n=== Fine-Mapping Python ===") print(""" # Use FINEMAP or SuSiE via R # Python: use subprocess import subprocess def run_finemap(z_file, ld_file, n, output="finemap"): \"\"\"Run FINEMAP.\"\"\" with open(f"{output}.master", "w") as f: f.write(f"z;{z_file}\\n") f.write(f"ld;{ld_file}\\n") f.write(f"n;{n}\\n") f.write(f"ss;1\\n") f.write(f"config;1\\n") subprocess.run(["finemap", "--master", f"{output}.master"]) return output # Use # run_finemap("z.txt", "ld.txt", 10000) """) print("\n=== Use Cases ===") print(""" Use cases: - Causal variant - Functional follow-up - Drug targets - Mechanism - Precision medicine """)

๐Ÿ› ๏ธ Tools (PLINK, GCTA, BOLT-LMM, GEMMA)

๐ŸŽฏ GWAS Tools

Many tools exist for GWAS analysis. Each has strengths and is suited to different use cases.

Tools

PLINK: QC, association. GCTA: Heritability. BOLT-LMM: Large-scale. GEMMA: Mixed models. SAIGE: Binary. REGENIE: Whole-genome.

๐Ÿ› ๏ธ PLINK๐Ÿ› ๏ธ GCTA๐Ÿ› ๏ธ BOLT-LMM๐Ÿ› ๏ธ GEMMA
# Tools print("=== PLINK ===") print(""" PLINK: - QC - Association - Standard - Free Commands: plink --bfile data --maf 0.01 --hwe 1e-6 --geno 0.05 --make-bed --out clean plink --bfile clean --linear --pheno pheno.txt --covar covar.txt --out assoc plink --bfile clean --logistic --pheno pheno.txt --covar covar.txt --out assoc """) print("\n=== GCTA ===") print(""" GCTA: - Heritability - Mixed model - Fast Commands: gcta --bfile data --make-grm --out grm gcta --grm grm --pheno pheno.txt --reml --out h2 gcta --grm grm --mlma --bfile data --out assoc """) print("\n=== BOLT-LMM ===") print(""" BOLT-LMM: - Large-scale - Fast - UK Biobank Command: bolt --bfile=data --phenoFile=pheno.txt --lmm --LDscoresFile=LD.txt """) print("\n=== GEMMA ===") print(""" GEMMA: - Mixed model - Multiple phenotypes Commands: gemma -bfile data -gk 1 -o kinship gemma -bfile data -k kinship.cXX.txt -lmm 1 -o assoc """) print("\n=== SAIGE ===") print(""" SAIGE: - Binary traits - Related individuals - Scalable Command: saige --bfile=data --phenoFile=pheno.txt --phenoCol=disease """) print("\n=== REGENIE ===") print(""" REGENIE: - Whole-genome regression - Fast - Large-scale Commands: regenie --step 1 --bfile data --phenoFile pheno.txt regenie --step 2 --bfile data --phenoFile pheno.txt --pred pred.list """) print("\n=== Other Tools ===") print(""" 1. PLINK2 - Faster - More features 2. METAL - Meta-analysis 3. MR-Base - Mendelian randomization 4. FUMA - Functional annotation 5. LocusZoom - Visualization 6. PRSice - Polygenic scores 7. LDSC - LD score regression 8. Hail - Cloud-scale """) print("\n=== Tool Comparison ===") print(f"{'Tool':<20} {'Type':<20} {'Speed':<15} {'Best For':<20}") print("-" * 75) print(f"{'PLINK':<20} {'QC, assoc':<20} {'Fast':<15} {'General':<20}") print(f"{'GCTA':<20} {'Mixed model':<20} {'Fast':<15} {'Heritability':<20}") print(f"{'BOLT-LMM':<20} {'Mixed model':<20} {'Very fast':<15} {'Biobank':<20}") print(f"{'GEMMA':<20} {'Mixed model':<20} {'Fast':<15} {'Multiple':<20}") print(f"{'SAIGE':<20} {'Binary':<20} {'Fast':<15} {'Related':<20}") print(f"{'REGENIE':<20} {'Whole-genome':<20} {'Fast':<15} {'Large-scale':<20}") print("\n=== When to Use Which ===") print(""" USE PLINK: โœ“ QC โœ“ Basic association โœ“ General USE GCTA: โœ“ Heritability โœ“ Mixed model โœ“ Relatedness USE BOLT-LMM: โœ“ Large-scale โœ“ Biobank โœ“ Fast USE GEMMA: โœ“ Multiple phenotypes โœ“ Mixed model โœ“ Bayesian USE SAIGE: โœ“ Binary traits โœ“ Related individuals โœ“ Scalable USE REGENIE: โœ“ Whole-genome โœ“ Large-scale โœ“ Fast """)

๐Ÿ Python Integration

๐ŸŽฏ GWAS with Python

Python is a powerful language for GWAS analysis, visualization, and automation. Here's how.

Python

Pandas: Data. NumPy: Arrays. Statsmodels: Regression. Matplotlib: Plots. Scikit-learn: PCA. Subprocess: Tools.

๐Ÿ Pandas๐Ÿ Statsmodels๐Ÿ Matplotlib๐Ÿ Automate
# Python Integration print("=== 1. Load GWAS Data ===") print(""" import pandas as pd # Load df = pd.read_csv("gwas_results.txt", sep="\\t") print(df.head()) print(df.columns) """) print("\n=== 2. Association Testing ===") print(""" import statsmodels.api as sm import numpy as np def association(genotype, phenotype, covariates=None): \"\"\"Association test.\"\"\" X = genotype.reshape(-1, 1) if covariates is not None: X = np.hstack([X, covariates]) X = sm.add_constant(X) model = sm.OLS(phenotype, X).fit() return model.params[1], model.pvalues[1] # Example # beta, pval = association(genotype, phenotype) """) print("\n=== 3. PCA ===") print(""" from sklearn.decomposition import PCA def compute_pca(genotypes, n_components=10): \"\"\"PCA.\"\"\" pca = PCA(n_components=n_components) pcs = pca.fit_transform(genotypes) return pcs, pca.explained_variance_ratio_ # Use # pcs, var = compute_pca(genotypes) """) print("\n=== 4. Manhattan Plot ===") print(""" import matplotlib.pyplot as plt import numpy as np def manhattan(df, output="manhattan.png"): \"\"\"Manhattan plot.\"\"\" df = df.sort_values(['CHR', 'BP']) df['ind'] = range(len(df)) df['logp'] = -np.log10(df['P']) fig, ax = plt.subplots(figsize=(15, 5)) colors = ['#a855f7', '#6366f1'] for i, (chrom, group) in enumerate(df.groupby('CHR')): ax.scatter(group['ind'], group['logp'], c=colors[i % 2], s=5, alpha=0.7) ax.axhline(-np.log10(5e-8), color='red', linestyle='--') ax.set_xlabel("Chromosome") ax.set_ylabel("-log10(p)") ax.set_title("Manhattan Plot") plt.tight_layout() plt.savefig(output) plt.show() # Use # manhattan(df) """) print("\n=== 5. QQ Plot ===") print(""" def qq_plot(pvals, output="qq.png"): \"\"\"QQ plot.\"\"\" n = len(pvals) expected = -np.log10(np.arange(1, n+1) / (n+1)) observed = -np.log10(np.sort(pvals)) plt.figure(figsize=(6, 6)) plt.scatter(expected, observed, s=5, alpha=0.7, color='purple') plt.plot([0, max(expected)], [0, max(expected)], 'r--') plt.xlabel("Expected -log10(p)") plt.ylabel("Observed -log10(p)") plt.title("QQ Plot") plt.tight_layout() plt.savefig(output) plt.show() # Use # qq_plot(df['P']) """) print("\n=== 6. Run PLINK ===") print(""" import subprocess def run_plink(bfile, output, command="--linear"): \"\"\"Run PLINK.\"\"\" cmd = ["plink", "--bfile", bfile, command, "--out", output] subprocess.run(cmd, check=True) return f"{output}.assoc.linear" # Use # run_plink("data", "assoc", "--linear") """) print("\n=== 7. Parse PLINK Output ===") print(""" def parse_plink(filepath): \"\"\"Parse PLINK association output.\"\"\" df = pd.read_csv(filepath, sep="\\s+") return df # Use # df = parse_plink("assoc.assoc.linear") """) print("\n=== 8. Heritability ===") print(""" import numpy as np def estimate_heritability(genotypes, phenotype): \"\"\"Estimate SNP heritability.\"\"\" # GRM n = genotypes.shape[0] p = genotypes.mean(axis=0) / 2 Z = genotypes - 2 * p K = Z @ Z.T / genotypes.shape[1] # REML (simplified) # ... return None # Use # h2 = estimate_heritability(genotypes, phenotype) """) print("\n=== 9. Polygenic Score ===") print(""" def compute_prs(genotypes, betas): \"\"\"Compute polygenic score.\"\"\" return genotypes @ betas # Use # prs = compute_prs(genotypes, betas) """) print("\n=== 10. Complete Pipeline ===") print(""" import pandas as pd import numpy as np import subprocess import matplotlib.pyplot as plt def gwas_pipeline(bfile, pheno, covar, output="gwas"): \"\"\"Complete GWAS pipeline.\"\"\" # QC subprocess.run([ "plink", "--bfile", bfile, "--maf", "0.01", "--hwe", "1e-6", "--geno", "0.05", "--make-bed", "--out", f"{output}_clean" ], check=True) # Association subprocess.run([ "plink", "--bfile", f"{output}_clean", "--linear", "--pheno", pheno, "--covar", covar, "--out", output ], check=True) # Parse df = pd.read_csv(f"{output}.assoc.linear", sep="\\s+") # Plots # manhattan(df) # qq_plot(df['P']) return df # Use # df = gwas_pipeline("data", "pheno.txt", "covar.txt") """)

โš–๏ธ Tool Comparison

๐ŸŽฏ Which Tool?

Choosing the right GWAS tool depends on your data size, trait type, and computational resources.

Comparison

PLINK: Standard. GCTA: Heritability. BOLT-LMM: Biobank. GEMMA: Multiple. SAIGE: Binary. REGENIE: Whole-genome.

โš–๏ธ PLINKโš–๏ธ GCTAโš–๏ธ BOLT-LMMโš–๏ธ GEMMA
Tool Type Speed Trait Best For
PLINKQC, assocFastBothGeneral
GCTAMixed modelFastBothHeritability
BOLT-LMMMixed modelVery fastQuantitativeBiobank
GEMMAMixed modelFastBothMultiple
SAIGEMixed modelFastBinaryRelated
REGENIEWhole-genomeFastBothLarge-scale
PLINK2QC, assocVery fastBothModern
HailCloudScalableBothCloud
# Decision Guide print("=== Choose Tool by Data Size ===") print(""" Small (< 10k): โ†’ PLINK โ†’ GEMMA Medium (10k-100k): โ†’ PLINK โ†’ GCTA โ†’ GEMMA Large (100k-500k): โ†’ BOLT-LMM โ†’ SAIGE โ†’ REGENIE Very large (> 500k): โ†’ BOLT-LMM โ†’ REGENIE โ†’ Hail """) print("\n=== Choose Tool by Trait ===") print(""" Quantitative: โ†’ BOLT-LMM โ†’ GCTA โ†’ GEMMA Binary: โ†’ SAIGE โ†’ PLINK โ†’ REGENIE Both: โ†’ PLINK โ†’ GEMMA โ†’ REGENIE """) print("\n=== Speed Comparison ===") print(f"{'Tool':<20} {'100k':<15} {'500k':<15} {'1M':<15}") print("-" * 65) print(f"{'PLINK':<20} {'1 hour':<15} {'5 hours':<15} {'10 hours':<15}") print(f"{'BOLT-LMM':<20} {'10 min':<15} {'1 hour':<15} {'2 hours':<15}") print(f"{'SAIGE':<20} {'30 min':<15} {'2 hours':<15} {'5 hours':<15}") print(f"{'REGENIE':<20} {'20 min':<15} {'1 hour':<15} {'3 hours':<15}") print("\n=== When to Use Which ===") print(""" USE PLINK: โœ“ QC โœ“ Basic association โœ“ Standard USE GCTA: โœ“ Heritability โœ“ Relatedness โœ“ Mixed model USE BOLT-LMM: โœ“ Biobank โœ“ Large-scale โœ“ Fast USE GEMMA: โœ“ Multiple phenotypes โœ“ Bayesian โœ“ Mixed model USE SAIGE: โœ“ Binary traits โœ“ Related individuals โœ“ Scalable USE REGENIE: โœ“ Whole-genome โœ“ Large-scale โœ“ Fast """)

โ“ FAQ

๐ŸŽฏ Frequently Asked Questions

Answers to common questions about GWAS and population genetics.

What is GWAS?

GWAS (Genome-Wide Association Study) is a method to identify genetic variants (usually SNPs) associated with a trait or disease. It tests millions of SNPs across the genome for statistical association with a phenotype, using large cohorts of individuals.

What is a SNP?

A SNP (Single Nucleotide Polymorphism) is a variation at a single position in DNA where different individuals have different nucleotides (e.g., A, T, C, or G). SNPs are the most common genetic variants and are the primary markers used in GWAS.

What is population structure and why does it matter in GWAS?

Population structure refers to systematic differences in allele frequencies between subpopulations. It matters in GWAS because it can cause spurious associations (false positives) if cases and controls have different ancestry. Methods like PCA, ADMIXTURE, and linear mixed models correct for structure.

What is linkage disequilibrium (LD)?

Linkage disequilibrium (LD) is the non-random association of alleles at different loci. High LD means alleles are inherited together more often than expected by chance. LD is used to identify haplotype blocks and to perform imputation and fine-mapping in GWAS.

Which tools are used for GWAS?

Common GWAS tools include PLINK (association testing, QC), GCTA (mixed models, heritability), BOLT-LMM (large-scale LMM), GEMMA (mixed models), SAIGE (binary traits, related individuals), and REGENIE (whole-genome regression). Visualization is done with QQ plots and Manhattan plots.

What is the genome-wide significance threshold?

The standard genome-wide significance threshold is p < 5 ร— 10โปโธ. This is based on Bonferroni correction for ~1 million independent tests (0.05 / 1,000,000). It is used to control for multiple testing across the genome.

What is a Manhattan plot?

A Manhattan plot displays GWAS results with chromosomes on the x-axis and -log10(p-value) on the y-axis. Each point is a SNP. Significant associations appear as peaks above the genome-wide significance line.

What is a QQ plot?

A QQ plot compares observed p-values to expected p-values under the null hypothesis. Points on the diagonal indicate good calibration. Deviation at the top indicates true associations. The inflation factor ฮป measures overall deviation.

What is fine-mapping?

Fine-mapping identifies the causal variant(s) within an associated locus. Because of LD, many SNPs may be associated. Fine-mapping uses Bayesian methods (FINEMAP, SuSiE) to define a credible set of variants that likely contains the causal variant.

What is imputation?

Imputation fills in missing genotypes using a reference panel (e.g., 1000 Genomes, HRC, TOPMed). It increases the number of SNPs tested in GWAS and improves fine-mapping resolution. Tools include IMPUTE5, Beagle, and Minimac4.

# Quick Reference print("=== Quick Answers ===") print(""" Q: What is GWAS? A: Genome-wide association study. Q: What is a SNP? A: Single nucleotide polymorphism. Q: Significance threshold? A: p < 5e-8. Q: What is LD? A: Linkage disequilibrium. Q: What is HWE? A: Hardy-Weinberg equilibrium. Q: Population structure? A: Ancestry differences; correct with PCA/LMM. Q: Tools? A: PLINK, GCTA, BOLT-LMM, GEMMA, SAIGE, REGENIE. Q: Manhattan plot? A: GWAS visualization. Q: QQ plot? A: P-value calibration. Q: Fine-mapping? A: Identify causal variant. Q: Imputation? A: Fill missing genotypes. Q: Free? A: Yes โ€” all major tools are free. """)

๐Ÿš€ Future

๐ŸŽฏ Where GWAS Is Going

The future is diverse populations, multi-omics, AI, rare variants, single-cell, and clinical translation.

Future Directions

Diversity: Global. Multi-omics: Integrated. AI: Prediction. Rare: Sequencing. Single-cell: Resolution. Clinical: Translation.

๐Ÿš€ Diversity๐Ÿš€ Multi-omics๐Ÿš€ AI๐Ÿš€ Clinical
# Future of GWAS print("=== 1. Diverse Populations ===") print(""" Trend: Global diversity Current: - Mostly European - ~80% European Future: - Global - Multi-ancestry - Equity Examples: - PAGE - H3Africa - GBMI Future: - Diverse - Equitable - Generalizable """) print("\n=== 2. Multi-Omics ===") print(""" Trend: Multi-omics Current: - Genomics only Future: - Genomics + transcriptomics - Genomics + proteomics - Genomics + metabolomics - Genomics + epigenomics Examples: - GTEx - UK Biobank - TOPMed Future: - Integrated - Mechanistic - Systems """) print("\n=== 3. AI ===") print(""" Trend: AI + GWAS Ideas: - Deep learning - Polygenic scores - Fine-mapping - Prediction Examples: - DeepWAS - Graph neural networks - Transformer Future: - Better prediction - Better fine-mapping - Better understanding """) print("\n=== 4. Rare Variants ===") print(""" Trend: Rare variants Current: - Common variants - Arrays Future: - Rare variants - Whole-genome sequencing - Gene-based tests Examples: - UK Biobank WGS - All of Us - gnomAD Future: - Rare variant associations - Missing heritability - Clinical """) print("\n=== 5. Single-Cell ===") print(""" Trend: Single-cell Current: - Bulk Future: - Single-cell - Cell-type specific - eQTL Examples: - scRNA-seq - scATAC-seq - Cell atlas Future: - Cell-type resolution - Mechanisms - Precision """) print("\n=== 6. Clinical Translation ===") print(""" Trend: Clinical Current: - Research Future: - Clinical - Polygenic scores - Pharmacogenomics - Screening Examples: - Breast cancer - Cardiovascular - Pharmacogenomics Future: - Clinical implementation - Precision medicine - Equity """) # TIMELINE print("\n=== Timeline ===") print(""" Past: 2005: First GWAS 2007: WTCCC 2010: Thousands 2015: Biobanks 2020: Millions 2025: Diverse populations Present: 2025: Multi-omics 2026: AI Future: 2030: Global 2035: Clinical 2040: Precision? """) print("\n=== Final Thoughts ===") print(""" GWAS is: - Transforming genetics - Finding variants - Understanding biology - Evolving The future is: - Diverse populations - Multi-omics - AI - Rare variants - Single-cell - Clinical translation "GWAS is the key to complex genetics." Keep exploring. Keep building. """)