Skip to main content

Python interface to MHC binding, presentation, immunogenicity, and antigen processing predictors

Project description

Tests PyPI

mhctools

Python interface to MHC binding, presentation, immunogenicity, and antigen processing predictors.

Installation

pip install mhctools

For MHCflurry support, also run:

mhcflurry-downloads fetch

Quick start

from mhctools import NetMHCpan41

predictor = NetMHCpan41(alleles=["HLA-A*02:01", "HLA-B*07:02"])

# predict() returns a list of PeptideResult — one per peptide
results = predictor.predict(["SIINFEKL", "GILGFVFTL"])

for r in results:
    if r.affinity:
        print(f"{r.peptide} -> {r.affinity.allele} IC50={r.affinity.value:.1f}nM")

Data model

predict() returns a list of PeptideResult — one per peptide. Each result carries the peptide string and provides accessors for each prediction kind (affinity, presentation, stability, etc.). Accessors return None when a predictor doesn't produce that kind.

results = predictor.predict(["SIINFEKL", "GILGFVFTL"])
r = results[0]

r.peptide                    # "SIINFEKL"
r.affinity.value             # IC50 in nM
r.affinity.percentile_rank   # 0-100, lower = better
r.affinity.allele            # best allele for this kind
r.presentation               # None if predictor doesn't produce it

Under the hood, each PeptideResult wraps a tuple of Prediction objects — frozen dataclasses, one per allele-kind combination. Everything converts to DataFrames with consistent column names.

Python API

Predicting peptides

from mhctools import NetMHCpan41

predictor = NetMHCpan41(alleles=["HLA-A*02:01", "HLA-B*07:02"])
results = predictor.predict(["SIINFEKL", "GILGFVFTL"])

r = results[0]
r.peptide                      # "SIINFEKL"
r.offset                       # position in source protein (if scanned)
r.kinds                        # {"pMHC_affinity", "pMHC_presentation"}
r.alleles                      # {"HLA-A*02:01", "HLA-B*07:02"}

# best prediction by kind — None when the kind is absent
r.affinity                     # Prediction or None
r.presentation                 # Prediction or None
r.stability                    # None (predictor doesn't produce it)

if r.affinity:
    r.affinity.value            # IC50 in nM
    r.affinity.percentile_rank  # 0-100, lower = better
    r.affinity.score            # ~0-1, higher = better
    r.affinity.allele           # best allele for this kind

# by rank instead of score
r.best_affinity_by_rank        # Prediction with lowest percentile rank, or None

# all predictions
r.preds                        # tuple of all Prediction objects
r.filter(kind="pMHC_affinity")
r.filter(allele="HLA-A*02:01")

NetMHCpan 4.1 automatically emits both pMHC_affinity and pMHC_presentation predictions per peptide-allele pair.

Scanning proteins

predict_proteins() takes a dictionary of protein sequences and returns {sequence_name: list[PeptideResult]}:

proteins = predictor.predict_proteins(
    {"TP53": "MEEPQSDPSVEPPLSQETFS...", "KRAS": "MTEYKLVVVGAGGVGKS..."},
    peptide_lengths=[9, 10],
)

for r in proteins["TP53"]:
    if r.affinity and r.affinity.value < 500:
        print(f"  offset={r.offset} {r.peptide} IC50={r.affinity.value:.0f}")

DataFrames

Every level has a _dataframe variant that flattens to a pandas DataFrame with consistent columns:

df = predictor.predict_dataframe(["SIINFEKL"], sample_name="pat001")
df = predictor.predict_proteins_dataframe({"TP53": "MEEPQ..."}, sample_name="pat001")

Columns: sample_name, peptide, n_flank, c_flank, source_sequence_name, offset, predictor_name, predictor_version, allele, kind, score, value, percentile_rank.

Multi-sample predictions

MultiSample runs a predictor across multiple samples, each with its own HLA genotype:

from mhctools import MultiSample, NetMHCpan41

