Getting started¶
Installation¶
Requires Python 3.12+. Optional extras:
pip install 'tdfpy[viz]'adds matplotlib forplot_centroiding.pip install 'tdfpy[mcp]'adds the MCP server for AI agents.
The examples below use D_PATH, the path to a .d folder. The repository's test data
(tests/data/example_dda.d, example_dia.d, example_prm.d) works with every example.
Detecting acquisition type¶
Before loading data, you can inspect the acquisition type of a .d folder:
from tdfpy import get_acquisition_type
acq_type = get_acquisition_type(D_PATH)
# Returns one of: "DDA", "DIA", "PRM", "unknown"
print(acq_type)
DDA acquisitions¶
from tdfpy import DDA
with DDA(D_PATH) as dda:
# Iterate over MS1 frames
for frame in dda.ms1:
print(f"Frame {frame.frame_id} at RT {frame.rt:.1f}s")
# Centroid the frame. Returns shape (N, 3): [m/z, intensity, 1/K0]
peaks = frame.centroid()
print(f" {len(peaks)} centroided peaks")
break
# Iterate over precursors (MS2)
for precursor in dda.precursors:
print(f"Precursor {precursor.precursor_id}: {precursor.largest_peak_mz:.4f} m/z")
# MS2 peaks centroided by tdfpy (ion mobility collapsed, merged at 30 ppm)
peaks = precursor.merged_peaks()
break
DIA acquisitions¶
from tdfpy import DIA
with DIA(D_PATH) as dia:
# MS1 frames
for frame in dia.ms1:
peaks = frame.centroid()
break
# DIA windows
for window in dia.windows:
print(f"Window group {window.window_group_id}: isolation {window.isolation_mz} m/z")
peaks = window.centroid()
break
PRM acquisitions¶
from tdfpy import PRM
with PRM(D_PATH) as prm:
# MS1 frames
for frame in prm.ms1:
peaks = frame.centroid()
break
# PRM targets (precursor ions being monitored)
for target in prm.targets:
print(f"Target {target.target_id}: {target.precursor_mz:.4f} m/z, charge {target.charge}")
break
# PRM transitions (MS2 spectra linked to a target)
for transition in prm.transitions:
print(f"Transition frame {transition.frame_id}: isolation {transition.isolation_mz} m/z")
raw = transition.scan_peaks() # list of per-scan (N, 2) [m/z, intensity] arrays
peaks = transition.centroid() # shape (N, 3): [m/z, intensity, 1/K0]
break
Lookups and queries¶
Access frames, precursors, or windows directly by ID or query by properties.
from tdfpy import DDA
with DDA(D_PATH) as dda:
# Access by ID
frame = dda.ms1[1]
precursor = dda.precursors[1]
# Query precursors by m/z and retention time
results = dda.precursors.query(
precursor_mz=1292.63,
mz_tolerance=20.0, # ppm by default
rt=2400.0, # seconds
rt_tolerance=30.0, # seconds
)
for p in results:
print(p.precursor_id, p.largest_peak_mz)
from tdfpy import DIA
with DIA(D_PATH) as dia:
# Get all windows in a window group
group_windows = dia.windows[1] # returns a tuple
# Query windows by retention time
results = dia.windows.query(rt=10.0, rt_tolerance=5.0)
for w in results:
print(w.window_group_id, w.isolation_mz)
How data access works¶
A .d folder contains two files: analysis.tdf (a SQLite database with metadata) and
analysis.tdf_bin (a binary file with the raw spectral data).
When you open a DDA, DIA or PRM reader, it immediately:
- Opens a connection to the binary file
- Reads all frame and precursor metadata from the SQLite database into memory
The objects you get back — Frame, Precursor, DiaWindow, etc. — all hold a reference
to that open connection. Their fields (frame_id, rt, precursor_mz, etc.) are
available immediately. Spectral data is fetched lazily: calling .merged_peaks(), .scan_peaks() or .centroid()
reads from the binary file at that moment.
This means objects cannot be used after the reader closes:
from tdfpy import DDA
with DDA(D_PATH) as dda:
frame = dda.ms1[1]
peaks = frame.centroid() # The connection is open
# peaks = frame.centroid() # ReaderClosedError (a RuntimeError subclass)
# dda.metadata # also ReaderClosedError: read metadata inside the block
Development¶
The project uses uv for dependency management and just as a task runner.
just install-dev # install with dev dependencies
just test # run tests
just lint # ruff linter
just check # lint + test + type check
To serve the docs locally: