proteomics/protein-inference/SKILL.md
Groups proteins from peptide identifications and controls protein-level FDR, framing inference as a chosen explanation (parsimony or a probability model) of underdetermined peptide evidence rather than a measurement. Reports protein GROUPS (proteins indistinguishable by observed peptides) with a leading protein, not flat lists. Covers shared-vs-unique peptides, indistinguishable/subsumable proteins, parsimony vs probabilistic (ProteinProphet, EPIFANY) vs razor inference, picked-protein and picked-group FDR, and why the two-peptide rule is wrong. Use when resolving which proteins are present from a peptide list, building protein groups, or estimating protein-level FDR. PSM/peptide FDR and search engines are peptide-identification; razor-vs-unique quant consequences are quantification; isoform/proteoform resolution is top-down and out of scope.
npx skillsauth add GPTomics/bioSkills bio-proteomics-protein-inferenceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reference examples tested with: pyOpenMS 3.1+, pandas 2.2+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturespackageVersion('<pkg>') then ?function_name to verify parametersIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
The pyOpenMS protein-inference class names have varied across releases. Confirm the exact spelling at the installed version with help(pyopenms.EpifanyAlgorithm) and help(pyopenms.BasicProteinInferenceAlgorithm) before relying on the reference code.
"Tell me which proteins are present from my identified peptides" -> Assign the observed peptides to a minimal or probability-weighted set of proteins, reported as groups of indistinguishable proteins with a leading accession -- because bottom-up MS measures peptides, and the protein set behind them is inferred, not observed.
pyopenms.BasicProteinInferenceAlgorithm().run(peptide_ids, protein_ids) for parsimony groupingpyopenms.EpifanyAlgorithm (TOPP tool Epifany) for Bayesian belief-propagation inferenceProteinProphet (TPP) for EM-based probabilistic inference; Philosopher filter for FragPipe FDRScope: this skill OWNS peptide-to-protein grouping, the indistinguishable/subsumable distinction, the leading-protein convention, inference-method choice, and protein/protein-group FDR. PSM-level and peptide-level FDR plus the search engines that produce the peptide list -> peptide-identification. The quantitative fallout of razor vs unique peptides on protein abundance -> quantification. OUT OF SCOPE: resolving splice isoforms, single-AA variants, or PTM-defined proteoforms (bottom-up groups cannot separate them; that is top-down / proteoform work).
The protein set is not uniquely recoverable from peptides, so a protein group -- not a flat protein list -- is the only honest reporting unit. Many peptides are shared across paralogs, gene families, and isoforms, so distinct protein sets can explain the same peptide evidence equally well. The inference picks ONE explanation under an assumption (parsimony, or a probability model); proteins that the observed peptides cannot tell apart (indistinguishable) MUST be reported as one group with a designated leading protein. A flat list double-counts indistinguishable proteins and breaks target/decoy symmetry at the protein level, silently corrupting FDR.
Protein FDR is its own estimation problem that INFLATES on large data; the fix is PICKED FDR, not the PSM formula reused. Controlling PSM-FDR at 1% does not give 1% protein-FDR. A deep run has many false PSMs in absolute terms, and each can nucleate a one-hit-wonder false protein; because true proteins accumulate many peptides while false proteins are hit once, the naive protein-FDR balloons to 10-30% on deep datasets. Savitski 2015 picked-protein FDR pairs each target protein with its decoy and keeps only the higher-scoring of the pair before counting, removing the target/decoy asymmetry; The & Kall 2016 extends this to the group level (picked-group FDR), which is required because parsimony grouping is anticonservative otherwise.
The two-peptide rule is wrong -- it increases protein FDR and discards real proteins. Requiring >=2 peptides per protein (Gupta & Pevzner 2009, "A strike against the two-peptide rule") removes MORE target proteins than decoy proteins, so it raises protein-level FDR rather than lowering it, while throwing away legitimate low-abundance single-peptide IDs. Replace the blanket rule with: control protein-level (picked) FDR, then judge single-peptide IDs by their score, not their peptide count.
| Tool / method | Citation | Mechanism / role | When |
|---------------|----------|------------------|------|
| Parsimony (Occam) | -- | Greedy minimal protein set explaining all peptides | Fast default; ties broken arbitrarily; anticonservative group-FDR on large data unless picked |
| ProteinProphet | Nesvizhskii 2003 | EM APPORTIONS shared peptides across candidate proteins, weighted by other evidence | TPP / FragPipe pipelines; the classic probabilistic standard |
| EPIFANY | Pfeuffer 2020 | Bayesian network over the peptide-protein graph, loopy belief propagation + convolution trees | OpenMS-recommended modern inference; strong at controlled protein-group FDR |
| Fido | -- | Bayesian generative model (Percolator --protein) | Percolator pipelines; superseded by picked-protein for FDR |
| Razor peptide | -- | Shared peptide assigned winner-take-all to the group with most evidence (MaxQuant) | MaxQuant default; ID-fine but distorts QUANT (route to quantification) |
| Picked-protein FDR | Savitski 2015 | Pair target with its decoy, keep the higher-scoring of the pair, then count decoys | Protein-level FDR on any non-trivial dataset |
| Picked-group FDR | The & Kall 2016 | Picking applied at the protein-GROUP level | When the inference unit is the group (the correct unit on deep data) |
| All-proteins / inclusive | -- | Report every protein any peptide could come from | Almost never; massive false-positive protein inflation |
| Scenario | Recommended | Why |
|----------|-------------|-----|
| Standard DDA run, OpenMS-based pipeline | EpifanyAlgorithm (or BasicProteinInferenceAlgorithm for parsimony) + picked-group FDR | Modern, group-FDR aware; well-calibrated on benchmarks |
| MaxQuant output (proteinGroups.txt) | Parse groups as-is; quantify on UNIQUE peptides | Groups already inferred; razor quant is the trap, not the inference |
| FragPipe / TPP pipeline | ProteinProphet inference + Philosopher/Philosopher-style FDR filtering | Native EM apportionment + 2-level FDR |
| Deep dataset (many thousands of proteins) | Picked-GROUP FDR, NOT naive decoy/target | Naive protein-FDR inflates to 10-30% from one-hit-wonders |
| Sensitive differential abundance downstream | Quantify on unique peptides only -> quantification | Razor assignment can flip between conditions and fake DE |
| Want isoform-level answers | Stop -- route to top-down / proteoform methods | Bottom-up groups cannot resolve proteoforms |
| Few PSMs (single-protein pulldown) | Report evidence, do not trust a "0% protein FDR" | Target-decoy FDR is meaningless at tiny counts |
Default when uncertain: run parsimony grouping (BasicProteinInferenceAlgorithm with annotate_indistinguishable_groups), report protein GROUPS with a leading accession, and control protein-GROUP FDR with picked-group FDR at 1%. Do NOT impose a two-peptide rule.
Goal: Turn an FDR-filtered peptide identification list into protein groups with a leading protein, resolving shared-peptide ambiguity.
Approach: Load the idXML from peptide identification, run the parsimony algorithm with indistinguishable-group annotation on, then read the inferred groups off the protein identification run.
from pyopenms import IdXMLFile, BasicProteinInferenceAlgorithm
protein_ids = []
peptide_ids = []
# protein_ids is FIRST in both load() and store() for IdXMLFile
IdXMLFile().load('peptides_1pct_fdr.idXML', protein_ids, peptide_ids)
inference = BasicProteinInferenceAlgorithm()
params = inference.getParameters()
# annotate_indistinguishable_groups reports indistinguishable proteins as ONE group
params.setValue('annotate_indistinguishable_groups', 'true')
inference.setParameters(params)
inference.run(peptide_ids, protein_ids)
# indistinguishable groups live on the protein identification run
for prot_id in protein_ids:
for group in prot_id.getIndistinguishableProteins():
leading = group.accessions[0] # convention: highest-evidence accession first
print(leading, group.probability, list(group.accessions))
Goal: Assign calibrated protein/group posteriors and control protein-group FDR with a probability model rather than greedy parsimony.
Approach: EPIFANY consumes idXML whose PSMs already carry posterior error probabilities (from Percolator or IDPosteriorErrorProbability), then propagates belief over the peptide-protein graph. The TOPP tool is reliably named Epifany; the pyOpenMS class spelling has varied across releases, so introspect first.
import pyopenms
from pyopenms import IdXMLFile
# CONFIRM the class name at the installed version before use:
# help(pyopenms.EpifanyAlgorithm)
algo_cls = getattr(pyopenms, 'EpifanyAlgorithm')
protein_ids = []
peptide_ids = []
IdXMLFile().load('peptides_with_pep.idXML', protein_ids, peptide_ids)
algo = algo_cls()
# EPIFANY expects PSM posteriors as input; greedy_group_resolution controls
# whether shared peptides are razor-resolved after inference
algo.inferPosteriorProbabilities(protein_ids, peptide_ids, False)
for prot_id in protein_ids:
for group in prot_id.getIndistinguishableProteins():
print(group.accessions[0], group.probability)
Goal: Estimate protein-group FDR without the inflation that the reused PSM formula causes on large data.
Approach: For each target group, find its decoy counterpart (same accessions with the decoy prefix); keep only the higher-scoring member of each target/decoy PAIR; rank the picked set and count decoys as the FDR estimate. This is the operation the reference example demonstrates end to end.
def picked_group_fdr(groups, decoy_prefix='DECOY_'):
# groups: list of dicts with 'accessions', 'score', 'is_decoy'
by_base = {}
for g in groups:
base = frozenset(a.replace(decoy_prefix, '') for a in g['accessions'])
# keep only the higher-scoring of the target/decoy pair (the 'pick')
if base not in by_base or g['score'] > by_base[base]['score']:
by_base[base] = g
picked = sorted(by_base.values(), key=lambda g: g['score'], reverse=True)
targets = decoys = 0
for g in picked:
if g['is_decoy']:
decoys += 1
else:
targets += 1
g['fdr'] = decoys / targets if targets else 1.0
running_min = 1.0
for g in reversed(picked): # monotone q-values from the bottom up
running_min = min(running_min, g['fdr'])
g['qvalue'] = running_min
return [g for g in picked if not g['is_decoy'] and g['qvalue'] <= 0.01]
Trigger: Reusing the PSM-level decoys/targets formula at the protein level on a deep dataset.
Mechanism: False target proteins (one-hit-wonders) and decoy proteins are not symmetric once peptides are mapped to proteins; true proteins absorb many peptides, false ones do not.
Symptom: Reported 1% protein FDR, actual 10-30%; reviewer or entrapment check exposes it.
Fix: Picked-protein FDR (Savitski 2015) or picked-group FDR (The & Kall 2016); validate with a two-species or entrapment search.
Trigger: Filtering to proteins with >=2 (unique) peptides "for confidence". Mechanism: The rule removes more target proteins than decoy proteins, inverting the FDR effect, and deletes real low-abundance single-peptide proteins. Symptom: Fewer proteins AND higher true FDR than picked FDR at the same nominal cutoff. Fix: Drop the rule; control picked protein-level FDR and score single-peptide IDs individually.
Trigger: Quantifying on MaxQuant's default unique+razor peptides for a sensitive comparison. Mechanism: A shared peptide's full intensity is credited to one group; that razor assignment can flip between conditions when peptide counts shift, so a protein's quantity changes for inference reasons, not biology. Symptom: Spurious differential abundance concentrated on proteins sharing peptides with paralogs. Fix: Quantify on unique peptides only for sensitive comparisons -> quantification.
Trigger: Multiple minimal protein sets explain the peptides equally well. Mechanism: Greedy parsimony breaks ties arbitrarily; minimality is a heuristic, not truth, and a real protein with only shared peptides is silently dropped. Symptom: Reported lead protein differs run-to-run or pipeline-to-pipeline on the same data. Fix: Prefer a probabilistic method (EPIFANY/ProteinProphet) that apportions shared evidence; retain group membership.
Trigger: Reporting "isoform X is present" from a group whose evidence is shared peptides. Mechanism: Splice isoforms, variants, and PTM forms collapse into groups in bottom-up data; the group cannot separate them. Symptom: Isoform-specific claim with no isoform-unique peptide behind it. Fix: Require an isoform-unique peptide for any isoform claim, or use top-down / proteoform methods.
| Threshold | Source | Rationale | |-----------|--------|-----------| | Protein / protein-group FDR 1% (sometimes 5% for discovery) | community standard | SEPARATE estimation from PSM FDR; never assume 1% PSM implies 1% protein | | Picked FDR (target/decoy pairing) | Savitski 2015; The & Kall 2016 | Removes target/decoy asymmetry; dataset-size-independent, unlike naive decoy/target | | Decoy:target ratio 1:1 | community standard | Standard null; unequal ratios require formula correction | | Min PSMs for trustworthy protein FDR | hundreds+ | Below ~100s of items decoy counts are too noisy; "0% FDR" from zero decoys is luck, not control | | Two-peptide rule | DO NOT USE (Gupta & Pevzner 2009) | Increases protein FDR and drops real proteins; replaced by picked FDR + per-ID score | | Single-peptide IDs | judge by score, not count | A high-confidence unique peptide can be a legitimate ID |
| Error / symptom | Cause | Solution |
|-----------------|-------|----------|
| Protein FDR much higher than nominal on deep data | Naive decoy/target reused from PSM level | Picked-protein or picked-group FDR |
| Real low-abundance proteins missing | Two-peptide rule applied | Remove the rule; control picked FDR |
| AttributeError on EpifanyAlgorithm / infer_proteins | Class name varies by version; the R ProteinInference::infer_proteins could not be confirmed to exist | help(pyopenms.EpifanyAlgorithm) to find the real name; use pyOpenMS, not an unverified R package |
| Indistinguishable proteins reported as separate IDs | Flat protein list instead of groups | Enable annotate_indistinguishable_groups; report groups with a leading protein |
| Spurious DE on paralog-sharing proteins | Razor-peptide quant flipped between conditions | Quantify on unique peptides -> quantification |
| "Unique" peptide count changed when DB changed | Uniqueness is database-relative | Fix and document the database (isoforms, contaminants, decoys) |
tools
End-to-end CLIP-seq pipeline from FASTQ to ENCODE-compliant binding sites, single-nucleotide crosslink maps, annotation, motifs, and (optionally) differential binding. Use when running the full Yeo lab eCLIP / iCLIP / iCLIP2 / iCLIP3 / irCLIP / PAR-CLIP analysis with SMInput control, protocol-specific UMI extraction, ENCODE STAR parameters, CLIPper or Skipper peak calling with stringent log2 FC and -log10 p thresholds, IDR rescue and self-consistency QC, and downstream motif registration with mCross or PEKA.
development
Detect, date, and contextualize whole-genome duplication (WGD / paleopolyploidy) events using wgd v2 (Chen et al 2024), KsRates (Sensalari 2022 substitution-rate-corrected Ks dating), DupGen_finder (Qiao 2019), MAPS (Li 2018 phylogenomic), POInT (Conant 2008 ordered-block), SLEDGe (2024 ML-based), Whale.jl (Bayesian DL+WGD), and synteny-anchored paranome construction. Use when identifying ancient polyploidy from Ks distributions and synteny block analysis, positioning WGD events relative to speciation, distinguishing tandem from segmental from WGD duplications, dating the 2R/3R vertebrate / fish / salmonid WGDs, building paranome and Ks-age mixture models, applying KsRates substitution-rate correction across lineages, or testing alternative biased-fractionation / dosage-balance models post-WGD.
tools
Build whole-genome alignments using Progressive Cactus (Armstrong 2020 reference-free clade-level WGA), Minigraph-Cactus (Hickey 2024 pangenome-aware), LASTZ chain/net (UCSC pipeline), MUMmer4 (Marçais 2018 pairwise), minimap2 -x asm5/10/20 (Li 2018 fast pairwise), AnchorWave (Song 2022 WGD-aware), and Mauve / progressiveMauve (bacterial). Operates the HAL toolkit (Hickey 2013) for downstream extraction including halSynteny, halLiftover, halBranchMutations, and hal2maf. Use when constructing multi-species alignments for comparative-annotation projection (TOGA), synteny detection, conservation analyses (phyloP / PhastCons), or pangenome graph construction; selecting between reference-free (Cactus) and reference-anchored (LASTZ chains/nets) approaches; tuning sensitivity for closely vs distantly related genomes; or producing HAL files for genome-wide downstream tools.
development
Detect syntenic blocks and structural rearrangements between genomes using MCScanX (Wang 2012), JCVI/MCScan (Tang 2008 Python), GENESPACE (Lovell 2022) for orthology-anchored riparian visualization, SyRI for structural variation, AnchorWave for sequence-level synteny, i-ADHoRe 3.0 for highly diverged species, SynNet for synteny networks, and ntSynt for multi-genome macrosynteny. Use when identifying collinear gene blocks across species, distinguishing macrosynteny from microsynteny, detecting inversions/translocations/duplications, anchoring orthology in WGD lineages, producing publication riparian plots, computing synteny block age via Ks (cross-references whole-genome-duplication), or running synteny-aware ortholog inference in polyploids.