Readers
spxtacular provides two reader classes that expose a uniform interface for iterating over MsnSpectrum objects regardless of the underlying file format.
Both readers yield MsnSpectrum instances populated with as much instrument metadata as the format provides. All spectrum-processing methods (.filter(), .denoise(), .deconvolute(), etc.) are immediately available on each yielded object.
MzmlReader
Reads standard .mzML files using mzmlpy. No context manager is required, but one is supported.
class MzmlReader:
def __init__(self, mzml_path: str): ...
@property
def ms1(self) -> Generator[MsnSpectrum, None, None]: ...
@property
def ms2(self) -> Generator[MsnSpectrum, None, None]: ...
Properties
| Property | Yields |
|---|---|
ms1 |
All MS1 spectra in scan order |
ms2 |
All MS2 spectra in scan order, including parsed precursor information |
Metadata populated from mzML
| Field | Source |
|---|---|
scan_number |
Spectrum index |
ms_level |
msLevel CV param |
native_id |
Raw spectrum id attribute |
rt |
scan start time (converted to seconds) |
mz_range |
scan window lower/upper limit |
polarity |
positive scan / negative scan CV params |
spectrum_type |
centroid spectrum / profile spectrum CV params |
charge (array) |
Per-peak charge array when present in the binary data |
im (array) |
First ion mobility binary array when present |
precursors |
MS2 only: selected ion m/z, intensity, charge, and activation info |
collision_energy |
MS2 only: from activation element |
activation_type |
MS2 only: from activation element |
Examples
Iterate MS1 spectra:
from spxtacular import MzmlReader
reader = MzmlReader("run.mzML")
for spec in reader.ms1:
print(spec)
# MsnSpectrum(scan=0, ms_level=1, rt=1.23s, polarity=positive, n_peaks=4521)
Filter and denoise each MS1 scan:
from spxtacular import MzmlReader
reader = MzmlReader("run.mzML")
for spec in reader.ms1:
processed = spec.filter(min_mz=200, max_mz=1600).denoise("mad")
print(f"Scan {spec.scan_number}: {len(processed)} peaks after denoise")
Iterate MS2 spectra with precursor info:
from spxtacular import MzmlReader
reader = MzmlReader("run.mzML")
for spec in reader.ms2:
if not spec.precursors:
continue
prec = spec.precursors[0]
print(
f"Scan {spec.scan_number} | "
f"Precursor {prec.mz:.4f} m/z, z={prec.charge} | "
f"CE={spec.collision_energy} eV"
)
Full deconvolution pipeline on MS1:
from spxtacular import MzmlReader
reader = MzmlReader("run.mzML")
for spec in reader.ms1:
neutral = (
spec
.filter(min_mz=300, min_intensity=1000)
.denoise("mad")
.deconvolute(charge_range=(1, 5), tolerance=10, tolerance_type="ppm")
.decharge()
)
for peak in neutral.top_peaks(10):
print(f" mass={peak.mz:.4f} Da intensity={peak.intensity:.2e}")
break # first scan only
Use as a context manager (optional):
MzmlReader supports the context manager protocol but the __exit__ method is a no-op — the underlying file handle is managed internally per-property call.
DReader
Reads Bruker timsTOF .d directories using tdfpy. Must be used as a context manager — the underlying tdfpy handle is opened on __enter__ and closed on __exit__.
class DReader:
def __init__(self, analysis_dir: str): ...
def __enter__(self) -> DReader: ...
def __exit__(self, exc_type, exc_val, exc_tb): ...
@property
def ms1(self) -> Generator[MsnSpectrum, None, None]: ...
@property
def ms2(self) -> Generator[MsnSpectrum, None, None]: ...
The acquisition type (DDA, DIA, PRM) is detected automatically from the .d directory at construction time and stored as reader.aquisition_type (AcquisitionType enum).
Properties
| Property | Yields |
|---|---|
ms1 |
All MS1 frames, centroided and merged by tdfpy |
ms2 |
All MS2 spectra (DDA: per-precursor; DIA: per isolation window) |
Metadata populated from timsTOF
MS1:
| Field | Source |
|---|---|
scan_number |
frame_id |
ms_level |
Always 1 |
rt |
Frame acquisition time (seconds) |
injection_time |
Frame accumulation time (ms) |
mz_range |
Instrument acquisition range from metadata |
im_range |
1/K0 acquisition range from metadata |
im (array) |
Per-peak 1/K0 values |
analyzer |
Always "TOF" |
ramp_time |
Frame ramp time (ms) |
polarity |
From frame polarity field |
spectrum_type |
Always CENTROID (timsTOF data arrives centroided) |
MS2 (DDA):
| Field | Source |
|---|---|
scan_number |
precursor_id |
ms_level |
Always 2 |
rt |
Retention time (seconds) |
mz_range |
Precursor isolation window |
im_range |
1/K0 range of precursor |
precursors |
Single TargetIon with monoisotopic m/z (or largest peak m/z if unavailable), intensity, charge, and 1/K0 |
collision_energy |
From precursor record |
activation_type |
"MS:1002481" (PASEF) |
MS2 (DIA):
| Field | Source |
|---|---|
scan_number |
frame_id |
native_id |
"{frame_id}@w{window_index}" |
ms_level |
Always 2 |
rt |
Retention time (seconds) |
mz_range |
Isolation window m/z range |
im (array) |
Per-peak 1/K0 values |
collision_energy |
From window record |
precursors |
None — DIA windows have no defined precursor |
Examples
Iterate MS1 frames (DDA or DIA):
from spxtacular import DReader
with DReader("/data/sample.d") as reader:
print(f"Acquisition type: {reader.aquisition_type}")
for spec in reader.ms1:
print(spec)
# MsnSpectrum(scan=1, ms_level=1, rt=0.42s, polarity=positive, n_peaks=8234)
break
MS1 with ion mobility filtering:
from spxtacular import DReader
with DReader("/data/sample.d") as reader:
for spec in reader.ms1:
# Keep only peaks in a specific 1/K0 window
filtered = spec.filter(min_im=0.8, max_im=1.2, min_intensity=500)
if len(filtered) == 0:
continue
neutral = (
filtered
.deconvolute(charge_range=(1, 5), tolerance=15, tolerance_type="ppm")
.decharge()
)
print(f"Frame {spec.scan_number}: {len(neutral)} neutral masses")
MS2 DDA — inspect precursors:
from spxtacular import DReader
with DReader("/data/sample_dda.d") as reader:
for spec in reader.ms2:
if not spec.precursors:
continue
prec = spec.precursors[0]
print(
f"Precursor {spec.scan_number}: "
f"m/z={prec.mz:.4f}, z={prec.charge}, "
f"1/K0={prec.im:.3f}, "
f"monoisotopic={prec.is_monoisotopic}"
)
break
MS2 DIA — iterate isolation windows:
from spxtacular import DReader
with DReader("/data/sample_dia.d") as reader:
for spec in reader.ms2:
print(
f"{spec.native_id}: "
f"mz_range={spec.mz_range}, "
f"CE={spec.collision_energy}"
)
break
AcquisitionType
Detected automatically by DReader from the .d directory. Accessible as reader.aquisition_type. PRM and UNKNOWN acquisition types are handled the same way as DDA for MS2 iteration.