
Bio Population Genetics Population Structure
- 3 installs
- 1.1k repo stars
- Updated July 25, 2026
- gptomics/bioskills
Analyze population structure with PCA and ADMIXTURE ancestry analysis using PLINK and ADMIXTURE.
About
Analyzes population structure and stratification using PCA and admixture analysis with PLINK and ADMIXTURE. Developers use it to identify population clusters, estimate ancestry proportions, and choose the optimal K.
- PCA of genetic ancestry via PLINK 2.0
- ADMIXTURE ancestry proportions and optimal-K selection
Bio Population Genetics Population Structure by the numbers
- 3 all-time installs (skills.sh)
- Ranked #1,661 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/gptomics/bioskills --skill bio-population-genetics-population-structureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 1.1k |
| Last updated | July 25, 2026 |
| Repository | gptomics/bioskills ↗ |
What it does
Analyze population structure with PCA and ADMIXTURE ancestry analysis using PLINK and ADMIXTURE.
Files
Population Structure
Analyze genetic ancestry and population stratification using PCA and ADMIXTURE.
Principal Component Analysis (PCA)
PLINK 2.0 PCA
# Basic PCA (10 PCs)
plink2 --bfile data --pca 10 --out pca_results
# More PCs
plink2 --bfile data --pca 20 --out pca_results
# Approximate PCA (faster for large datasets)
plink2 --bfile data --pca 10 approx --out pca_results
# Output variant loadings
plink2 --bfile data --pca 10 var-wts --out pca_resultsOutput Files
| File | Contents |
|---|---|
.eigenvec | PC scores per sample (FID, IID, PC1, PC2, ...) |
.eigenval | Eigenvalues (variance explained) |
.eigenvec.var | Variant loadings (if var-wts) |
Variance Explained
import numpy as np
eigenvalues = np.loadtxt('pca_results.eigenval')
variance_explained = eigenvalues / eigenvalues.sum() * 100
cumulative = np.cumsum(variance_explained)
for i, (ve, cum) in enumerate(zip(variance_explained, cumulative), 1):
print(f'PC{i}: {ve:.2f}% (cumulative: {cum:.2f}%)')PCA Visualization
import pandas as pd
import matplotlib.pyplot as plt
eigenvec = pd.read_csv('pca_results.eigenvec', sep='\s+', header=None)
eigenvec.columns = ['FID', 'IID'] + [f'PC{i}' for i in range(1, len(eigenvec.columns) - 1)]
pop_info = pd.read_csv('population_labels.txt', sep='\t') # FID, IID, Population
eigenvec = eigenvec.merge(pop_info, on=['FID', 'IID'])
plt.figure(figsize=(10, 8))
for pop in eigenvec['Population'].unique():
subset = eigenvec[eigenvec['Population'] == pop]
plt.scatter(subset['PC1'], subset['PC2'], label=pop, s=20, alpha=0.7)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.legend()
plt.savefig('pca_plot.png', dpi=150)LD Pruning (Before Admixture)
ADMIXTURE requires LD-pruned SNPs:
# Calculate LD and identify pruned set
plink2 --bfile data --indep-pairwise 50 10 0.1 --out prune
# Extract pruned variants
plink2 --bfile data --extract prune.prune.in --make-bed --out data_prunedPruning Parameters
| Parameter | Description |
|---|---|
| Window (50) | SNPs in each window |
| Step (10) | SNPs to shift per step |
| r² threshold (0.1) | Max LD allowed |
ADMIXTURE Analysis
Basic Usage
# Run ADMIXTURE for K=3 clusters
admixture data_pruned.bed 3
# With cross-validation
admixture --cv data_pruned.bed 3
# Multithreaded
admixture -j4 data_pruned.bed 3Output Files
| File | Contents |
|---|---|
.Q | Ancestry proportions (samples × K) |
.P | Allele frequencies per cluster |
Testing Multiple K Values
# Run for K=2 through K=10
for K in $(seq 2 10); do
admixture --cv -j4 data_pruned.bed $K 2>&1 | tee log${K}.out
done
# Extract CV errors
grep -h "CV" log*.out | awk '{print NR+1, $4}' > cv_errors.txtChoose Optimal K
import matplotlib.pyplot as plt
cv_errors = []
with open('cv_errors.txt') as f:
for line in f:
k, cv = line.strip().split()
cv_errors.append((int(k), float(cv)))
ks, cvs = zip(*cv_errors)
plt.figure(figsize=(8, 5))
plt.plot(ks, cvs, 'o-')
plt.xlabel('K')
plt.ylabel('Cross-validation error')
plt.title('Admixture CV Error')
plt.savefig('admixture_cv.png', dpi=150)
optimal_k = ks[cvs.index(min(cvs))]
print(f'Optimal K: {optimal_k}')Visualize Admixture
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
K = 3
Q = pd.read_csv(f'data_pruned.{K}.Q', sep='\s+', header=None)
fam = pd.read_csv('data_pruned.fam', sep='\s+', header=None)
Q.columns = [f'Cluster{i}' for i in range(1, K + 1)]
Q['IID'] = fam[1].values
pop_info = pd.read_csv('population_labels.txt', sep='\t')
Q = Q.merge(pop_info, on='IID')
Q = Q.sort_values('Population')
colors = plt.cm.Set1(range(K))
fig, ax = plt.subplots(figsize=(14, 4))
bottom = np.zeros(len(Q))
for i in range(K):
ax.bar(range(len(Q)), Q[f'Cluster{i+1}'], bottom=bottom, color=colors[i], width=1)
bottom += Q[f'Cluster{i+1}'].values
ax.set_xlim(0, len(Q))
ax.set_ylim(0, 1)
ax.set_ylabel('Ancestry proportion')
plt.savefig('admixture_barplot.png', dpi=150, bbox_inches='tight')FlashPCA2 (Fast PCA for Large Datasets)
FlashPCA2 is optimized for very large datasets (100,000+ samples). Uses randomized algorithms for speed.
Installation
# From conda
conda install -c bioconda flashpca
# Or download binaries from GitHub
# https://github.com/gabraham/flashpcaBasic Usage
# Standard PCA
flashpca2 --bfile data --ndim 10 --outpc pcs.txt --outvec loadings.txt --outval eigenvalues.txt
# --ndim 10: Number of PCs to compute
# --outpc: Principal components output
# --outvec: Eigenvectors (variant loadings)
# --outval: EigenvaluesFlashPCA2 Options
| Option | Description |
|---|---|
| --bfile | PLINK binary prefix |
| --ndim | Number of PCs (default 10) |
| --outpc | PC scores output file |
| --outvec | Eigenvectors output |
| --outval | Eigenvalues output |
| --numthreads | CPU threads to use |
| --mem | Memory limit (GB) |
| --seed | Random seed for reproducibility |
Large Dataset Settings
# For biobank-scale data (>100k samples)
# numthreads=16: Adjust to available cores.
# mem=64: Memory in GB. Increase for larger datasets.
flashpca2 \
--bfile large_data \
--ndim 20 \
--numthreads 16 \
--mem 64 \
--outpc pcs.txt \
--outval eigenvalues.txt \
--seed 42FlashPCA2 vs PLINK2
| Feature | FlashPCA2 | PLINK2 |
|---|---|---|
| Speed (100k samples) | Faster | Good |
| Memory efficiency | Better | Good |
| Randomized algorithm | Yes | Optional (approx) |
| Part of standard toolkit | No | Yes |
Use FlashPCA2 for biobank-scale data; PLINK2 sufficient for most studies.
Parse FlashPCA2 Output
import pandas as pd
# Load PCs
pcs = pd.read_csv('pcs.txt', sep='\t', header=None)
pcs.columns = ['FID', 'IID'] + [f'PC{i}' for i in range(1, len(pcs.columns) - 1)]
# Load eigenvalues
eigenvals = pd.read_csv('eigenvalues.txt', header=None)[0].values
var_explained = eigenvals / eigenvals.sum() * 100
print('Variance explained:')
for i, ve in enumerate(var_explained[:10], 1):
print(f' PC{i}: {ve:.2f}%')MDS (Alternative to PCA)
# PLINK 1.9 MDS
plink --bfile data --cluster --mds-plot 10 --out mds_results
# Output: mds_results.mds (sample coordinates)Kinship/Relatedness
PLINK 2.0 KING-robust
# Calculate kinship matrix
plink2 --bfile data --make-king-table --out kinship
# Output: kinship.kin0 (pairs with kinship > 0.0442)Identify Related Individuals
import pandas as pd
kin = pd.read_csv('kinship.kin0', sep='\t')
related = kin[kin['KINSHIP'] > 0.0884] # First-degree relatives
print(f'Related pairs (1st degree): {len(related)}')
related = kin[kin['KINSHIP'] > 0.0442] # Second-degree
print(f'Related pairs (2nd degree): {len(related)}')Remove Related Individuals
# Create list to remove (keep one per pair)
plink2 --bfile data --king-cutoff 0.0884 --out unrelated
# Filter to unrelated
plink2 --bfile data --keep unrelated.king.cutoff.in.id --make-bed --out unrelatedComplete Workflow
# 1. QC filtering
plink2 --bfile raw --maf 0.01 --geno 0.05 --hwe 1e-6 --make-bed --out qc
# 2. LD pruning
plink2 --bfile qc --indep-pairwise 50 10 0.1 --out prune
plink2 --bfile qc --extract prune.prune.in --make-bed --out pruned
# 3. PCA
plink2 --bfile pruned --pca 20 --out pca
# 4. Admixture (multiple K)
for K in 2 3 4 5 6; do
admixture --cv -j4 pruned.bed $K 2>&1 | tee log${K}.out
doneRelated Skills
- plink-basics - Data preparation and QC
- linkage-disequilibrium - LD pruning details
- association-testing - Use PCs as covariates
#!/bin/bash
# Population structure analysis with PCA and Admixture
# Usage: ./structure_analysis.sh <plink_prefix> <output_prefix> [max_K]
BFILE="${1}"
PREFIX="${2:-structure}"
MAX_K="${3:-6}"
if [[ -z "$BFILE" ]]; then
echo "Usage: $0 <plink_prefix> [output_prefix] [max_K]"
exit 1
fi
echo "=== Population Structure Analysis ==="
echo "Input: $BFILE"
echo "Output prefix: $PREFIX"
echo "Testing K=2 to K=$MAX_K"
echo -e "\n=== Step 1: LD Pruning ==="
plink2 --bfile "$BFILE" --indep-pairwise 50 10 0.1 --out "${PREFIX}_prune"
plink2 --bfile "$BFILE" --extract "${PREFIX}_prune.prune.in" --make-bed --out "${PREFIX}_pruned"
echo "Pruned SNPs: $(wc -l < ${PREFIX}_pruned.bim)"
echo -e "\n=== Step 2: PCA ==="
plink2 --bfile "${PREFIX}_pruned" --pca 20 --out "${PREFIX}_pca"
echo -e "\n=== Step 3: Admixture ==="
cd "$(dirname ${PREFIX}_pruned.bed)" || exit
for K in $(seq 2 $MAX_K); do
echo "Running K=$K..."
admixture --cv -j4 "$(basename ${PREFIX}_pruned.bed)" $K 2>&1 | tee "${PREFIX}_log${K}.out"
done
echo -e "\n=== Step 4: CV Error Summary ==="
echo "K CV_Error"
for K in $(seq 2 $MAX_K); do
CV=$(grep "CV" "${PREFIX}_log${K}.out" | awk '{print $4}')
echo "$K $CV"
done
echo -e "\n=== Output Files ==="
echo "PCA eigenvectors: ${PREFIX}_pca.eigenvec"
echo "PCA eigenvalues: ${PREFIX}_pca.eigenval"
echo "Admixture Q files: ${PREFIX}_pruned.*.Q"
Population Structure - Usage Guide
Overview
Population structure analysis identifies genetic ancestry and stratification using PCA (continuous clustering) and ADMIXTURE (discrete ancestry proportions). Essential for GWAS stratification control and ancestry inference.
Prerequisites
# PLINK 2.0
conda install -c bioconda plink2
# ADMIXTURE
conda install -c bioconda admixture
# FlashPCA2 (for large datasets)
conda install -c bioconda flashpca
# Visualization
pip install pandas matplotlibQuick Start
Tell your AI agent what you want to do:
- "Run PCA on my genetic data"
- "Run fast PCA on biobank-scale data with FlashPCA2"
- "Estimate ancestry proportions with ADMIXTURE"
- "Check for population stratification before GWAS"
- "Plot PC1 vs PC2 colored by population"
Example Prompts
PCA Analysis
"Calculate the first 10 principal components from my PLINK data"
"Run PCA and identify any outlier samples"
"Generate a PCA plot colored by self-reported ancestry"
"Run FlashPCA2 on my large dataset (100k+ samples)"
Admixture Analysis
"Run ADMIXTURE for K=2 through K=6 and find the best K"
"Estimate ancestry proportions assuming 3 ancestral populations"
"Create a stacked bar plot of admixture proportions"
Combined Analysis
"Perform full population structure analysis with LD pruning, PCA, and ADMIXTURE"
"Check my data for population stratification and generate covariates for GWAS"
"Compare PCA clustering with ADMIXTURE assignments"
What the Agent Will Do
1. LD prune the data for unbiased estimation 2. Run PCA to calculate principal components 3. Run ADMIXTURE across multiple K values if requested 4. Identify optimal K using cross-validation error 5. Generate visualizations (PCA scatter, ADMIXTURE bar plots) 6. Flag outlier samples if detected
Tips
- Always LD prune before PCA/ADMIXTURE (r2 < 0.1)
- PC1-2 usually capture the largest population splits
- Choose K with lowest cross-validation error
- Outliers may indicate sample swaps, contamination, or unique ancestry
- Remove related individuals (IBD > 0.125) before analysis
- Use FlashPCA2 for biobank-scale data (100k+ samples) for better performance
Quick Reference
PCA Only
# Simple PCA
plink2 --bfile data --pca 10 --out pcaFull Pipeline
# 1. LD prune
plink2 --bfile data --indep-pairwise 50 10 0.1 --out prune
plink2 --bfile data --extract prune.prune.in --make-bed --out pruned
# 2. PCA
plink2 --bfile pruned --pca 10 --out pca
# 3. Admixture
for K in 2 3 4 5; do
admixture --cv pruned.bed $K
doneInterpreting Results
PCA
- PC1: Usually largest population split
- Clusters: Groups with similar ancestry
- Outliers: Sample swaps, contamination, or unique ancestry
Admixture
- K: Number of ancestral populations
- Q values: Proportion of each ancestry
- CV error: Lower is better for choosing K
Common Issues
PCA Shows No Structure
- May be homogeneous population
- Try more PCs
- Check for batch effects
Admixture Won't Converge
- LD prune more aggressively
- Remove closely related individuals
- Increase iterations