Source code for mir.bench.vdjdb

"""Load VDJdb dumps into the AIRR polars frame TCREmp expects.

VDJdb "slim" dumps use dotted column names (``cdr3``, ``v.segm``, ``j.segm``,
``antigen.epitope``, ``gene``, ``complex.id``, ``mhc.class``). This maps them to
the ``vdjtools.io.schema`` names plus an ``epitope`` label column.
"""

from __future__ import annotations

import polars as pl


[docs] def load_vdjdb(path: str) -> pl.DataFrame: """Return an AIRR frame with ``v_call, j_call, junction_aa, locus, epitope``. Args: path: VDJdb slim TSV (optionally gzipped). """ raw = pl.read_csv(path, separator="\t", infer_schema_length=0) # all Utf8 return ( raw.select( junction_aa=pl.col("cdr3"), v_call=pl.col("v.segm"), j_call=pl.col("j.segm"), locus=pl.col("gene"), epitope=pl.col("antigen.epitope"), mhc_class=pl.col("mhc.class"), ) .filter( pl.col("junction_aa").is_not_null() & (pl.col("junction_aa").str.len_chars() >= 5) & pl.col("epitope").is_not_null() & pl.col("v_call").is_not_null() & pl.col("j_call").is_not_null() ) # maintain_order: .unique() is otherwise run-to-run nondeterministic, which wobbles # downstream DBSCAN F1 in the third decimal. .unique(subset=["junction_aa", "v_call", "j_call", "locus", "epitope"], maintain_order=True) )
[docs] def antigen_subset(df: pl.DataFrame, chain: str, min_records: int) -> pl.DataFrame: """Rows for *chain* restricted to epitopes with ``>= min_records`` records.""" sub = df.filter(pl.col("locus") == chain) keep = ( sub.group_by("epitope").len().filter(pl.col("len") >= min_records)["epitope"].to_list() ) return sub.filter(pl.col("epitope").is_in(keep))