File I/O¶
High-level readers dispatch to the right implementation based on file extension.
import snputils as su
snpobj = su.read_snp("cohort.vcf.gz") # auto-detects format
All text formats in the table below accept plain, gzip (.gz), or Zstandard
(.zst) input. Binary formats are read directly and do not accept an outer
compression suffix.
Readable Formats¶
Data type |
Formats |
|---|---|
Genotype |
VCF ( |
Local ancestry |
MSP ( |
Global ancestry |
ADMIXTURE proportions ( |
Phenotype |
Headered whitespace tables (commonly |
Metadata/covariates |
Delimited sample metadata and whitespace-delimited covariate tables |
Identity by descent |
hap-IBD ( |
SNP Formats¶
from snputils import read_bcf, read_bed, read_bgen, read_pgen, read_vcf
bcf = read_bcf("cohort.bcf")
bed = read_bed("cohort.bed")
bgen = read_bgen("cohort.bgen")
pgen = read_pgen("cohort.pgen")
vcf = read_vcf("cohort.vcf.gz")
Reader options:
from snputils import BCFReader, PGENReader, BGENReader, VCFReader
# PGEN: read specific samples/variants (phased by default)
pgen = PGENReader("cohort.pgen").read(
sample_ids=["sample1", "sample2"],
variant_ids=["rs1", "rs2"],
)
# VCF: read a specific region (phased by default)
vcf = VCFReader("cohort.vcf.gz").read(
region="chr1:1000000-2000000",
)
# Dosages can be requested explicitly.
dosages = VCFReader("cohort.vcf.gz").read(genotype_mode="dosage")
# BCF: read only specific samples and variants
bcf = BCFReader("cohort.bcf").read(
sample_ids=["sample1", "sample2"],
variant_ids=["chr1:1000000", "rs123"],
)
Phase-capable formats (VCFReader, VCFReaderPolars, BCFReader, PGENReader)
default to genotype_mode="auto": phased genotypes are preserved with shape
(n_variants, n_samples, 2), while unphased hardcalls fall back to per-sample
dosages (0, 1, 2, or missing). Use genotype_mode="dosage" to always get
dosages. Use genotype_mode="phased" to require phased allele-call output and
reject unphased hardcalls because their allele order is not meaningful.
BEDReader defaults to genotype_mode="dosage" because PLINK BED/BIM/FAM does not
store phase.
genotype_mode="dosage" rejects multiallelic variants because one scalar cannot
identify which alternate allele is being counted. Use genotype_mode="phased"
to preserve multiallelic allele indexes.
BCF notes: BCF reads use a native snputils parser over BGZF-compressed BCF2.2 records. Genotypes are stored on SNPObject.genotypes just like VCF input. region=... works without an index by scanning and filtering matching records.
BGEN notes: Genotype probabilities are stored on SNPObject.calldata_gp with shape (n_snps, n_samples, n_probs). Hard calls are not inferred during reads. Mixed-width BGEN records are padded with NaN columns.
Sampleless/Annotation-Only VCF notes: VCFReader supports reading sampleless VCFs (VCFs containing variant metadata but no sample columns). In this case, the returned SNPObject will have an empty genotypes array with a variant axis (shape (n_snps, 0) or (n_snps, 0, 2)). Correspondingly, VCFWriter can write sampleless VCFs from such SNPObject instances, provided their genotypes array has a variant axis of size matching the variant count.
Writer options:
from snputils import BEDWriter, PGENWriter, VCFWriter, BCFWriter, BGENWriter
BEDWriter(snpobj, "out.bed").write()
PGENWriter(snpobj, "out.pgen").write(vzs=True) # compressed .pvar.zst
VCFWriter(snpobj, "out.vcf", phased=True).write(
chrom_partition=True # one file per chromosome
)
BCFWriter(snpobj, "out.bcf", phased=True).write(
chrom_partition=True # one file per chromosome
)
BGENWriter(snpobj, "out.bgen").write(
compression="zstd", layout=2, bit_depth=16, phased=True
)
Local Ancestry Formats¶
msp = su.read_lai("local_ancestry.msp")
flare = su.read_lai("flare.out.anc.vcf.gz")
lanc = su.read_lai("cohort.lanc")
su.MSPWriter(laiobj, "out.msp").write()
su.LANCWriter(laiobj, "out.lanc").write()
# FLARE VCF output requires matching SNP data (GT + variant metadata)
su.FLAREWriter(laiobj, "out.anc.vcf", snpobj=snpobj).write()
.lanc stores only the SNP-level diploid ancestry matrix in run-length encoded form. To keep snputils round-trips self-contained, LANCWriter(...).write() and LocalAncestryObject.save_lanc(...) write sibling .pvar and .psam sidecars by default when the necessary metadata is present on the object.
su.LANCWriter(laiobj, "out.lanc").write() # also writes out.pvar + out.psam
laiobj.save_lanc("out.lanc") # same default behavior
# sparse stream only
su.LANCWriter(laiobj, "out.lanc", write_sidecars=False).write()
On reads, read_lanc(...) and read_lai(...) use matching .pvar and .psam
sidecars when available.
lanc = su.read_lanc("cohort.lanc") # looks for cohort.pvar and cohort.psam
# or point at sidecars elsewhere
lanc = su.read_lanc(
"cohort.lanc",
pvar_file="metadata/chr1.pvar",
psam_file="metadata/cohort.psam",
)
If the sidecars are missing, snputils warns and falls back to loading the LAI calls alone: it generates sample and haplotype IDs (sample_0, sample_0.0, …), sets window_sizes to one SNP per row, and leaves unavailable coordinate metadata unset.
Global Ancestry / ADMIXTURE¶
admobj = su.read_admixture("run_prefix") # reads run_prefix.Q and run_prefix.P
su.AdmixtureWriter(admobj, "out_prefix").write()
Admixture Mapping Output¶
# Write a VCF for downstream admixture-mapping tools
su.AdmixtureMappingVCFWriter(laiobj, snpobj, "admix_map.vcf").write()
IBD Formats¶
hapibd = su.read_ibd("segments.hapibd") # hap-IBD
ancibd = su.AncIBDReader("ch_all.tsv").read(
include_segment_types=["IBD1", "IBD2"] # filter by segment type
)
Phenotype Formats¶
phen = su.read_pheno("pheno.tsv")
mphen = su.MultiPhenReader("multi.tsv").read()
Metadata and Covariate Formats¶
Covariate files feed run_gwas() and run_admixture_mapping() through the covar argument. Use whitespace-separated text with IID in the header and numeric columns after it. An optional #FID column before IID is allowed.
#FID IID age sex
sample1 sample1 58 1
sample2 sample2 49 2
In Python you can pass the same data as a CovariateObject instead of a path.
Datasets¶
Built-in datasets and synthetic data builders for quick experimentation:
su.available_datasets_list() # list bundled datasets
ds = su.load_dataset("1kgp") # returns a SNPObject
snpobj = su.build_synthetic_snp_dataset()
laiobj = su.build_synthetic_chromosome_painting_dataset()
admobj = su.build_synthetic_admixture_dataset()
phen = su.build_synthetic_phenotype_dataset()
grg = su.build_synthetic_grg()
mdpca_ds = su.build_synthetic_mdpca_dataset()
maas_ds = su.build_synthetic_maasmds_dataset()