diff --git a/pymzml/spec.py b/pymzml/spec.py index dcd620f8..4ee9b02a 100755 --- a/pymzml/spec.py +++ b/pymzml/spec.py @@ -49,7 +49,14 @@ from struct import unpack import numpy as np - +try: + DECON_DEP = True + from ms_deisotope.deconvolution import deconvolute_peaks + from ms_peak_picker import simple_peak +except ImportError: + DECON_DEP = True + print('[Warning] ms_deisotope is not installed, spectrum deconvolution is not possible.') + print('To enable deconvolution, please use pip install ms_deisotope.') from . import regex_patterns from .decoder import MSDecoder @@ -1018,6 +1025,21 @@ def peaks(self, peak_type): peaks.sort(key=itemgetter(0)) return peaks + def _deconvolute_peaks(self, *args, **kwargs): + if DECON_DEP: + peaks = self.peaks("centroided") + # pack peak matrix into expected structure + peaks = [simple_peak(p[0], p[1], 0.01) for p in peaks] + decon_result = deconvolute_peaks(peaks, *args, **kwargs) + dpeaks = decon_result.peak_set + # pack deconvoluted peak list into matrix structure + dpeaks_mat = np.zeros((len(dpeaks), 3), dtype=float) + for i, dp in enumerate(dpeaks): + dpeaks_mat[i, :] = dp.neutral_mass, dp.intensity, dp.charge + return dpeaks_mat + else: + print('ms_deisotope is missing, please install using pip install ms_deisotope') + def set_peaks(self, peaks, peak_type): """ Assign a custom peak array of type peak_type @@ -1045,6 +1067,10 @@ def set_peaks(self, peaks, peak_type): self._peak_dict["reprofiled"] = peaks except TypeError: self._peak_dict["reprofiled"] = None + elif peak_type == "deconvoluted": + self._peak_dict["deconvoluted"] = peaks + self._mz = self.peaks("raw")[:, 0] + self._i = self.peaks("raw")[:, 1] else: raise Exception( "Peak type is not suppported\n" diff --git a/requirements.txt b/requirements.txt index ecd8c7c4..3dbff1fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ numpy plotly pynumpress >= 0.0.4 regex +ms_deisotope == 0.0.9 tox diff --git a/setup.py b/setup.py index 4e4505d9..cc8fd831 100755 --- a/setup.py +++ b/setup.py @@ -24,15 +24,17 @@ python_requires = '>=3.4.0', install_requires = [ 'numpy >= 1.8.0', - 'regex' + 'regex', ], extras_require = { 'full': [ 'plotly < 2.0', 'pynumpress>=0.0.4', + 'ms_deisotope', ], 'plot': ['plotly < 2.0'], 'pynumpress': ['pynumpress>=0.0.4'], + 'deconvolution': ['ms_deisotope'] }, description = 'high-throughput mzML parsing', long_description = 'pymzML - python module for mzML parsing', diff --git a/tests/ms2_spec_test.py b/tests/ms2_spec_test.py index 08468316..1964bd48 100755 --- a/tests/ms2_spec_test.py +++ b/tests/ms2_spec_test.py @@ -2,9 +2,12 @@ import os sys.path.append(os.path.abspath(".")) +from pymzml.spec import PROTON + import pymzml.run as run import unittest import test_file_paths +import numpy as np class SpectrumMS2Test(unittest.TestCase): @@ -44,6 +47,19 @@ def test_select_precursors(self): selected_precursor, [{"mz": 443.711242675781, "i": 0.0, "charge": 2}] ) + def test_deconvolute_peaks(self): + charge = 3 + test_mz = 430.313 + arr = np.array([(test_mz, 100), (test_mz + PROTON / charge, 49)]) + spec = self.Run[2548] + spec.set_peaks(arr, "centroided") + decon = spec.peaks("deconvoluted") + self.assertEqual(len(decon), 1) + decon_mz = (test_mz * charge) - charge * PROTON + self.assertEqual(decon[0][0], decon_mz) + self.assertEqual(decon[0][1], 149) # 149 since itensities are 100 and 49 + self.assertEqual(decon[0][2], 3) + if __name__ == "__main__": unittest.main(verbosity=3)