ms = MultiSample(
    samples={
        "pat001": ["HLA-A*02:01", "HLA-B*07:02"],
        "pat002": ["HLA-A*01:01", "HLA-B*08:01"],
    },
    predictor_class=NetMHCpan41,
)

# {sample_name: list[PeptideResult]}
results = ms.predict(["SIINFEKL", "GILGFVFTL"])

# {sample_name: {seq_name: list[PeptideResult]}}
protein_results = ms.predict_proteins({"TP53": "MEEPQ..."})

# flat DataFrames with sample_name column
df = ms.predict_dataframe(["SIINFEKL"])
df = ms.predict_proteins_dataframe({"TP53": "MEEPQ..."})

Measurement kinds and MHC context

Each Prediction has a kind string describing what it measures:

The canonical prediction kind strings are defined in mhctools.pred.Kind.

Kind Meaning
pMHC_affinity Peptide-MHC binding affinity
pMHC_presentation Likelihood of surface presentation (EL/processing)
pMHC_stability Peptide-MHC complex stability
pMHC_TCR_binding TCR recognition of a peptide-MHC (pMHC:TCR binding)
immunogenicity T-cell immunogenicity
antigen_processing Combined processing score
proteasome_cleavage Proteasomal (MHC-I, cytosolic) C-terminal cleavage score
endolysosomal_cleavage Endolysosomal (MHC-II, cathepsin) C-terminal cleavage score
tap_transport TAP transport score (reserved, not yet used)
erap_trimming ERAP trimming score (reserved, not yet used)

Predictors also expose kind_support() so downstream code can tell what MHC context is meaningful for each emitted kind:

support = predictor.kind_support()
support["pMHC_affinity"]
# {"mhc_dependence": "single_allele", "mhc_class": "I"}

mhc_dependence is one of:

Value Meaning
none The prediction is MHC-independent; Prediction.allele is empty.
single_allele The prediction is for one peptide/MHC allele pair; Prediction.allele is part of the key.
haplotype The prediction uses the requested MHC repertoire jointly; Prediction.allele may carry best-allele attribution but is not the prediction key.

mhc_class is one of none, I, II, or both.

The allowed metadata values are defined in mhctools.pred as MHC_DEPENDENCE_VALUES and MHC_CLASS_VALUES.

Examples:

Predictor Kind mhc_dependence mhc_class
NetMHCpan41 pMHC_affinity single_allele I
NetMHCpan41 pMHC_presentation single_allele I
NetMHCIIpan4_EL pMHC_presentation single_allele II
NetMHCstabpan pMHC_stability single_allele I
MHCflurry pMHC_affinity single_allele I
MHCflurry haplotype mode pMHC_presentation haplotype I
MHCflurry per-allele panel mode pMHC_presentation single_allele I
Pepsickle proteasome_cleavage none none
NetCleave_I proteasome_cleavage none I
NetCleave_II endolysosomal_cleavage none II
NetTCR pMHC_TCR_binding none I
Tulip pMHC_TCR_binding single_allele I

TCR predictors (NetTCR, Tulip)

NetTCR and Tulip predict pMHC:TCR binding — whether a paired αβ T-cell receptor (an mhctools.TCR, described by its CDR loops) recognises a peptide. Both take (peptide, TCR) inputs; Tulip additionally takes the presenting MHC allele.

from mhctools import Tulip, TCR

tcr = TCR(cdr3a="CAGASGNTGKLIF", cdr3b="CASSIRASYEQYF", name="clone1")
predictor = Tulip()                       # needs TULIP_HOME + TULIP_PYTHON
results = predictor.predict(["GILGFVFTL"], [tcr], mhc="HLA-A*02:01")
results[0].preds[0].score                 # higher = more likely binding

