Skip to content

Fix decoding of MS-Numpress encoded spectra - #413

Merged
MKoesters merged 1 commit into
pymzml:devfrom
aaronkanzer:fix/numpress-spectra
Oct 27, 2026
Merged

MKoesters merged 1 commit into
pymzml:devfrom
aaronkanzer:fix/numpress-spectra

Conversation

@aaronkanzer

Copy link
Copy Markdown
Contributor

pymzml 2.7.0 cannot read the m/z or intensity array of any spectrum that uses MS-Numpress.

import pymzml
spec = next(iter(pymzml.run.Reader("small-composite-test.mzML")))  # msconvert output, galaxyp test data
spec.mz
  • MS:1002312 (linear) and MS:1002314 (slof) raise AttributeError: 'Spectrum' object has no attribute 'calling_instance'.
  • MS:1002313 (pic) and the three "followed by zlib compression" terms MS:1002746, MS:1002747 and MS:1002748 raise ValueError: Unsupported data type: None.

Why:

  • _decodeNumpress_to_array still looks up self.calling_instance.OT. calling_instance was removed in 174193e (2023) when Spectrum got its own obo_translator, and nothing sets it anymore.
  • _decode_to_numpy only checks for the linear and slof names, not pic or the "followed by zlib" names. Those terms are all children of MS:1000572 (binary data compression type) in the PSI-MS CV, and msconvert and OpenMS write them.
  • When numpress is used, _get_encoding_parameters returns d_type = None on purpose, but _decode_to_numpy then always reaches raise ValueError(f"Unsupported data type: {data_type}"), even after a successful decode.

#332 looks like the same problem for the "followed by zlib" term.

Fix: map the six numpress CV names (plus the legacy ms-np-* names) to their method, zlib-decompress first when the term says so, and return the decoded float64 array directly. The pure Python MSNumpress fallback is stateful (MSNumpress(bytearray).decode_linear()), so it is called that way when pynumpress is not installed.

Tested:

  • New tests/numpress_spectrum_test.py covers linear, pic, slof and the three zlib variants, with pynumpress and with the Python fallback. It fails on dev (8 subtests) and passes with the fix.
  • Five real numpress files (two msconvert outputs from galaxyp test data and three OpenMS TOPP test files, 42 spectra in all) now give m/z and intensity arrays identical to pyteomics.
  • Full suite: before 135 passed, 2 failed, 3 skipped. After: 137 passed, 2 failed, 3 skipped. The 2 failures are plot_spec_test.py and fail the same way before and after, because plotly is not installed.
  • black --check pymzml is clean (black 26.5.1, as in CI).

Note: the legacy MS_Spectrum class in spec.py keeps its own copy of the old decoding code. Reader does not use it, so this PR leaves it alone.

Spectra whose arrays use any MS-Numpress compression term failed to
decode: linear and slof raised AttributeError on the removed
calling_instance attribute, and pic plus the three 'followed by zlib'
terms (MS:1002746-MS:1002748) were not recognized and raised
ValueError: Unsupported data type: None. Map all six CV terms to their
decoder, zlib-decompress first where the term says so, return the
decoded array directly, and support the pure Python fallback.
@sonarqubecloud

Copy link
Copy Markdown

@MKoesters
MKoesters self-requested a review October 26, 2026 13:31
@MKoesters
MKoesters merged commit 8bd658d into pymzml:dev Oct 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants