PyOpenMS — Mass Spectrometry Analysis
Overview
PyOpenMS provides Python bindings to the OpenMS C++ library for computational mass spectrometry. It supports proteomics and metabolomics data processing including file I/O for 10+ MS formats, signal processing, feature detection, peptide/protein identification, and quantitative analysis across samples.
When to Use
- Processing raw LC-MS/MS data (mzML, mzXML) for proteomics or metabolomics
- Detecting chromatographic features and linking them across multiple samples
- Identifying peptides and proteins from MS/MS search engine results with FDR control
- Running untargeted metabolomics workflows (peak picking → feature detection → alignment → annotation)
- Converting between mass spectrometry file formats (mzML, mzXML, featureXML, idXML)
- Smoothing, filtering, and centroiding raw spectral data
- For simple spectral library matching and metabolite identification, use matchms instead
- For protein sequence analysis (not mass spec), use biopython instead
Prerequisites
uv pip install pyopenms numpy pandas matplotlib
- Python 3.8+; NumPy for peak array operations
- Input data: mzML files (standard MS format), FASTA databases (for identification)
- All algorithms follow a consistent pattern:
algo = Algorithm(); params = algo.getParameters(); params.setValue(...); algo.setParameters(params)
Quick Start
import pyopenms as ms
# Load mzML file
exp = ms.MSExperiment()
ms.MzMLFile().load("sample.mzML", exp)
print(f"Spectra: {exp.getNrSpectra()}, Chromatograms: {exp.getNrChromatograms()}")
# Examine first spectrum
spec = exp.getSpectrum(0)
mz, intensity = spec.get_peaks()
print(f"MS level: {spec.getMSLevel()}, RT: {spec.getRT():.2f}s, Peaks: {len(mz)}")
# Quick preprocessing: smooth + centroid
gauss = ms.GaussFilter()
p = gauss.getParameters(); p.setValue("gaussian_width", 0.1); gauss.setParameters(p)
gauss.filterExperiment(exp)
picker = ms.PeakPickerHiRes()
centroided = ms.MSExperiment()
picker.pickExperiment(exp, centroided)
print(f"Centroided spectra: {centroided.getNrSpectra()}")
Core API
Module 1: File I/O & Data Access
Read and write mass spectrometry data in multiple formats.
import pyopenms as ms
# Read mzML (standard MS format)
exp = ms.MSExperiment()
ms.MzMLFile().load("data.mzML", exp)
# Indexed access for large files (memory-efficient)
loader = ms.IndexedMzMLFileLoader()
indexed_file = ms.OnDiscMSExperiment()
loader.load("large_data.mzML", indexed_file)
spec = indexed_file.getSpectrum(0) # Load single spectrum on demand
print(f"Total spectra: {indexed_file.getNrSpectra()}")
# Read identification results (idXML)
protein_ids, peptide_ids = [], []
ms.IdXMLFile().load("results.idXML", protein_ids, peptide_ids)
print(f"Peptide IDs: {len(peptide_ids)}, Protein IDs: {len(protein_ids)}")
# Read feature map
fm = ms.FeatureMap()
ms.FeatureXMLFile().load("features.featureXML", fm)
print(f"Features: {fm.size()}")
# Write mzML with compression
exp_out = ms.MSExperiment()
# ... populate experiment ...
ms.MzMLFile().store("output.mzML", exp_out)
# Read FASTA database
entries = []
ms.FASTAFile().load("database.fasta", entries)
print(f"Proteins in DB: {len(entries)}")
for e in entries[:3]:
print(f" {e.identifier}: {e.sequence[:30]}...")
Supported formats: mzML, mzXML, mzData (spectra); featureXML, consensusXML (features); idXML, mzIdentML, pepXML (identifications); TraML (transitions); mzTab (results); FASTA (sequences)
Module 2: Signal Processing & Peak Picking
Preprocess raw spectral data for downstream analysis.
import pyopenms as ms
exp = ms.MSExperiment()
ms.MzMLFile().load("raw.mzML", exp)
# Gaussian smoothing
gauss = ms.GaussFilter()
p = gauss.getParameters()
p.setValue("gaussian_width", 0.15) # m/z width
gauss.setParameters(p)
gauss.filterExperiment(exp)
# Savitzky-Golay smoothing (alternative)
sg = ms.SavitzkyGolayFilter()
p = sg.getParameters()
p.setValue("frame_length", 15) # Must be odd
sg.setParameters(p)
# sg.filterExperiment(exp) # Use one smoother, not both
# Peak picking (centroiding) — required before feature detection
picker = ms.PeakPickerHiRes()
p = picker.getParameters()
p.setValue("signal_to_noise", 1.0)
picker.setParameters(p)
centroided = ms.MSExperiment()
picker.pickExperiment(exp, centroided)
print(f"Raw peaks in spec 0: {exp.getSpectrum(0).size()}")
print(f"Centroided peaks: {centroided.getSpectrum(0).size()}")
# Normalization
normalizer = ms.Normalizer()
p = normalizer.getParameters()
p.setValue("method", "to_one") # "to_one" or "to_TIC"
normalizer.setParameters(p)
normalizer.filterPeakMap(centroided)
# Peak filtering — remove low-intensity noise
mower = ms.ThresholdMower()
p = mower.getParameters()
p.setValue("threshold", 100.0) # Minimum intensity
mower.setParameters(p)
mower.filterPeakMap(centroided)
# Baseline reduction
morph = ms.MorphologicalFilter()
p = morph.getParameters()
p.setValue("struc_elem_length", 3.0) # m/z window
morph.setParameters(p)
morph.filterExperiment(exp)
Module 3: Feature Detection & Linking
Detect chromatographic features and link them across samples.
import pyopenms as ms
# Load centroided data
exp = ms.MSExperiment()
ms.MzMLFile().load("centroided.mzML", exp)
# Feature detection (proteomics — centroided data)
ff = ms.FeatureFinder()
features = ms.FeatureMap()
seeds = ms.FeatureMap()
params = ms.FeatureFinder().getParameters("centroided")
ff.run("centroided", exp, features, params, seeds)
print(f"Detected {features.size()} features")
# Access feature properties
for f in features[:5]:
print(f" RT: {f.getRT():.1f}s, m/z: {f.getMZ():.4f}, "
f"intensity: {f.getIntensity():.0f}, quality: {f.getOverallQuality():.3f}")
# Feature linking across samples — align retention times first
aligner = ms.MapAlignmentAlgorithmPoseClustering()
p = aligner.getParameters()
p.setValue("max_num_peaks_considered", 1000)
aligner.setParameters(p)
# Link features into consensus map
linker = ms.FeatureGroupingAlgorithmQT()
p = linker.getParameters()
p.setValue("distance_RT:max_difference", 60.0) # seconds
p.setValue("distance_MZ:max_difference", 10.0) # ppm
linker.setParameters(p)
consensus = ms.ConsensusMap()
linker.group([features_sample1, features_sample2, features_sample3], consensus)
print(f"Consensus features: {consensus.size()}")
# Export to pandas for downstream analysis
import pandas as pd
df = consensus.get_df()
print(f"Consensus table: {df.shape}")
Module 4: Peptide & Protein Identification
Process search engine results with FDR control and protein inference.
import pyopenms as ms
# Load search engine results
protein_ids, peptide_ids = [], []
ms.IdXMLFile().load("search_results.idXML", protein_ids, peptide_ids)
# Examine peptide hits
for pep_id in peptide_ids[:3]:
print(f"Spectrum: RT={pep_id.getRT():.1f}, MZ={pep_id.getMZ():.4f}")
for hit in pep_id.getHits():
seq = hit.getSequence()
print(f" {seq} score={hit.getScore():.4f} charge={hit.getCharge()}")
# FDR filtering (target-decoy approach)
fdr = ms.FalseDiscoveryRate()
fdr.apply(peptide_ids)
# Filter at 1% FDR
filtered = []
for pep_id in peptide_ids:
hits = [h for h in pep_id.getHits() if h.getScore() <= 0.01]
if hits:
pep_id.setHits(hits)
filtered.append(pep_id)
print(f"Peptide IDs at 1% FDR: {len(filtered)}")
# Protein inference
inference = ms.BasicProteinInferenceAlgorithm()
inference.run(peptide_ids, protein_ids)
for prot_id in protein_ids:
for hit in prot_id.getHits()[:5]:
print(f"Protein: {hit.getAccession()}, score: {hit.getScore():.4f}")
# Peptide sequence handling
seq = ms.AASequence.fromString("PEPTIDER")
print(f"Molecular weight: {seq.getMonoWeight():.4f}")
print(f"Formula: {seq.getFormula()}")
# Modified sequence
mod_seq = ms.AASequence.fromString("PEPTM(Oxidation)DER")
print(f"Modified weight: {mod