Skip to main content

BLOSUM62-aware anchor-residue clustering for immunopeptides, with a Rust backend

Project description

PepCluster

BLOSUM62-aware anchor-residue clustering for immunopeptides — with a fast Rust backend.

PepCluster groups peptides by the similarity of their MHC-I anchor residues (the first 3 + last 3 amino acids) using a BLOSUM62-normalized similarity metric with double weight on the primary anchor positions P2 and PΩ. It is built for large immunopeptidomics datasets: a Rust extension does the heavy lifting (10–100× faster than pure Python), and a pure-Python fallback keeps it working everywhere.

Unlike general-purpose sequence tools (e.g. MMseqs2), PepCluster distinguishes anchor from non-anchor positions, which is what actually drives MHC-I binding specificity — producing biologically interpretable clusters on short (8–14 aa) peptides where full-length estimators break down.


Install

pip install pepcluster

Prebuilt wheels are published for Linux, macOS, and Windows, so no Rust toolchain is required for end users. If you install on a platform without a wheel, pip builds from source (needs a Rust compiler — see Building from source).


Quick start

Command line

pepcluster -i examples/peptides.fasta -o out -t 0.6

This writes cluster assignments and per-cluster FASTA files under out/ (see Output files).

Python

import pepcluster

# End-to-end: FASTA in → TSV + per-cluster FASTA out
stats = pepcluster.cluster_fasta("peptides.fasta", "out", threshold=0.6)
print(stats["n_clusters"], "clusters")

# Low-level: cluster a dict of unique 6-mer anchors → frequency
mapping, n_cmp, n_early = pepcluster.cluster_anchors(
    {"YLLAGV": 3, "YMLAGV": 1, "GYAWTK": 2}, 0.6)
# mapping: {anchor -> representative anchor}

# Optional Lloyd-style refinement on top of the greedy result
refined, refine_stats = pepcluster.refine_clusters(
    {"YLLAGV": 3, "YMLAGV": 1}, mapping, 0.6, iterations=3)

pepcluster.HAS_RUST tells you whether the compiled backend is active (cluster_anchors / refine_clusters automatically use Rust when available and fall back to identical pure-Python implementations otherwise).


CLI options

Flag Default Description
-i, --input required Input FASTA file
-o, --outdir anchor_clusters Output directory
-t, --threshold 0.6 BLOSUM similarity threshold (0.0–1.0)
--min-cluster-size 2 Min members for a per-cluster FASTA
--n-front 3 N-terminal anchor length
--n-back 3 C-terminal anchor length
--anchors "2;3" Which positions are binding anchors, as FRONT;BACK (1-based per side). See Choosing the anchors
--anchor-weight 2.0 Weight given to anchor positions (all others are 1.0)
--obg-block-search off Also compare each anchor against centroids in neighbouring blocks whose upper bound reaches the threshold (fewer, tighter clusters)
--obg-max-probes 0 With --obg-block-search, max blocks searched per anchor incl. own (0 = all)
--obg-min-block-upper-bound 0.0 With --obg-block-search, only search blocks with upper bound ≥ this (effective cut max(threshold, this))
--refinement off Apply Lloyd-style refinement after greedy clustering
--iterations 3 Max refinement passes (with --refinement)
--refine-cap 32 Max centroid comparisons per anchor in refinement reassignment (<=0 = no cap). Lower = faster
--no-merge off Skip the refinement centroid-merge step (much faster on many-cluster data)
--fast-medoid off O(N) medoid instead of exact O(k²) (much faster when a few clusters are huge)
--merge-cap 0 Cap candidate centroids per cluster in the merge step (0 = no cap; e.g. 32 on many-cluster data)
--backend auto auto | rust | python
-q, --quiet Suppress progress output

Making refinement fast

Refinement (--refinement) has three sub-steps, and two of them are quadratic by default — which is fine at moderate thresholds but blows up at the extremes:

Sub-step Cost (default) Blows up when… Fast flag
medoid update O(k²) per cluster a few clusters are huge (low threshold) --fast-medoid → O(N)
reassignment O(cap) per anchor (already capped) --refine-cap
merge O(clusters × neighbours) there are many clusters (high threshold) --merge-cap N (or --no-merge)
  • --refine-cap N bounds candidate centroids per anchor in reassignment (own-block-first, largest-cluster-first). Default 32 is near-lossless.
  • --fast-medoid replaces the exact all-pairs medoid with an O(N) per-position decomposition. On a single cluster of 30k anchors it is ~470× faster (and the gap grows quadratically with cluster size).
  • --merge-cap N bounds candidate centroids per cluster in the merge step (~130× faster with ~1M clusters). --no-merge skips merging entirely.

For a huge dataset, turn all three on so every sub-step is bounded:

pepcluster -i peptides.fasta -o out -t 0.6 --refinement \
    --fast-medoid --refine-cap 32 --merge-cap 32

Defaults are unchanged (exact medoid, uncapped merge) for backward compatibility — the fast paths are opt-in.

Threshold guide:

Value Effect
0.8 Strict — mostly exact matches + very conservative substitutions
0.6 Moderate — allows 1–2 conservative substitutions (recommended)
0.4 Relaxed — broader groups for exploratory analysis

Choosing the anchors

The anchor is the peptide's first --n-front + last --n-back residues. --anchors picks which positions inside that anchor are the binding anchors: they get --anchor-weight (default 2×) in the similarity score and they define the coarse-alphabet blocking.

The format is "FRONT;BACK", where each side is a comma-separated list of 1-based indices into that side's residues. Either side may be empty.

The default "2;3" means the 2nd of the first 3 residues (P2) and the 3rd of the last 3 () — the classic MHC-I anchors.

--anchors Anchor positions (1-based, in the 6-mer) Meaning
"2;3" (default) 2, 6 P2 + PΩ — MHC-I
"2;2,3" 2, 5, 6 P2 plus the last two C-terminal residues
"1,2;3" 1, 2, 6 first two N-terminal residues plus PΩ
";3" 6 C-terminal anchor only
"2;" 2 N-terminal anchor only
# emphasise P2 and the last two residues, and weight anchors 3x
pepcluster -i peptides.fasta -o out --anchors "2;2,3" --anchor-weight 3

Anchor positions are indexed relative to --n-front / --n-back, so the two work together — e.g. a 2+2 anchor with anchors at the 2nd of each side:

pepcluster -i peptides.fasta -o out --n-front 2 --n-back 2 --anchors "2;2"

Up to 8 anchor positions are supported (they form the blocking key).


Tighter clusters: upper-bound-guided block search

For speed, the greedy pass only compares anchors that share a coarse-alphabet block at the anchor positions. This can over-split: two anchors that are genuinely similar overall but whose anchor residues fall in different coarse groups never get compared, so they land in separate clusters.

--obg-block-search addresses this. For each anchor it also searches neighbouring blocks, but only those whose upper bound on achievable similarity reaches the threshold — computed from the best possible residue match per block, so a block that cannot contain a match is provably skipped (a real match is never dropped). Eligible blocks are searched highest-bound-first.

pepcluster -i peptides.fasta -o out -t 0.5 --obg-block-search
# cap the work (search at most 4 blocks per anchor):
pepcluster -i peptides.fasta -o out -t 0.5 --obg-block-search --obg-max-probes 4

It yields fewer, tighter clusters (e.g. ~7% fewer at t=0.5 on random data) at extra cost; --obg-max-probes and --obg-min-block-upper-bound bound that cost. Off by default, so standard runs are unchanged.


Output files

out/
├── clusters.tsv            # cluster_id, representative_anchor, representative_peptide, header, sequence, anchor (every peptide)
├── cluster_summary.tsv     # cluster_id, representative_anchor, representative_peptide, size (sorted by size)
├── summary.txt             # run statistics
└── fasta/
    ├── cluster_0.fasta     # per-cluster FASTA, ready for MSA (>= --min-cluster-size members)
    ├── cluster_1.fasta
    └── SHORT_peptides.fasta # peptides too short to form an anchor (if any)

representative_peptide is the cluster's central member — the peptide with the least average distance (highest weighted average similarity) to every other peptide in the cluster. It is computed in linear time and is always a real member sequence, so it's a good label or seed for each cluster. representative_anchor is the anchor that originally seeded the cluster.


How it works

  1. Anchor extraction. Each peptide is reduced to its anchor: the first --n-front (3) and last --n-back (3) amino acids. Peptides shorter than that are set aside in SHORT_peptides.fasta.
  2. Deduplicate. Peptides are grouped by their exact anchor, so clustering operates on unique anchors weighted by frequency.
  3. Similarity metric. Two anchors are compared position-by-position with a BLOSUM62 score normalized to sim(a,b) = B(a,b) / sqrt(B(a,a)·B(b,b)). The anchor positions (--anchors, default P2 and PΩ) carry --anchor-weight (default 2×); every other position carries 1×. The score is a weighted mean in [−…, 1].
  4. Blocking. Unique anchors are bucketed by a reduced 10-letter alphabet at the anchor positions (10ᵏ bins for k anchors — 100 by default), so only plausibly-similar anchors are ever compared. High-weight positions are checked first with early termination.
  5. Greedy clustering. Within each block, anchors are processed most-frequent-first; each joins the first centroid above threshold or becomes a new centroid.
  6. Optional refinement (--refinement). A Lloyd-style pass iterates: medoid update → cross-block reassignment → centroid merging, until stable.

The Rust backend (pepcluster._core) and the pure-Python reference (pepcluster.clustering) implement identical logic — both compute in f64 with identical, deterministic orderings — and produce bit-identical cluster assignments; the test suite asserts this parity across anchor lengths, positions and weights.


Performance

Dataset Python Rust
7K peptides <1 s <1 s
2.5M peptides ~3 min ~15 s

Speed comes from anchor deduplication, coarse-alphabet blocking, and weighted early-termination in the similarity check.


Building from source

Requires a Rust toolchain and maturin.

# one-time
pip install maturin

# build + install into the current environment (editable-ish)
maturin develop --release

# or build a wheel
maturin build --release      # wheel lands in target/wheels/

