A new dataset end-to-end: Sadeghi 2026 FragPipe PSMs¶
This walkthrough takes a new search-engine output - a FragPipe/MSFragger combined PSM table - from raw PSMs to detected regions in one run() call, entirely offline.
We use a committed 50-PSM sample of the FragPipe fractionation sheet (Sheet12) from the Sadeghi 2026 breast-cancer cohort (PXD077545). The full 181 MB table is available from the study's PRIDE/Zenodo record and is deliberately not committed; swap the cohort YAML to point at the full export and everything below runs unchanged.
Scope note. This notebook demonstrates package usage. The boundary-concordance validation against top-down spans (Barnes round) is a separate analysis with its own task track; see the docs page Methods vs paper modules. On Colab: the walkthrough assets live in the repository - download
examples/walkthrough/alongside this notebook (or clone the repo once it is public).
try:
import proteoform_regions
except ImportError:
%pip install proteoform-regions
from pathlib import Path
# works from the repo root, from docs/, and in Colab beside examples/
WALKTHROUGH = next(
(p for p in (Path("examples/walkthrough"), Path("../examples/walkthrough"), Path("walkthrough")) if p.exists()),
None,
)
assert WALKTHROUGH is not None, "walkthrough assets not found beside the notebook"
print("assets:", sorted(str(p.relative_to(WALKTHROUGH)) for p in WALKTHROUGH.rglob("*") if p.is_file()))
assets: ['PXD077545/sheet12_sample50.csv', 'cohort-sheet12-sample.yaml', 'uniprot_snapshot_20261007.json']
1. Declare the study (not code - data)¶
Study metadata lives in a declarative cohort YAML; the fragpipe adapter is a pure function of (config, files). Adding a dataset is a YAML edit, never a code change.
from proteoform_regions import load_cohort
cohort = WALKTHROUGH / "cohort-sheet12-sample.yaml"
print(cohort.read_text())
studies = load_cohort(cohort)
studies[0]
# Walkthrough cohort: 50-row sample of the Sadeghi 2026 FragPipe PSM sheet
# (breast-cancer top-down/fractionation cohort, PXD077545).
#
# The committed file is a 50-PSM sample of FragPipe_Fractions_psm (Sheet12),
# pinned by the test suite; the full table is available from the study's
# PRIDE/Zenodo record - it is deliberately NOT committed (181 MB xlsx).
studies:
- dataset_accession: PXD077545
adapter: fragpipe
sample_type: breast cancer tissue (BC fractions, BUP LC-CE arms)
disease_context: breast cancer vs non-cancer control (fractionation cohort)
assay_type: LC-MS/MS
acquisition_mode: DDA
search_engine: FragPipe/MSFragger
representative_file_arm: breast-cancer fraction arm (interact.pep.xml)
representative_file_species: Homo sapiens
files:
- name: sheet12_sample50.csv
url: null
StudyConfig(dataset_accession='PXD077545', adapter='fragpipe', study_id='PXD077545_2026', study_year='2026', parser_family='fragpipe_psm_table', sample_type='breast cancer tissue (BC fractions, BUP LC-CE arms)', disease_context='breast cancer vs non-cancer control (fractionation cohort)', assay_type='LC-MS/MS', acquisition_mode='DDA', search_engine='FragPipe/MSFragger', representative_file_arm='breast-cancer fraction arm (interact.pep.xml)', representative_file_species='Homo sapiens', publication_doi=None, files=[FileSpec(name='sheet12_sample50.csv', url=None, sha256=None)], build_usi=None, extract_protein_names=True, drop_zero_intensities=False, psm_count_zero_as_none=False)
2. One run(), all stages, offline¶
run() executes harmonize -> map -> compute -> background -> stitch (we stop before bias here: a 50-PSM sample is one study, and RQ2 is a k-study meta-analysis - running it on n=1 would be meaningless). Everything resolves offline against the committed, dated UniProt snapshot (uniprot_snapshot_20261007.json) - the same snapshot-pinning semantics the paper used for its freeze.
from proteoform_regions import run
out_dir = Path("walkthrough_run")
report = run(
cohort, out_dir,
stages=["harmonize", "map", "compute", "background", "stitch"],
data_dir=WALKTHROUGH, # raw PSM sample lives here
download=False, # files are committed; never fetch
uniprot_cache=WALKTHROUGH, # committed snapshot
snapshot_date="20261007",
offline=True, # hard guarantee: no network
)
report.counts
{'harmonize': 23,
'map': 23,
'map_properties': 22,
'compute': 23,
'background': 6600,
'stitch': 23}
Every artifact uses the paper's data/processed/ vocabulary, and the run closes with a sha256 manifest over its own outputs:
for name in sorted(p.name for p in out_dir.iterdir()):
print(name)
print()
print((out_dir / "manifest.sha256").read_text().splitlines()[0], "...")
manifest.sha256 peptide_evidence_table.csv processed_feature_table.csv processed_peptide_protein_map.csv processed_protein_properties.csv processed_region_inference.csv processed_tryptic_background.csv run_report.json db14b3e2d99baf63d172015d0e84e9c2e32a0e9c858afdedbab508b949cb9abf peptide_evidence_table.csv ...
3. What came out of 50 PSMs¶
- Evidence (
peptide_evidence_table.csv): PSMs aggregated to one row per (stripped peptide, protein), carrying FragPipe protein coordinates - so region inference can run directly on the evidence. - Mapping (
processed_peptide_protein_map.csv): exact-substring peptide-to-protein with explicit tiers; degenerate peptides are flagged, never silently dropped. - Features / background: peptide pI/MW (Bjellqvist engine) plus the digestion-matched tryptic background over the 22 parent proteins.
- Regions (
processed_region_inference.csv): single-linkage stitching at the paper's gap = 25 aa.
import pandas as pd
regions = pd.read_csv(out_dir / "processed_region_inference.csv")
cols = ["mapped_accession", "gene_name", "region_start", "region_end",
"region_n_peptides", "region_pi", "region_pi_class_anl003",
"region_pi_divergence", "confidence_label"]
regions[cols].head(8)
| mapped_accession | gene_name | region_start | region_end | region_n_peptides | region_pi | region_pi_class_anl003 | region_pi_divergence | confidence_label | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | A0A096LPE2 | SAA2-SAA4 | 192 | 198 | 1 | 5.679489 | acidic | -3.415663 | peptide_only |
| 1 | O00139 | KIF2A | 464 | 472 | 1 | 4.207813 | acidic | -2.070644 | peptide_only |
| 2 | O00410 | IPO5 | 1053 | 1059 | 1 | 5.320323 | acidic | 0.490520 | peptide_only |
| 3 | P00450 | CP | 202 | 209 | 1 | 6.120445 | acidic | 0.680533 | peptide_only |
| 4 | P01008 | SERPINC1 | 61 | 71 | 1 | 4.407318 | acidic | -1.913712 | peptide_only |
| 5 | P02787 | TF | 611 | 618 | 1 | 6.741980 | acidic | -0.068889 | peptide_only |
| 6 | P06396 | GSN | 349 | 355 | 1 | 4.531398 | acidic | -1.364988 | peptide_only |
| 7 | P08697 | SERPINF2 | 446 | 454 | 1 | 4.370430 | acidic | -1.499015 | peptide_only |
# how acidic/basic are the detected pieces vs their precursors?
print(regions["region_pi_class_anl003"].value_counts().to_string())
print()
print("median region-vs-precursor pI divergence:", round(regions["region_pi_divergence"].median(), 3))
region_pi_class_anl003 acidic 22 basic 1 median region-vs-precursor pI divergence: -1.113
Single-peptide regions are labeled peptide_only; a region needs at least 2 member peptides within the 25 aa gap to be a detected_region. On the full sheet, multi-peptide regions appear in proportion to sequence coverage.
4. Next step on a full cohort: detection-bias quantification¶
With the full cohort (multiple studies), stage 8 turns the feature table + background into the paper's headline statistics - per-study Woolf ORs, fixed/random-effects meta-analysis, the leave-one-out envelope, and the RQ1 digestion-matched permutation null:
# on a full cohort (do NOT run on this 50-PSM sample):
#
# report = run("cohort.yaml", "run/", stages="all",
# uniprot_cache="data/raw/uniprot_cache", offline=True)
#
# or just the stats stage on existing artifacts:
# proteoform-regions bias \
# --features run/processed_feature_table.csv \
# --background run/processed_tryptic_background.csv --out results.json
print("see docs: CLI reference and methods-mapping pages")
see docs: CLI reference and methods-mapping pages
Recap¶
| stage | artifact | rows here |
|---|---|---|
| harmonize | peptide_evidence_table.csv |
50 PSMs to 23 peptide-protein rows |
| map | processed_peptide_protein_map.csv + properties |
23 / 22 proteins |
| compute | processed_feature_table.csv |
23 |
| background | processed_tryptic_background.csv |
~6.6 k tryptic peptides |
| stitch | processed_region_inference.csv |
23 candidate regions |
Determinism note: every stochastic knob (seed, b_perm, b_boot) is an explicit parameter with the paper's defaults; the same inputs + snapshot reproduce the same tables byte-for-byte modulo timestamps.