Skip to content

Filtering¤

Use filters to keep the variants you want before they become a matrix. Python builds the expression; Rust evaluates it while reading records.

rare_high_quality = (
    genoio.region("22:20000000-21000000")
    & genoio.qual(min=20)
    & genoio.maf(max=0.05)
)

ds = genoio.vcf("data/chr22_hg38.vcf.gz")
samples = ds.samples()
y = load_phenotype_vector(samples["iid"])

for X, variants in ds.iter_blocks(
    5_000,
    variants=rare_high_quality,
    return_variants=True,
):
    association_scan(X, y, variants=variants)

Filters compose with Python operators:

genoio.chrom("22") & genoio.snp()
genoio.maf(max=0.01) | genoio.id_in(["rs123", "rs456"])
~genoio.missing_rate(max=0.1)

Metadata filters and genotype filters¤

Some predicates use metadata already stored on the variant record: chromosome, position, ID, REF/ALT structure, and QUAL. These can remove records before genotypes are decoded.

Other predicates need the genotype values. maf(...), mac(...), missing_rate(...), and polymorphic() are evaluated only for records that survive the metadata part of the expression. For common genotype-stat filters, the Rust reader can often evaluate a backend-specific filter plan from native counts or packed genotypes instead of building full variant statistics first.

Combine metadata and genotype filters

region("22:20000000-21000000") & qual(min=20) & maf(max=0.05) lets the reader narrow the candidate set before evaluating MAF. You do not need to hand-order simple expressions for performance: Rust simplifies the filter IR and partially evaluates metadata predicates before genotype decoding.


Region filters¤

Use a concrete region string with 1-based inclusive coordinates:

region = genoio.region("22:20000000-21000000")

Indexed regions

Compressed text VCF region reads require a .tbi or .csi index. genoio rejects unindexed compressed text VCF region reads instead of silently scanning the whole file.

BGEN dosage reads use a same-path bgenix SQLite index when one exists. For cohort.bgen, the expected path is cohort.bgen.bgi. Without that index, BGEN falls back to a sequential scan.

bgen_ds = genoio.bgen("cohort.bgen")
region = genoio.region("22:20000000-21000000")

for X, variants in bgen_ds.iter_blocks(
    1_000,
    dosage="dosage",
    variants=region,
    return_variants=True,
):
    analyze_region(X, variants)

Common predicates¤

genoio.chrom("22")
genoio.region("22:20000000-21000000")
genoio.snp()
genoio.biallelic()
genoio.qual(min=20)
genoio.maf(max=0.05)
genoio.mac(min=10)
genoio.missing_rate(max=0.1)
genoio.polymorphic()
genoio.id_in(["rs123", "rs456"])

Threshold predicates are inclusive. maf(max=0.05) keeps variants with minor allele frequency less than or equal to 0.05.

When a filter matches no variants

Empty filters are valid. Whole reads return shape (n_samples, 0), returned variant metadata has the standard columns with zero rows, and block reads yield no blocks.

Advanced: filter IR

Filter expressions are converted to a small intermediate representation before they cross into Rust:

expr = genoio.chrom("22") & genoio.maf(max=0.05)
expr.to_ir()

The IR records predicate names, validated parameters, and boolean structure. It does not contain Python callbacks. That keeps filtering portable across whole reads, block reads, and Rust reader implementations.

Rust uses the IR to separate metadata decisions from genotype-dependent decisions. Metadata predicates can be evaluated before matrix construction, and concrete compressed text VCF or BGEN region predicates can use an index when one is available. Before reading, Rust also normalizes simple boolean expressions: overlapping conjoined regions are reduced to their intersection, repeated threshold predicates are tightened, conjoined id_in predicates are intersected, and contradictory predicates become an empty result without scanning variant records.

Treat to_ir() as an inspection aid rather than a stable wire format. Build filters with the Python constructors so validation stays consistent.

See Filter API for signatures and validation rules.