The project uses maturin's mixed layout: Rust lives in src/lib.rs (compiled to pepcluster._core), Python in python/pepcluster/.

Run the tests with:

pip install pytest
pytest

Releasing (maintainers)

Wheels are built for Linux / macOS / Windows by .github/workflows/CI.yml and published to PyPI on version tags via PyPI Trusted Publishing (OpenID Connect — no API token or stored secret).

  1. One-time: on https://pypi.org/manage/account/publishing/ add a pending publisher — Owner AmirAsgary, Repository PepCluster, Workflow CI.yml (leave the environment blank).

  2. Bump the version in pyproject.toml and Cargo.toml.

  3. Tag and push:

    git tag v0.1.0
    git push origin v0.1.0
    

The release job then builds all wheels + an sdist and uploads them to PyPI.


License

MIT © 2026 Amir Asgary


Citation

If you use PepCluster in your research, please cite this repository:

Asgary, A. PepCluster: BLOSUM62-aware anchor-residue clustering for immunopeptides. https://github.com/AmirAsgary/PepCluster

Project details


Download files

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

Source Distribution

pepcluster-0.1.4.tar.gz (51.1 kB view details)

Uploaded Source

Built Distributions

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

pepcluster-0.1.4-cp38-abi3-win_amd64.whl (190.7 kB view details)

Uploaded CPython 3.8+Windows x86-64

pepcluster-0.1.4-cp38-abi3-musllinux_1_2_x86_64.whl (534.2 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ x86-64

pepcluster-0.1.4-cp38-abi3-musllinux_1_2_aarch64.whl (498.4 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARM64

pepcluster-0.1.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (331.3 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

pepcluster-0.1.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (322.9 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

pepcluster-0.1.4-cp38-abi3-macosx_11_0_arm64.whl (291.5 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

pepcluster-0.1.4-cp38-abi3-macosx_10_12_x86_64.whl (297.3 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file pepcluster-0.1.4.tar.gz.

File metadata

  • Download URL: pepcluster-0.1.4.tar.gz
  • Upload date:
  • Size: 51.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pepcluster-0.1.4.tar.gz
Algorithm Hash digest
SHA256 db85b2d292aeacfd9bb1f25ffaecd8e36429acc011b79ee4a768439f104b9002
MD5 b72d747976f79d0d0bd34267bf0d12cd
BLAKE2b-256 21fdd3f8cec35e13644e2d9ff56a148383fedd6ae137e1d563f2cbdd806e71cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4.tar.gz:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: pepcluster-0.1.4-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 190.7 kB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 e393a0d3082e4f868eb9430ec3e17e26391e4e94959f77f234643d4f60ff492f
MD5 cc1d70f014aae986d340f448ef3a5330
BLAKE2b-256 7381dd6c1742173493c637d4162a06f69efdd264c9dbd6e94059aa7d33410ba3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-win_amd64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 fad977906f51866e14dcfbc4dfebe06ad9ce16c25f47055c964504f480c5be75
MD5 ca5ff7315261cafe36f6d0f5eb411460
BLAKE2b-256 3fdd6212e1bc777a3b474272de19bee694e6031544049943f0e1aa83a784d7d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-musllinux_1_2_x86_64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 9fed2ce085d3c2a47724835a0c99578d55b14f435f7217ab4ec3c4ac7103a6d6
MD5 e4fab74e0233db66cf4a8b853ef416dc
BLAKE2b-256 40f78f1718e4d934163dac8a0ccc4964a1afe4aa78d7c0932f4773f52e6ba615

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-musllinux_1_2_aarch64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4d63af53e91c313f893904bdd4bbd2eaf7e7623626217d4be66b686258cf00ff
MD5 6c11f2124b87560790f3f41003316c21
BLAKE2b-256 1d7d4e25ba197d819f5e4403a26483f11e1d2e0add21430d958eff5f9aff5d80

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 30fa0bdb6b646479c9d1ad3182dd41a325bf48760b549e6ec1b967af0399df7a
MD5 70a2235368663a10944fdba90651fd7a
BLAKE2b-256 d8de6371d011cb67ac56bb46d40baecec77929d678c17f8d83e751adf8c375cc

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ae5d13ccf668b5c8233edba61081e458d1d115370b8cb942083840f629e90e52
MD5 9f321098e07cafe2f2ad97256d4e37cc
BLAKE2b-256 e4e20990c7ddafa0733377b573d31406bb7713cea6dd118e2efd6a07b8402657

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pepcluster-0.1.4-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pepcluster-0.1.4-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2066b778da71ed66f5cee82ac988bf3c0a56f1c0d1844ed6fc1beccff97c4f02
MD5 3d91fe1e1f7195a106a05462a982afa4
BLAKE2b-256 00c0e0bbad9a5c29d67d1fa1eee48368d8c45a41cc8444540b0fca75b48b3d19

See more details on using hashes here.

Provenance

The following attestation bundles were made for pepcluster-0.1.4-cp38-abi3-macosx_10_12_x86_64.whl:

Publisher: CI.yml on AmirAsgary/PepCluster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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