Skip to content

Getting started

Installation

pip install tdfpy
# or
uv add tdfpy

Requires Python 3.12+. Optional extras:

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:

  1. Opens a connection to the binary file
  2. 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:

uv run --group docs mkdocs serve