Repository navigation
v5.0.0
Breaking API cleanup for 5.0. docs/migration.md has the full old-to-new table.
Removed
plot_centroidingtolerance keywordsmz_tolerance,mz_tolerance_type,im_tolerance,im_tolerance_type,min_peaks,max_peaks(deprecated in 4.1). Passcentroid=MergePeaksCentroider(...).- camelCase names with no alias:
TimsData.indexToMz,mzToIndex,scanNumToOneOverK0,oneOverK0ToScanNum,scanNumToVoltage,voltageToScanNum,readScans;tdfpy.timsdata.oneOverK0ToCCSforMz,ccsToOneOverK0forMz,ccsToOneOverK0ToCCSforMz. tdfpy.centroiding.batch_iterator,calculate_nmass,get_tdf_dfandPeak, unused helpers outside the documented API.DIAMs1Frame.dia_windowsandPRMMs1Frame.prm_transitions, which were always empty. UseDIA.windows/PRM.transitions.
Added
tdfpy.errors:TdfpyError(ValueError)base,TdfpyKeyError(+KeyError),ReaderClosedError(+RuntimeError),AcquisitionTypeError. All exported fromtdfpy.- Opening a
DDA,DIAorPRMreader on another acquisition type raisesAcquisitionTypeError("not a DDA acquisition (found DIA); use tdfpy.get_acquisition_type()"). It used to open and return empty or wrong elements. AcquisitionType(StrEnum),MsMsType,Polarity(aLiteral),MetaValue,ook0_to_ccsandccs_to_ook0exported fromtdfpy.tdfpy.typeswithToleranceUnit = Literal["da", "ppm"]andPolarity = Literal["positive", "negative"], the same names, values and order astacular.types(tdfpy does not depend on tacular). Both exported fromtdfpy; the m/z tolerance parameters are typedToleranceUnit.ms_levelon every frame (1 for MS1, 2 for MS/MS) and onPasefFrameMsmsInfo,DiaWindowandPrmTransition(always 2), next tomsms_type. The window classes carrymsms_typeas a class constant (DDA_MS2,DIA_MS2,PRM_MS2).Precursor.precursor_mz:monoisotopic_mzwhen known, elselargest_peak_mz. The raw fields stay.Ms1FrameLookup.query_range(rt_range=)andquery(rt=, rt_tolerance=30): MS1 frames by retention time.tdfpy.iter_precursor_spectra(precursors): yields(precursor, precursor.merged_peaks()), decoding each PASEF frame once instead of once per precursor window.scan_peaks(),raw_peaks()andcentroid()onPasefFrameMsmsInfo.- Lookups:
ids(),id in lookup, andget(id, default)on every lookup. __all__in every public module;Raises:sections in docstrings.docs/migration.md("Migrating to 5.0").- MCP: library errors (
TdfpyError, missing paths, bad indices, unsupported formats) reach the client as tool errors with their message, e.g. "precursor queries require DDA, but this acquisition is DIA". The SDK used to replace them with a bare "Error executing tool ". - MCP: unknown tool arguments are rejected ("Extra inputs are not permitted"). They used to be dropped silently, so the 4.x
query_frames(rt=...)returned unfiltered rows. tests/test_mcp_vocabulary.py: drift guard for MCP schema names, shared enums, unknown-argument rejection, record keys and error text.
Changed
- Renamed, no alias:
.time->.rt(Frame,PrmTarget);PrmTarget.one_over_k0->ook0;Frame.mz_calibration,tims_calibration,property_group->*_id;Precursor.parent_frame->parent_frame_id;PasefFrameMsmsInfo.precursor->precursor_id;window_group->window_group_idonDiaWindowGroup/DiaWindow;MetaData.one_over_k0_acq_range*->ook0_acq_range*;TimsCalibration.scan_to_one_over_k0/one_over_k0_to_scan->scan_to_ook0/ook0_to_scan;tdfpy.calibration.one_over_k0_to_ccs/ccs_to_one_over_k0->ook0_to_ccs/ccs_to_ook0; camelCaseTimsDatamethods -> snake_case (index_to_mz,mz_to_index,scan_num_to_ook0,ook0_to_scan_num,scan_num_to_voltage,voltage_to_scan_num,read_scans). - Every spectral accessor is a method. The per-scan
.peaksproperty ofFrame,DiaWindowandPrmTransitionis nowscan_peaks().Precursor.peaksandPasefFrameMsmsInfo.peaks(one mobility-collapsed(N, 2)spectrum, the most expensive accessor) are nowmerged_peaks(), andPrecursor.pasef_peaksispasef_merged_peaks(). - Mobility ranges are
(low, high), likeisolation_mz_range,ook0_acq_rangeandquery_range(ook0_range=).ook0_range,ccs_rangeandvoltage_rangeonPasefFrameMsmsInfo,Precursor,DiaWindowandPrmTransitionused to come out high-to-low because scan number and 1/K0 run in opposite directions;*_beginis now the low value and*_endthe high one. FrameMetadata(exported fromtdfpy) renamed its fields to matchFrame:time->rt,mz_calibration->mz_calibration_id,tims_calibration->tims_calibration_id,property_group->property_group_id.TimsData(path, *, use_recalibrated_state=..., pressure_compensation_strategy=...): the options are keyword-only.- After a reader closes,
reader.timsdata,metadata,calibrationandpandas_tdfraiseReaderClosedError, as doPrecursor.ook0/ccsand the mobility ranges.timsdataused to hand back the closed handle andmetadatakept reading the file. AcquisitionType.UNKNOWNis"unknown"(was"Unknown").TimsData.read_scansandread_frame_arraysraiseTdfpyErrorfor a non-integer bound or a range outside0 <= scan_begin <= scan_end <= num_scans; an empty range (begin == end) is valid and reads nothing.read_scansused to pad out-of-range scans with empty arrays andread_frame_arraysclamped them.- Window accessors (
scan_peaks,raw_peaks,centroid,merged_peaks) raiseTdfpyErrorwhen a PASEF/DIA window's scan range does not fit its frame (a corrupt file).scan_peaksused to clamp it silently. - Every error is a
TdfpyError. Lookup misses and missingMetaData/Calibrationkeys raiseTdfpyKeyError; spectral access after close raisesReaderClosedError; SQLite errors inPandasTdf,convert_table_to_dfandslice_d_folderraiseTdfpyError(convert_table_to_dfraisedRuntimeError).PandasTdf.get_table_namesopens the database read-only. get_acquisition_typereturnsAcquisitionType;Frame.msms_typeisMsMsType. Both still compare equal to the oldstr/intvalues.- Elements are frozen, slotted, keyword-only dataclasses.
PrmTargetequality and hashing ignoretransitions. DiaWindowLookup[id]andPrmTransitionLookup[id]return tuples (were lists);DIA.window_groupsreturns a tuple (was a generator).query()/query_range()arguments are keyword-only, andDiaWindowLookup'swindow_group_index=is nowwindow_group=.MetaDataandCalibrationare read-onlyMappings; thedffield is replaced bytable.plot_centroiding: every argument afterframe_idis keyword-only, includingmobility_type.- MCP
query_dia_windows: parameterwindow_group->window_group_id. - MCP
query_framesand the entity tools (query_precursors,query_dia_windows,query_prm_targets,query_prm_transitions): range parameterrt->rt_range;mz->precursor_mz_rangeon the precursor and PRM-target tools andisolation_mz_rangeon the DIA-window and PRM-transition tools. ion_mobility_type->mobility_type, no alias, on every function and method that takes it (get_raw_peaks,get_centroided_spectrum,convert,raw_peaks()/centroid()on every element,iter_window_spectra, theCentroidercall,plot_centroiding), matching mzmlpy's MCPmobility_type. Values are unchanged ("ook0","ccs","voltage").- MCP
Processing.ion_mobility_type->mobility_type(values"ook0","voltage") inpreview_spectrum,export_spectrumandexport_window_batch. - MCP
query_framesrows useFrameattribute names instead of rawFramesSQL columns:Id->frame_id,Time->rt,Polarity"+"/"-"->polarity"positive"/"negative",ScanMode->scan_mode,MsMsType->msms_type,TimsId->tims_id,MaxIntensity->base_peak_intensity,SummedIntensities->total_ion_current,NumScans->num_scans,NumPeaks->num_peaks,MzCalibration->mz_calibration_id,T1/T2->t1/t2,TimsCalibration->tims_calibration_id,PropertyGroup->property_group_id,AccumulationTime->accumulation_time,RampTime->ramp_time; each row addsms_leveland a frameselection. The page is{items, total, offset, next_offset}like the other query tools (was{table, columns, offset, rows, next_offset}).read_metadata_tablestill returns raw columns. - MCP frame metadata in
convert_coordinates(frame) and in spectrum/export metadata (frames) reportspolarityas"positive"/"negative"(was"+"/"-"). - MCP
server_infounits keyretention_time->rt. - Reader vocabulary shared with mzmlpy, renamed with no alias:
mz_range->isolation_mz_rangeonPasefFrameMsmsInfo,DiaWindow,PrmTransitionandPrecursor;mz_begin/mz_endremoved (useisolation_mz_range);PrmTarget.monoisotopic_mz->precursor_mz;Frame.summed_intensities->total_ion_current;Frame.max_intensity->base_peak_intensity. PolarityisLiteral["positive", "negative"], not aStrEnum, and everypolarityfield isPolarity | None. A frame whosePolaritycolumn is not+/-givesNoneand oneUserWarningper file; a precursor whose PASEF frames disagree givesNonewith a warning.FrameMetadata.polaritystays the raw"+"/"-"string.- Lookup keywords (same as mzmlpy 0.10): a
(low, high)tuple is*_range, a point isrt=/precursor_mz=/ook0=plus a tolerance.PrecursorLookup.query_range(mz_range=)andPrmTargetLookup.query_range(mz_range=)->precursor_mz_range=;PrecursorLookup.query(mz=)andPrmTargetLookup.query(mz=)->precursor_mz=. - Tolerance switches follow the tacular-omics naming rule, renamed with no alias:
mz_tolerance_type->mz_tolerance_unitandim_tolerance_type->im_tolerance_unitonmerge_peaks,get_mobility_collapsed_spectrum,MergePeaksCentroider,PrecursorLookup.queryandPrmTargetLookup.query. Values are unchanged ("da"/"ppm"for m/z;"relative"/"absolute"for 1/K0). tdfpy.constants.PROTON_MASSis the full CODATA 2018 value1.007276466621(was truncated to1.007276466), the same astacular.constants.PROTON_MASS.- The
DDA,DIAandPRMreaders load their tables with plain sqlite queries instead of pandasiterrows; SQLite errors there raiseTdfpyError("Failed to read TDF database ..."). - Reading a scan range (
read_frame_arrays,read_scans, every window accessor) decodes TOF indices for those scans only. Whole-frame layout checks still run; the TOF-bounds check now covers the scans read. MergePeaksCentroidersorts peaks on the integer TOF index (a counting sort under numba) before converting to m/z, andmerge_peaks(both kernels) puts its input in one canonical order: m/z, then descending 1/K0 (that is, TOF then scan for frame data), then intensity. Input already in that order, as the reader produces it, skips the sort after an O(n) check; anything else is lexsorted into it. The greedy seed order is a stable sort on descending intensity, so equal-intensity seeds are taken in that canonical order instead of an unstable quicksort order. Results no longer depend on the NumPy version or on input order, including when many points share one m/z; only points identical in m/z, 1/K0 and intensity keep their input order, and those are interchangeable. Measured against 4.x on 12 dense MS1 frames each: 1.43% of centroids differ on a 15-min diaPASEF run (11,919 of 834,389) and 1.16% on a 15-min DDA run (4,278 of 368,896); summed intensity per frame moves by under 0.02%.merged_peaks()output is unchanged.
Performance
Speedups only, indicative, not exclusive timing: single-core direct runs on a shared, loaded machine, 5.0 against commit b783db4 (readers still built with pandas iterrows, as in 4.1).
| workload | speedup |
|---|---|
open DDA, 35-min plasma run (174,658 precursors) |
about 9x |
open DDA, 15-min HeLa run |
about 8x |
open DIA, 15-min run |
about 3.5x |
centroid() on MS1 frames |
about 1.1x |
merged_peaks() over 2000 precursors |
about 1.25x in a loop, about 3.3x with iter_precursor_spectra |
centroid() over 1500 DIA windows |
about 1.9x in a loop; iter_window_spectra itself about 1.8x |