TULIP-TCR is GPLv3 and pinned to transformers==4.32.1; mhctools is Apache-2.0 and depends on neither torch nor transformers. The Tulip wrapper therefore vendors none of TULIP — it runs a user-provided checkout out-of-process, in an isolated interpreter, via TULIP's own predict.py. Set two things up first (see scripts/setup_tulip_env.sh, which does both):

  • TULIP_HOME — a clone of TULIP-TCR (provides predict.py, src/, tokenizers, and the released model_weights/);
  • TULIP_PYTHON — an isolated Python 3.11 interpreter with torch and transformers==4.32.1 (3.11 so tokenizers installs from a prebuilt wheel and needs no Rust toolchain).

For MHCflurry presentation, presentation_allele_mode="haplotype" treats the requested alleles as one sample genotype and emits one pMHC_presentation record per peptide. The allele field carries MHCflurry's best_allele attribution when available. presentation_allele_mode="per_allele" treats each allele as a separate one-allele synthetic sample and emits one presentation record per peptide/allele pair. The default "auto" mode uses haplotype mode for up to six alleles and per-allele mode for larger allele panels.

The Prediction object

Every prediction is a frozen, self-contained Prediction dataclass:

from mhctools import Prediction

pred = Prediction(
    kind="pMHC_affinity",
    score=0.85,           # ~0-1, higher = better
    peptide="SIINFEKL",
    allele="HLA-A*02:01",
    value=120.5,          # IC50 in nM
    percentile_rank=0.8,
    source_sequence_name="TP53",
    offset=42,
    predictor_name="netMHCpan",
    predictor_version="4.1",
)

score is always higher-is-better. value is in native units (nM for affinity, hours for stability). percentile_rank is always optional, 0-100, lower = stronger.

Supported predictors

MHC binding & presentation

Predictor Kinds produced Requires
NetMHCpan / NetMHCpan41 / NetMHCpan42 affinity + presentation NetMHCpan
NetMHCpan4 affinity or presentation NetMHCpan 4.0
NetMHCpan3 / NetMHCpan28 affinity older NetMHCpan
NetMHC / NetMHC3 / NetMHC4 affinity NetMHC
NetMHCIIpan / NetMHCIIpan43 affinity or presentation NetMHCIIpan
NetMHCcons affinity NetMHCcons
NetMHCstabpan stability NetMHCstabpan
MHCflurry affinity + presentation pip install mhcflurry + mhcflurry-downloads fetch
MHCflurry_Affinity affinity pip install mhcflurry + mhcflurry-downloads fetch
BigMHC presentation or immunogenicity BigMHC clone (set BIGMHC_DIR)
MixMHCpred presentation MixMHCpred
IedbNetMHCpan / IedbSMM / IedbNetMHCIIpan affinity IEDB web API
RandomBindingPredictor affinity (built-in)

Antigen processing

Predictor Kinds produced Requires
Pepsickle proteasome cleavage pip install pepsickle (paper)
NetChop proteasome cleavage NetChop
NetCleave_I / NetCleave_II proteasomal (I) / endolysosomal (II) C-terminal cleavage NetCleave clone (set NETCLEAVE_DIR)

Pepsickle and NetChop use configurable scoring to aggregate per-position cleavage probabilities into peptide-level scores (see ProcessingPredictor and ProteasomePredictor).

NetCleave is different: it emits a single C-terminal cleavage score per peptide and covers both the MHC-I proteasomal (NetCleave_Iproteasome_cleavage) and MHC-II endolysosomal (NetCleave_IIendolysosomal_cleavage) pathways — MHC-II processing is otherwise a gap in the predictor set. It needs the residues downstream of the peptide to build the cleavage site, so pass c_flanks (or scan proteins). Its weights ship in the git repo; the R dependency in NetCleave's README is only for its training pipeline, not prediction.

from mhctools import NetCleave_II

predictor = NetCleave_II()                 # resolves NETCLEAVE_DIR / ~/NetCleave
# score peptides with their C-terminal flanking residues (>= 3)
results = predictor.predict(["SIINFEKL"], c_flanks=["DGH"])
results[0].endolysosomal_cleavage.score

