| name | bio-applied-proteomics |
| description | Compute peptide b/y ion masses, run trypsin/PMF search, quantify LFQ protein abundance (volcano plots), and calculate PTM shifts and protein inference. Use for MS/MS peptide ID, PMF search, LFQ quantification, or PTM analysis. |
| tool_type | python |
| primary_tool | pandas |
Applied Proteomics
When to Use
- Interpreting an MS/MS spectrum's b/y ion ladder or checking a peptide-spectrum match by hand
- Simulating in-silico tryptic digestion (with missed cleavages) and matching masses via peptide mass fingerprinting (PMF)
- Quantifying label-free (LFQ) or TMT protein abundance between two conditions and building volcano/MA plots
- Computing PTM mass shifts (phosphorylation, oxidation, acetylation) on identified peptides
- Resolving protein inference when peptides map to multiple protein entries (razor peptide / parsimony)
Version Compatibility
- Python ≥3.10, NumPy ≥1.26, pandas ≥2.2, Matplotlib ≥3.8, SciPy ≥1.12
- Applies to bottom-up (shotgun) LC-MS/MS workflows (Orbitrap/Q-TOF, DDA); concepts map onto Mascot/MaxQuant/MSFragger/Comet search output
Prerequisites
pip install numpy pandas matplotlib scipy
- Familiarity with amino acid chemistry and basic mass spectrometry (m/z, charge states)
- Related skills:
bio-proteomics-peptide-identification, bio-proteomics-quantification
MS/MS Fragmentation: b/y Ions
Goal: derive peptide and fragment-ion masses from a sequence to interpret or simulate an MS/MS spectrum.
Approach: sum monoisotopic residue masses; b ions are N-terminal fragments (+ proton, no water), y ions are C-terminal fragments (+ water + proton).
import numpy as np
AA_MONO_MASS = {
'A': 71.03711, 'R': 156.10111, 'N': 114.04293, 'D': 115.02694,
'C': 103.00919, 'E': 129.04259, 'Q': 128.05858, 'G': 57.02146,
'H': 137.05891, 'I': 113.08406, 'L': 113.08406, 'K': 128.09496,
'M': 131.04049, 'F': 147.06841, 'P': 97.05276, 'S': 87.03203,
'T': 101.04768, 'W': 186.07931, 'Y': 163.06333, 'V': 99.06841,
}
H2O = 18.01056
H = 1.00728
def peptide_mass(seq: str) -> float:
"""Neutral monoisotopic mass of a peptide (sum of residues + one water)."""
return sum(AA_MONO_MASS[aa] for aa in seq) + H2O
() -> [[], []]:
n = (seq)
b_ions, y_ions = [], []
i (, n):
b_ions.append((AA_MONO_MASS[seq[j]] j (i)) + H)
y_ions.append((AA_MONO_MASS[seq[j]] j (i, n)) + H2O + H)
b_ions, y_ions
peptide =
b, y = by_ions(peptide)
()
i, (bi, yi) ((b, y), ):
()
Trypsin Digestion and Peptide Mass Fingerprinting
Goal: identify a protein from a set of observed peptide masses without MS/MS (PMF), or generate the candidate peptide list for a database search.
Approach: cleave after K/R unless followed by P; compute theoretical masses for every candidate protein and count matches within a ppm tolerance.
import numpy as np
def trypsin_digest(sequence: str, missed_cleavages: int = 0) -> list[str]:
"""In-silico trypsin digestion: cleaves after K/R unless followed by P."""
seq = sequence.upper()
sites = [0]
for i in range(len(seq) - 1):
if seq[i] in ('K', 'R') and seq[i + 1] != 'P':
sites.append(i + 1)
sites.append(len(seq))
fragments = [seq[sites[i]:sites[i + 1]] for i in range(len(sites) - 1)]
fragments = [f for f in fragments if f]
if missed_cleavages == 0:
return fragments
result = list(fragments)
for mc in range(1, missed_cleavages + 1):
for i in range(len(fragments) - mc):
result.append(''.join(fragments[i:i + mc + 1]))
return ((result), key= x: sequence.index(x))
() -> [[, , ]]:
results = []
obs = np.array((observed_masses))
name, seq database.items():
theo_peptides = trypsin_digest(seq, missed_cleavages)
theo_masses = np.array((
peptide_mass(p) p theo_peptides (aa AA_MONO_MASS aa p)
))
matched = ( m obs np.(np.(theo_masses - m) <= m * tolerance_ppm * ))
results.append((name, matched, matched / (obs) obs.size ))
(results, key= x: -x[])
PTM Mass Shifts
Goal: account for post-translational modifications when computing a modified peptide's mass.
Approach: add the modification's monoisotopic delta mass at the modified residue(s); common shifts below.
PTM_MASS_SHIFT = {
'phospho': 79.9663,
'oxidation': 15.9949,
'acetylation': 42.0106,
'carbamidomethyl': 57.0215,
'deamidation': 0.9840,
}
def modified_peptide_mass(seq: str, mods: dict[int, str]) -> float:
"""Neutral mass of a peptide with PTMs.
mods: {0-based residue index: modification name in PTM_MASS_SHIFT}.
"""
base = peptide_mass(seq)
return base + sum(PTM_MASS_SHIFT[m] for m in mods.values())
print(f"{modified_peptide_mass('ACSDEFGHIK', {2: 'phospho'}):.4f} Da")
Protein Inference (Razor Peptides)
Goal: assign shared (non-unique) peptides to a single "razor" protein instead of double-counting them across all matching entries.
Approach: greedy parsimony — process peptides with the fewest candidate proteins first, assign each to whichever candidate already has the most peptide evidence.
from collections import Counter
def infer_proteins_razor(peptide_to_proteins: dict[str, set[str]]) -> dict[str, str]:
"""Simplified MaxQuant-style razor-peptide protein inference.
Returns {peptide: assigned_protein}.
"""
protein_counts = Counter()
for prots in peptide_to_proteins.values():
for p in prots:
protein_counts[p] += 1
assignment = {}
for pep, prots in sorted(peptide_to_proteins.items(), key=lambda kv: len(kv[1])):
assignment[pep] = max(prots, key=lambda p: protein_counts[p])
return assignment
Label-Free Quantification and Volcano Plot
Goal: compare protein abundance between two conditions from replicate LFQ log2 intensities.
Approach: median-normalize each sample, compute per-protein log2 fold change, and test significance with Welch's t-test across replicates.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
def median_normalize(log2_intensities: np.ndarray) -> np.ndarray:
"""Median-center each sample's log2 intensities (columns = replicates)."""
return log2_intensities - np.nanmedian(log2_intensities, axis=0)
def lfq_differential_abundance(intensities_a: np.ndarray, intensities_b: np.ndarray) -> pd.DataFrame:
"""Per-protein log2FC + Welch's t-test p-value between two conditions.
intensities_a/b: (n_proteins, n_replicates) log2-transformed intensity arrays.
"""
log2fc = np.nanmean(intensities_b, axis=1) - np.nanmean(intensities_a, axis=1)
_, pvals = stats.ttest_ind(intensities_b, intensities_a, axis=1, nan_policy='omit', equal_var=False)
pvals = np.nan_to_num(pvals, nan=1.0)
return pd.DataFrame({'log2fc': log2fc, 'pvalue': pvals, 'neg_log10p': -np.log10(pvals)})
np.random.seed(7)
n_proteins, n_reps = 200, 3
base = np.random.normal(27, 3, (n_proteins, 1))
cond_a = median_normalize(base + np.random.normal(0, 0.3, (n_proteins, n_reps)))
fc_true = np.zeros((n_proteins, 1))
fc_true[np.random.choice(n_proteins, 20, replace=False)] = np.random.uniform(1, 3, (, )) * np.random.choice([-, ], (, ))
cond_b = median_normalize(base + fc_true + np.random.normal(, , (n_proteins, n_reps)))
de = lfq_differential_abundance(cond_a, cond_b)
sig = (de.log2fc.() > ) & (de.pvalue < )
plt.scatter(de.log2fc, de.neg_log10p, c=np.where(sig, , ), alpha=, s=)
plt.axvline(, ls=, c=); plt.axvline(-, ls=, c=); plt.axhline(, ls=, c=)
plt.xlabel(); plt.ylabel(); plt.title()
plt.tight_layout(); plt.show()
Pitfalls
- Trypsin does not cleave before proline — skipping this rule generates false theoretical peptides and misses real ones.
- b ions carry no C-terminal OH (only + proton); confusing b/y mass formulas is the most common manual-calculation bug.
- PMF alone cannot resolve complex mixtures (multiple proteins per sample) — use LC-MS/MS with target-decoy FDR filtering instead.
- Carbamidomethylation on cysteine is a fixed modification after standard iodoacetamide alkylation; forgetting it shifts every Cys-containing peptide by 57.02 Da.
- PTM site localization is ambiguous when a peptide has multiple candidate S/T/Y residues — needs a localization score (AScore, PTM score), not just the mass shift.
- Naive protein inference (assigning a shared peptide to every matching protein) inflates protein-group counts; use razor/parsimony logic.
- Missing values in LFQ intensity matrices should be handled with proper imputation (e.g. left-censored/MNAR-aware methods), not zero-filling, before differential testing.
See Also
bio-proteomics-peptide-identification
bio-proteomics-quantification
bio-proteomics-ptm-analysis
bio-proteomics-protein-inference