# or scan a protein so each peptide is scored in real context
by_protein = predictor.predict_proteins({"TP53": "MEEPQ..."}, peptide_lengths=[15])

⚠️ NetCleave's own paper reports class-II C-terminal cleavage is a much weaker signal than class I (AUC ~0.66 vs ~0.91). Treat endolysosomal_cleavage scores accordingly.

TCR specificity

Predictor Kinds produced Requires
NetTCR pMHC:TCR binding NetTCR-2.2 clone (set NETTCR_DIR) + a TFLite runtime (pip install mhctools[nettcr])

NetTCR predicts whether a paired αβ T-cell receptor recognises a (class-I) peptide. Unlike the MHC-ligand predictors, its input is a peptide plus a TCR (the six CDR loops), not an allele, and it emits the pMHC_TCR_binding kind. NetTCR ships its pretrained weights in its git repository as small TFLite models; this wrapper runs the pan cross-validation ensemble in-process and does not need NetTCR's conda environment.

from mhctools import NetTCR, TCR

predictor = NetTCR()   # resolves NETTCR_DIR / ~/NetTCR-2.2
tcr = TCR(
    cdr1a="NSASQS", cdr2a="VYSSG", cdr3a="VVEGDKVI",
    cdr1b="MGHRA", cdr2b="YSYEKL", cdr3b="ASSHSGYEQF", name="clone1")

# Score explicit (peptide, TCR) pairs...
results = predictor.predict_pairs([("LLWNGPMAV", tcr)])
results[0].tcr_binding.score        # ensemble-mean recognition probability

# ...or every peptide x TCR combination.
results = predictor.predict(["LLWNGPMAV", "GILGFVFTL"], [tcr])

Commandline examples

Prediction for user-supplied peptide sequences

mhctools --sequence SIINFEKL SIINFEKLQ --mhc-predictor netmhc --mhc-alleles A0201

Automatically extract peptides as subsequences of specified length

mhctools --sequence AAAQQQSIINFEKL --extract-subsequences --mhc-peptide-lengths 8-10 --mhc-predictor mhcflurry --mhc-alleles A0201

Legacy API

The old predict_peptides() and predict_subsequences() methods still work and return BindingPredictionCollection objects:

predictor = NetMHCpan(alleles=["A*02:01"])
collection = predictor.predict_subsequences(
    {"1L2Y": "NLYIQWLKDGGPSSGRPPPS"},
    peptide_lengths=[9],
)
df = collection.to_dataframe()

for bp in collection:
    if bp.affinity < 100:
        print("Strong binder: %s" % bp)

To convert legacy results to the new types:

preds = collection.to_preds()           # list of Prediction
pp_list = collection.to_peptide_preds() # list of PeptideResult

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mhctools-3.20.1.tar.gz (148.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mhctools-3.20.1-py3-none-any.whl (118.4 kB view details)

Uploaded Python 3

File details

Details for the file mhctools-3.20.1.tar.gz.

File metadata

  • Download URL: mhctools-3.20.1.tar.gz
  • Upload date:
  • Size: 148.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for mhctools-3.20.1.tar.gz
Algorithm Hash digest
SHA256 6c73eaf15b8c3a4b87ff0ce0d90e402152274152ce77d3b22601e495b063dddb
MD5 eca986f900ad63a8d5318f0b5ce7bd07
BLAKE2b-256 cfeabffcbad69b0ae18d95f71297e0053921abec3e6b4f38abae02aa3894b115

See more details on using hashes here.

File details

Details for the file mhctools-3.20.1-py3-none-any.whl.

File metadata

  • Download URL: mhctools-3.20.1-py3-none-any.whl
  • Upload date:
  • Size: 118.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for mhctools-3.20.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6f802534b174865348875cdd30d8d7edbf6d8c3253f30c3c4df571e96f35dedf
MD5 25bd1a95fd10b53a0d6e5f26e69ee296
BLAKE2b-256 ac556e99dec3356f48ed29b1afeab1556e02c983425b9183d720978b76b120a7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page