CoreMS 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- corems/__init__.py +63 -0
- corems/chroma_peak/__init__.py +0 -0
- corems/chroma_peak/calc/ChromaPeakCalc.py +480 -0
- corems/chroma_peak/calc/__init__.py +0 -0
- corems/chroma_peak/calc/subset.py +196 -0
- corems/chroma_peak/factory/__init__.py +0 -0
- corems/chroma_peak/factory/chroma_peak_classes.py +1178 -0
- corems/encapsulation/__init__.py +0 -0
- corems/encapsulation/constant.py +1283 -0
- corems/encapsulation/factory/__init__.py +0 -0
- corems/encapsulation/factory/parameters.py +392 -0
- corems/encapsulation/factory/processingSetting.py +1244 -0
- corems/encapsulation/input/__init__.py +0 -0
- corems/encapsulation/input/parameter_from_json.py +579 -0
- corems/encapsulation/output/__init__.py +0 -0
- corems/encapsulation/output/parameter_to_dict.py +142 -0
- corems/encapsulation/output/parameter_to_json.py +332 -0
- corems/mass_spectra/__init__.py +0 -0
- corems/mass_spectra/calc/GC_Calc.py +84 -0
- corems/mass_spectra/calc/GC_Deconvolution.py +558 -0
- corems/mass_spectra/calc/GC_RI_Calibration.py +44 -0
- corems/mass_spectra/calc/MZSearch.py +249 -0
- corems/mass_spectra/calc/SignalProcessing.py +656 -0
- corems/mass_spectra/calc/__init__.py +0 -0
- corems/mass_spectra/calc/lc_calc.py +5790 -0
- corems/mass_spectra/calc/lc_calc_operations.py +1127 -0
- corems/mass_spectra/factory/GC_Class.py +791 -0
- corems/mass_spectra/factory/__init__.py +0 -0
- corems/mass_spectra/factory/chromat_data.py +51 -0
- corems/mass_spectra/factory/lc_class.py +2721 -0
- corems/mass_spectra/input/__init__.py +0 -0
- corems/mass_spectra/input/andiNetCDF.py +200 -0
- corems/mass_spectra/input/boosterHDF5.py +216 -0
- corems/mass_spectra/input/brukerSolarix.py +153 -0
- corems/mass_spectra/input/brukerSolarix_utils.py +73 -0
- corems/mass_spectra/input/corems_hdf5.py +1710 -0
- corems/mass_spectra/input/massList.py +133 -0
- corems/mass_spectra/input/mzml.py +668 -0
- corems/mass_spectra/input/parserbase.py +239 -0
- corems/mass_spectra/input/rawFileReader.py +1839 -0
- corems/mass_spectra/output/__init__.py +0 -0
- corems/mass_spectra/output/export.py +2800 -0
- corems/mass_spectrum/__init__.py +0 -0
- corems/mass_spectrum/calc/AutoRecalibration.py +237 -0
- corems/mass_spectrum/calc/Calibration.py +602 -0
- corems/mass_spectrum/calc/CalibrationCalc.py +253 -0
- corems/mass_spectrum/calc/KendrickGroup.py +239 -0
- corems/mass_spectrum/calc/MassErrorPrediction.py +436 -0
- corems/mass_spectrum/calc/MassSpectrumCalc.py +303 -0
- corems/mass_spectrum/calc/MeanResolvingPowerFilter.py +212 -0
- corems/mass_spectrum/calc/NoiseCalc.py +371 -0
- corems/mass_spectrum/calc/NoiseCalc_Bayes.py +93 -0
- corems/mass_spectrum/calc/PeakPicking.py +994 -0
- corems/mass_spectrum/calc/__init__.py +0 -0
- corems/mass_spectrum/factory/MassSpectrumClasses.py +1753 -0
- corems/mass_spectrum/factory/__init__.py +0 -0
- corems/mass_spectrum/input/__init__.py +0 -0
- corems/mass_spectrum/input/baseClass.py +531 -0
- corems/mass_spectrum/input/boosterHDF5.py +161 -0
- corems/mass_spectrum/input/coremsHDF5.py +475 -0
- corems/mass_spectrum/input/massList.py +402 -0
- corems/mass_spectrum/input/numpyArray.py +133 -0
- corems/mass_spectrum/output/__init__.py +0 -0
- corems/mass_spectrum/output/export.py +841 -0
- corems/molecular_formula/__init__.py +0 -0
- corems/molecular_formula/calc/MolecularFormulaCalc.py +885 -0
- corems/molecular_formula/calc/__init__.py +0 -0
- corems/molecular_formula/factory/MolecularFormulaFactory.py +895 -0
- corems/molecular_formula/factory/__init__.py +0 -0
- corems/molecular_formula/input/__init__.py +0 -0
- corems/molecular_formula/input/masslist_ref.py +355 -0
- corems/molecular_id/__init__.py +0 -0
- corems/molecular_id/calc/ClusterFilter.py +251 -0
- corems/molecular_id/calc/MolecularFilter.py +122 -0
- corems/molecular_id/calc/SpectralSimilarity.py +632 -0
- corems/molecular_id/calc/__init__.py +0 -0
- corems/molecular_id/calc/math_distance.py +1637 -0
- corems/molecular_id/data/FAMES_REF.msp +980 -0
- corems/molecular_id/data/PNNLMetV20191015.msp +157267 -0
- corems/molecular_id/factory/EI_SQL.py +650 -0
- corems/molecular_id/factory/MolecularLookupTable.py +914 -0
- corems/molecular_id/factory/__init__.py +0 -0
- corems/molecular_id/factory/classification.py +884 -0
- corems/molecular_id/factory/lipid_molecular_metadata.py +50 -0
- corems/molecular_id/factory/molecularSQL.py +827 -0
- corems/molecular_id/factory/spectrum_search_results.py +119 -0
- corems/molecular_id/input/__init__.py +0 -0
- corems/molecular_id/input/nistMSI.py +148 -0
- corems/molecular_id/search/__init__.py +0 -0
- corems/molecular_id/search/compoundSearch.py +214 -0
- corems/molecular_id/search/database_interfaces.py +1527 -0
- corems/molecular_id/search/findOxygenPeaks.py +330 -0
- corems/molecular_id/search/lcms_spectral_search.py +348 -0
- corems/molecular_id/search/molecularFormulaSearch.py +1117 -0
- corems/molecular_id/search/priorityAssignment.py +723 -0
- corems/ms_peak/__init__.py +0 -0
- corems/ms_peak/calc/MSPeakCalc.py +1010 -0
- corems/ms_peak/calc/__init__.py +0 -0
- corems/ms_peak/factory/MSPeakClasses.py +542 -0
- corems/ms_peak/factory/__init__.py +0 -0
- corems/transient/__init__.py +0 -0
- corems/transient/calc/TransientCalc.py +362 -0
- corems/transient/calc/__init__.py +0 -0
- corems/transient/factory/TransientClasses.py +457 -0
- corems/transient/factory/__init__.py +0 -0
- corems/transient/input/__init__.py +0 -0
- corems/transient/input/brukerSolarix.py +461 -0
- corems/transient/input/midasDatFile.py +172 -0
- corems-4.0.0.dist-info/METADATA +475 -0
- corems-4.0.0.dist-info/RECORD +171 -0
- corems-4.0.0.dist-info/WHEEL +5 -0
- corems-4.0.0.dist-info/licenses/LICENSE +22 -0
- corems-4.0.0.dist-info/top_level.txt +4 -0
- examples/archive/scripts/CoreMS_tutorial.py +94 -0
- examples/archive/scripts/DI HR-MS Halogens Bruker.py +196 -0
- examples/archive/scripts/DI HR-MS MassList.py +385 -0
- examples/archive/scripts/GC-MS MetabRef.py +213 -0
- examples/archive/scripts/GC-MS NetCDF.py +217 -0
- examples/archive/scripts/HR-MS Thermo Raw 21T.py +136 -0
- examples/archive/scripts/LC-ICPMS_metal_peaks.py +297 -0
- examples/archive/scripts/LCMS-Thermo.py +460 -0
- examples/archive/scripts/LCMS_isotopes.py +283 -0
- examples/archive/scripts/MSParams_example.py +21 -0
- examples/archive/scripts/Molecular Formula Data Aggreation.py +84 -0
- examples/archive/scripts/Single Mz Search.py +69 -0
- examples/test_notebooks.py +145 -0
- ext_lib/ChemstationMSFileReader.dll +0 -0
- ext_lib/ChemstationMSFileReader.xml +126 -0
- ext_lib/RawFileReaderLicense.doc +0 -0
- ext_lib/ThermoFisher.CommonCore.BackgroundSubtraction.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.BackgroundSubtraction.xml +2307 -0
- ext_lib/ThermoFisher.CommonCore.Data.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.Data.xml +28974 -0
- ext_lib/ThermoFisher.CommonCore.MassPrecisionEstimator.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.MassPrecisionEstimator.xml +241 -0
- ext_lib/ThermoFisher.CommonCore.RawFileReader.dll +0 -0
- ext_lib/ThermoFisher.CommonCore.RawFileReader.xml +31174 -0
- ext_lib/__init__.py +0 -0
- ext_lib/dotnet/OpenMcdf.Extensions.dll +0 -0
- ext_lib/dotnet/OpenMcdf.dll +0 -0
- ext_lib/dotnet/OpenMcdf.xml +1154 -0
- ext_lib/dotnet/System.IO.FileSystem.AccessControl.dll +0 -0
- ext_lib/dotnet/System.IO.FileSystem.AccessControl.xml +506 -0
- ext_lib/dotnet/System.Security.AccessControl.dll +0 -0
- ext_lib/dotnet/System.Security.AccessControl.xml +2043 -0
- ext_lib/dotnet/System.Security.Principal.Windows.dll +0 -0
- ext_lib/dotnet/System.Security.Principal.Windows.xml +1011 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.BackgroundSubtraction.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.BackgroundSubtraction.xml +2307 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.Data.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.Data.xml +29148 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.MassPrecisionEstimator.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.MassPrecisionEstimator.xml +241 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.RawFileReader.dll +0 -0
- ext_lib/dotnet/ThermoFisher.CommonCore.RawFileReader.xml +31492 -0
- ext_lib/version +18 -0
- support_code/atom_parsers/AtomsDescription.py +359 -0
- support_code/atom_parsers/CreateAtomsDescription.py +56 -0
- support_code/nmdc/filefinder.py +90 -0
- support_code/nmdc/lipidomics/lipidomics_workflow.py +748 -0
- support_code/nmdc/lipidomics/manifest_examples.py +49 -0
- support_code/nmdc/metabolomics/gcms_workflow.py +158 -0
- support_code/nmdc/metabolomics/lcms_metabolomics_targeted_search.py +59 -0
- support_code/nmdc/metabolomics/lcms_metabolomics_workflow.py +248 -0
- support_code/nmdc/metabolomics/metabolomics_collection.py +628 -0
- support_code/nmdc/metadata/dms_api.py +42 -0
- support_code/nmdc/nom/archived_scripts/nmdc_metadata_gen.py +288 -0
- support_code/nmdc/nom/archived_scripts/nom_grow_workflow.py +209 -0
- support_code/nmdc/nom/nom_workflow.py +312 -0
- support_code/windows_only_importers/BrukerCompassXtract.py +180 -0
- support_code/windows_only_importers/ThermoMSFileReader.py +405 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
__author__ = "Yuri E. Corilo"
|
|
2
|
+
__date__ = "Jul 31, 2019"
|
|
3
|
+
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from threading import Thread
|
|
6
|
+
|
|
7
|
+
from numpy import average, std
|
|
8
|
+
|
|
9
|
+
from corems.molecular_id.calc.ClusterFilter import ClusteringFilter
|
|
10
|
+
from corems.molecular_id.factory.molecularSQL import MolForm_SQL
|
|
11
|
+
from corems.molecular_id.search.molecularFormulaSearch import SearchMolecularFormulas
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FindOxygenPeaks(Thread):
|
|
15
|
+
"""Class to find Oxygen peaks in a mass spectrum for formula assignment search
|
|
16
|
+
|
|
17
|
+
Class to walk 14Da units over oxygen space for negative ion mass spectrum of natural organic matter
|
|
18
|
+
Returns a list of MSPeak class containing the possible Molecular Formula class objects.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
mass_spectrum_obj : MassSpec class
|
|
23
|
+
This is where we store MassSpec class obj,
|
|
24
|
+
|
|
25
|
+
lookupTableSettings: MolecularLookupTableSettings class
|
|
26
|
+
This is where we store MolecularLookupTableSettings class obj
|
|
27
|
+
|
|
28
|
+
min_O , max_O : int
|
|
29
|
+
minium and maximum of Oxygen to allow the software to look for
|
|
30
|
+
it will override the settings at lookupTableSettings.usedAtoms
|
|
31
|
+
default min = 1, max = 22
|
|
32
|
+
|
|
33
|
+
Attributes
|
|
34
|
+
----------
|
|
35
|
+
mass_spectrum_obj : MassSpec class
|
|
36
|
+
This is where we store MassSpec class obj,
|
|
37
|
+
lookupTableSettings: MolecularLookupTableSettings class
|
|
38
|
+
This is where we store MolecularLookupTableSettings class obj
|
|
39
|
+
|
|
40
|
+
Methods
|
|
41
|
+
----------
|
|
42
|
+
* run().
|
|
43
|
+
will be called when the instantiated class method start is called
|
|
44
|
+
* get_list_found_peaks().
|
|
45
|
+
returns a list of MSpeaks classes cotaining all the MolecularFormula candidates inside the MSPeak
|
|
46
|
+
for more details of the structure see MSPeak class and MolecularFormula class
|
|
47
|
+
* set_mass_spec_indexes_by_found_peaks().
|
|
48
|
+
set the mass spectrum to interate over only the selected indexes
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self, mass_spectrum_obj, sql_db: bool = False, min_O: int = 1, max_O: int = 22
|
|
53
|
+
):
|
|
54
|
+
Thread.__init__(self)
|
|
55
|
+
|
|
56
|
+
self.mass_spectrum_obj = mass_spectrum_obj
|
|
57
|
+
self.min_0 = min_O
|
|
58
|
+
self.max_O = max_O
|
|
59
|
+
|
|
60
|
+
if not sql_db:
|
|
61
|
+
self.sql_db = MolForm_SQL(
|
|
62
|
+
mass_spectrum_obj.molecular_search_settings.url_database
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
self.sql_db = sql_db
|
|
66
|
+
|
|
67
|
+
def run(self):
|
|
68
|
+
"""Run the thread"""
|
|
69
|
+
# save initial settings min peaks per class filter
|
|
70
|
+
initial_min_peak_bool = deepcopy(
|
|
71
|
+
self.mass_spectrum_obj.molecular_search_settings.use_min_peaks_filter
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# deactivate the usage of min peaks per class filter
|
|
75
|
+
self.mass_spectrum_obj.molecular_search_settings.use_min_peaks_filter = False
|
|
76
|
+
|
|
77
|
+
# save initial settings for Ox
|
|
78
|
+
initial_ox = deepcopy(
|
|
79
|
+
self.mass_spectrum_obj.molecular_search_settings.usedAtoms["O"]
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# resets the used atoms to look only for oxygen organic compounds
|
|
83
|
+
self.mass_spectrum_obj.molecular_search_settings.usedAtoms["O"] = (
|
|
84
|
+
self.min_0,
|
|
85
|
+
self.max_O,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
self.list_found_mspeaks = []
|
|
89
|
+
|
|
90
|
+
kmd_base = self.mass_spectrum_obj.mspeaks_settings.kendrick_base
|
|
91
|
+
|
|
92
|
+
self.mass_spectrum_obj.change_kendrick_base_all_mspeaks(kmd_base)
|
|
93
|
+
|
|
94
|
+
# needs to be wrapped inside the mass_spec class
|
|
95
|
+
ClusteringFilter().filter_kendrick(self.mass_spectrum_obj)
|
|
96
|
+
|
|
97
|
+
if self.mass_spectrum_obj.parameters.mass_spectrum.verbose_processing:
|
|
98
|
+
print("Start most abundant mass spectral peak search")
|
|
99
|
+
molecular_formula_obj_reference = self.find_most_abundant_formula(
|
|
100
|
+
self.mass_spectrum_obj
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if self.mass_spectrum_obj.parameters.mass_spectrum.verbose_processing:
|
|
104
|
+
print(
|
|
105
|
+
"Select most abundant peak with molecular formula = %s with a m/z error of %s ppm"
|
|
106
|
+
% (
|
|
107
|
+
molecular_formula_obj_reference.string,
|
|
108
|
+
molecular_formula_obj_reference.mz_error,
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
print("Started mass spectral peak series search")
|
|
112
|
+
|
|
113
|
+
self.list_found_mspeaks = self.find_series_mspeaks(
|
|
114
|
+
self.mass_spectrum_obj, molecular_formula_obj_reference, deltamz=14
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# reset indexes after done with operation that includes a filter (i.e. ClusteringFilter().filter_kendrick())
|
|
118
|
+
|
|
119
|
+
self.mass_spectrum_obj.molecular_search_settings.usedAtoms["O"] = initial_ox
|
|
120
|
+
|
|
121
|
+
self.mass_spectrum_obj.molecular_search_settings.use_min_peaks_filter = (
|
|
122
|
+
initial_min_peak_bool
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
self.mass_spectrum_obj.reset_indexes()
|
|
126
|
+
|
|
127
|
+
self.mass_spectrum_obj.filter_by_noise_threshold()
|
|
128
|
+
if self.mass_spectrum_obj.parameters.mass_spectrum.verbose_processing:
|
|
129
|
+
print("Done with mass spectral peak series search")
|
|
130
|
+
|
|
131
|
+
self.sql_db.close()
|
|
132
|
+
|
|
133
|
+
def find_most_abundant_formula(self, mass_spectrum_obj):
|
|
134
|
+
"""Find the most abundant formula in the mass spectrum
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
mass_spectrum_obj : MassSpec class
|
|
139
|
+
Mass spectrum object
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
----------
|
|
143
|
+
MolecularFormula class obj
|
|
144
|
+
most abundant MolecularFormula with the lowest mass error
|
|
145
|
+
"""
|
|
146
|
+
# need to find a better way to cut off outliners
|
|
147
|
+
# import matplotlib.pyplot as plt
|
|
148
|
+
# plt.hist(mass_spectrum_obj.abundance, bins=100)
|
|
149
|
+
# plt.show()
|
|
150
|
+
|
|
151
|
+
abundances = mass_spectrum_obj.abundance
|
|
152
|
+
abun_mean = average(abundances, axis=0)
|
|
153
|
+
abun_std = std(abundances, axis=0)
|
|
154
|
+
|
|
155
|
+
upper_limit = abun_mean + 7 * abun_std
|
|
156
|
+
if mass_spectrum_obj.parameters.mass_spectrum.verbose_processing:
|
|
157
|
+
print(
|
|
158
|
+
"Maximum abundance limit = %s and max abundance kendrick cluster = %s"
|
|
159
|
+
% (
|
|
160
|
+
upper_limit,
|
|
161
|
+
max(mass_spectrum_obj, key=lambda m: m.abundance).abundance,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
mspeak_most_abundant = max(
|
|
166
|
+
mass_spectrum_obj,
|
|
167
|
+
key=lambda m: m.abundance if m.abundance <= upper_limit else 0,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
print("Searching molecular formulas")
|
|
171
|
+
|
|
172
|
+
SearchMolecularFormulas(mass_spectrum_obj, self.sql_db).run_worker_ms_peaks(
|
|
173
|
+
[mspeak_most_abundant]
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
print("Finished searching molecular formulas")
|
|
177
|
+
|
|
178
|
+
if mspeak_most_abundant:
|
|
179
|
+
return mspeak_most_abundant.best_molecular_formula_candidate
|
|
180
|
+
|
|
181
|
+
else:
|
|
182
|
+
raise Exception(
|
|
183
|
+
"Could not find a possible molecular formula match for the most abundant peak of m/z %.5f"
|
|
184
|
+
% mspeak_most_abundant.mz_exp
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# return the first option
|
|
188
|
+
# return mspeak_most_abundant[0]
|
|
189
|
+
|
|
190
|
+
def find_most_abundant_formula_test(self, mass_spectrum_obj, settings):
|
|
191
|
+
"""[Test function] Find the most abundant formula in the mass spectrum
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
mass_spectrum_obj : MassSpec class
|
|
196
|
+
Mass spectrum object
|
|
197
|
+
settings : MolecularSearchSettings class
|
|
198
|
+
Molecular search settings object
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
----------
|
|
202
|
+
MolecularFormula class obj
|
|
203
|
+
most abundant MolecularFormula with the lowest mass error
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
# this function is intended for test only.
|
|
207
|
+
# Have to sort by Kendrick to be able to select the most abundant series
|
|
208
|
+
# then select the most abundant peak inside the series
|
|
209
|
+
# or have the user select the reference mspeak on the gui
|
|
210
|
+
|
|
211
|
+
mspeak_most_abundant = mass_spectrum_obj.most_abundant_mspeak
|
|
212
|
+
|
|
213
|
+
SearchMolecularFormulas(mass_spectrum_obj, self.sql_db).run_worker_ms_peaks(
|
|
214
|
+
[mspeak_most_abundant]
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if mspeak_most_abundant:
|
|
218
|
+
return mspeak_most_abundant.best_molecular_formula_candidate
|
|
219
|
+
|
|
220
|
+
else:
|
|
221
|
+
raise Exception(
|
|
222
|
+
"Could not find a possible molecular formula match for the most abundant peak of m/z %.5f"
|
|
223
|
+
% mspeak_most_abundant.mz_exp
|
|
224
|
+
)
|
|
225
|
+
# return the first option
|
|
226
|
+
# return mspeak_most_abundant[0]
|
|
227
|
+
|
|
228
|
+
def find_series_mspeaks(
|
|
229
|
+
self, mass_spectrum_obj, molecular_formula_obj_reference, deltamz=14
|
|
230
|
+
):
|
|
231
|
+
"""Find a series of abundant peaks in the mass spectrum for a given molecular formula
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
mass_spectrum_obj : MassSpec class
|
|
236
|
+
Mass spectrum object
|
|
237
|
+
molecular_formula_obj_reference : MolecularFormula class
|
|
238
|
+
Molecular formula object
|
|
239
|
+
deltamz : float
|
|
240
|
+
delta m/z to look for peaks
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
----------
|
|
244
|
+
list
|
|
245
|
+
list of MSpeak class objects
|
|
246
|
+
"""
|
|
247
|
+
abundances = mass_spectrum_obj.abundance
|
|
248
|
+
abun_mean = average(abundances, axis=0)
|
|
249
|
+
abun_std = std(abundances, axis=0)
|
|
250
|
+
upper_limit = abun_mean + 7 * abun_std
|
|
251
|
+
|
|
252
|
+
list_most_abundant_peaks = list()
|
|
253
|
+
|
|
254
|
+
min_mz = mass_spectrum_obj.min_mz_exp
|
|
255
|
+
|
|
256
|
+
max_mz = mass_spectrum_obj.max_mz_exp
|
|
257
|
+
|
|
258
|
+
initial_nominal_mass = molecular_formula_obj_reference.mz_nominal_calc
|
|
259
|
+
|
|
260
|
+
mass = initial_nominal_mass
|
|
261
|
+
|
|
262
|
+
nominal_masses = []
|
|
263
|
+
while mass <= max_mz:
|
|
264
|
+
# print "shit 1", mass, min_mz
|
|
265
|
+
mass += deltamz
|
|
266
|
+
nominal_masses.append(mass)
|
|
267
|
+
|
|
268
|
+
mass = initial_nominal_mass
|
|
269
|
+
while mass >= min_mz:
|
|
270
|
+
# print "shit 1", mass, min_mz
|
|
271
|
+
mass -= deltamz
|
|
272
|
+
nominal_masses.append(mass)
|
|
273
|
+
|
|
274
|
+
nominal_masses = sorted(nominal_masses)
|
|
275
|
+
|
|
276
|
+
for nominal_mass in nominal_masses:
|
|
277
|
+
first_index, last_index = (
|
|
278
|
+
mass_spectrum_obj.get_nominal_mz_first_last_indexes(nominal_mass)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
ms_peaks = mass_spectrum_obj[first_index:last_index]
|
|
282
|
+
|
|
283
|
+
if ms_peaks:
|
|
284
|
+
#
|
|
285
|
+
# print (nominal_mass, first_index,
|
|
286
|
+
# last_index,
|
|
287
|
+
# mass_spectrum_obj[first_index].mz_exp,
|
|
288
|
+
# mass_spectrum_obj[last_index].mz_exp
|
|
289
|
+
# )
|
|
290
|
+
#
|
|
291
|
+
|
|
292
|
+
mspeak_most_abundant = max(
|
|
293
|
+
ms_peaks,
|
|
294
|
+
key=lambda m: m.abundance if m.abundance <= upper_limit else 0,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# mspeak_most_abundant = max(ms_peaks, key=lambda m: m.abundance)
|
|
298
|
+
|
|
299
|
+
list_most_abundant_peaks.append(mspeak_most_abundant)
|
|
300
|
+
if mass_spectrum_obj.parameters.mass_spectrum.verbose_processing:
|
|
301
|
+
print("Start molecular formula search")
|
|
302
|
+
SearchMolecularFormulas(mass_spectrum_obj, self.sql_db).run_worker_ms_peaks(
|
|
303
|
+
list_most_abundant_peaks
|
|
304
|
+
)
|
|
305
|
+
if mass_spectrum_obj.parameters.mass_spectrum.verbose_processing:
|
|
306
|
+
print("Done molecular formula search")
|
|
307
|
+
return [mspeak for mspeak in list_most_abundant_peaks if mspeak]
|
|
308
|
+
|
|
309
|
+
def get_list_found_peaks(self):
|
|
310
|
+
"""Get the list of found peaks
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
----------
|
|
314
|
+
list
|
|
315
|
+
list of MSpeak class objects
|
|
316
|
+
"""
|
|
317
|
+
return sorted(self.list_found_mspeaks, key=lambda mp: mp.mz_exp)
|
|
318
|
+
|
|
319
|
+
def set_mass_spec_indexes_by_found_peaks(self):
|
|
320
|
+
"""Set the mass spectrum to interate over only the selected indexes.
|
|
321
|
+
|
|
322
|
+
Notes
|
|
323
|
+
----------
|
|
324
|
+
Warning!!!!
|
|
325
|
+
set the mass spectrum to interate over only the selected indexes
|
|
326
|
+
don not forget to call mass_spectrum_obj.reset_indexes after the job is done
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
indexes = [msp.index for msp in self.list_found_mspeaks]
|
|
330
|
+
self.mass_spectrum_obj.set_indexes(indexes)
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from corems.molecular_id.factory.spectrum_search_results import SpectrumSearchResults
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LCMSSpectralSearch:
|
|
9
|
+
"""
|
|
10
|
+
Methods for searching LCMS spectra.
|
|
11
|
+
|
|
12
|
+
This class is designed to be a mixin class for the :obj:`~corems.mass_spectra.factory.lc_class.LCMSBase` class.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def get_more_match_quals(
|
|
18
|
+
query_mz_arr, lib_entry, mz_tol_da=0.1, include_fragment_types=False
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Return additional match qualities between query and library entry.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
query_mz_arr : np.array
|
|
26
|
+
Array of query spectrum. Shape (N, 2), with m/z in the first column
|
|
27
|
+
and abundance in the second.
|
|
28
|
+
lib_entry : dict
|
|
29
|
+
Library spectrum entry, with 'mz' key containing the spectrum in
|
|
30
|
+
the format (mz, abundance),(mz, abundance), i.e. from MetabRef.
|
|
31
|
+
mz_tol_da : float, optional
|
|
32
|
+
Tolerance in Da for matching peaks (in MS2). Default is 0.1.
|
|
33
|
+
include_fragment_types : bool, optional
|
|
34
|
+
If True, include fragment type comparisons in output.
|
|
35
|
+
Defaults to False.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
tuple
|
|
40
|
+
Tuple of (query_in_lib, query_in_lib_fract, lib_in_query, lib_in_query_fract, query_frags, lib_frags, lib_precursor_mz).
|
|
41
|
+
|
|
42
|
+
Notes
|
|
43
|
+
-----
|
|
44
|
+
query_in_lib : int
|
|
45
|
+
Number of peaks in query that are present in the library entry (within mz_tol_da).
|
|
46
|
+
query_in_lib_fract : float
|
|
47
|
+
Fraction of peaks in query that are present in the library entry (within mz_tol_da).
|
|
48
|
+
lib_in_query : int
|
|
49
|
+
Number of peaks in the library entry that are present in the query (within mz_tol_da).
|
|
50
|
+
lib_in_query_fract : float
|
|
51
|
+
Fraction of peaks in the library entry that are present in the query (within mz_tol_da).
|
|
52
|
+
query_frags : list
|
|
53
|
+
List of unique fragment types present in the query, generally 'MLF' or 'LSF' or both.
|
|
54
|
+
lib_frags : list
|
|
55
|
+
List of unique fragment types present in the library entry, generally 'MLF' or 'LSF' or both.
|
|
56
|
+
|
|
57
|
+
Raises
|
|
58
|
+
------
|
|
59
|
+
ValueError
|
|
60
|
+
If library entry does not have 'fragment_types' key and include_fragment_types is True.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
if "mz" in lib_entry.keys():
|
|
65
|
+
# Get the original mz values from the library entry
|
|
66
|
+
lib_mzs = np.array(
|
|
67
|
+
re.findall(r"\(([^,]+),([^)]+)\)", lib_entry["mz"]), dtype=float
|
|
68
|
+
).reshape(-1, 2)[:, 0]
|
|
69
|
+
elif "peaks" in lib_entry.keys() and lib_entry["peaks"] is not None:
|
|
70
|
+
lib_mzs = lib_entry["peaks"][:, 0]
|
|
71
|
+
|
|
72
|
+
# Get count and fraction of peaks in query that are in lib entry
|
|
73
|
+
query_in_lib = 0
|
|
74
|
+
for peak in query_mz_arr:
|
|
75
|
+
if np.any(np.isclose(lib_mzs, peak, atol=mz_tol_da)):
|
|
76
|
+
query_in_lib += 1
|
|
77
|
+
query_in_lib_fract = query_in_lib / len(query_mz_arr)
|
|
78
|
+
|
|
79
|
+
# Get count and fraction of peaks in lib that are in query
|
|
80
|
+
lib_in_query = 0
|
|
81
|
+
for peak in lib_mzs:
|
|
82
|
+
if np.any(np.isclose(query_mz_arr, peak, atol=mz_tol_da)):
|
|
83
|
+
lib_in_query += 1
|
|
84
|
+
lib_in_query_fract = lib_in_query / len(lib_mzs)
|
|
85
|
+
|
|
86
|
+
if include_fragment_types:
|
|
87
|
+
# Check that fragment types are present in the library entry
|
|
88
|
+
if "fragment_types" not in lib_entry.keys():
|
|
89
|
+
raise ValueError(
|
|
90
|
+
"Flash entropy library entry must have 'fragment_types' key to include fragment types in output."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Get types of fragments in the lib entry and convert it to a list on ", "
|
|
94
|
+
lib_frags = [x.strip() for x in lib_entry["fragment_types"].split(",")]
|
|
95
|
+
# make list of the fragment types that are present in the query spectrum
|
|
96
|
+
lib_in_query_ids = list(
|
|
97
|
+
set(
|
|
98
|
+
[
|
|
99
|
+
ind
|
|
100
|
+
for ind, x in enumerate(lib_mzs)
|
|
101
|
+
if len(np.where(np.isclose(query_mz_arr, x, atol=mz_tol_da))[0])
|
|
102
|
+
> 0
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
query_frags = list(set([lib_frags[x] for x in lib_in_query_ids]))
|
|
107
|
+
lib_frags = list(set(lib_frags))
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
query_frags = None
|
|
111
|
+
lib_frags = None
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
query_in_lib,
|
|
115
|
+
query_in_lib_fract,
|
|
116
|
+
lib_in_query,
|
|
117
|
+
lib_in_query_fract,
|
|
118
|
+
query_frags,
|
|
119
|
+
lib_frags,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def fe_search(
|
|
123
|
+
self,
|
|
124
|
+
scan_list,
|
|
125
|
+
fe_lib,
|
|
126
|
+
precursor_mz_list=[],
|
|
127
|
+
use_mass_features=True,
|
|
128
|
+
peak_sep_da=0.01,
|
|
129
|
+
get_additional_metrics=True,
|
|
130
|
+
accumulate_results=False,
|
|
131
|
+
):
|
|
132
|
+
"""
|
|
133
|
+
Search LCMS spectra using a FlashEntropy approach.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
scan_list : list
|
|
138
|
+
List of scan numbers to search.
|
|
139
|
+
fe_lib : :obj:`~ms_entropy.FlashEntropySearch`
|
|
140
|
+
FlashEntropy Search instance.
|
|
141
|
+
precursor_mz_list : list, optional
|
|
142
|
+
List of precursor m/z values to search, by default [], which implies
|
|
143
|
+
matched with mass features; to enable this use_mass_features must be True.
|
|
144
|
+
use_mass_features : bool, optional
|
|
145
|
+
If True, use mass features to get precursor m/z values, by default True.
|
|
146
|
+
If True, will add search results to mass features' ms2_similarity_results attribute.
|
|
147
|
+
peak_sep_da : float, optional
|
|
148
|
+
Minimum separation between m/z peaks spectra in Da. This needs match the
|
|
149
|
+
approximate resolution of the search spectra and the FlashEntropySearch
|
|
150
|
+
instance, by default 0.01.
|
|
151
|
+
get_additional_metrics : bool, optional
|
|
152
|
+
If True, get additional metrics from FlashEntropy search, by default True.
|
|
153
|
+
accumulate_results : bool, optional
|
|
154
|
+
If True, accumulate results with existing spectral_search_results instead of
|
|
155
|
+
replacing them. This allows searching the same scans with multiple libraries
|
|
156
|
+
without overwriting previous results, by default False.
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
None, but adds results to self.spectral_search_results and associates these
|
|
161
|
+
spectral_search_results with mass_features within the self.mass_features dictionary.
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
# Retrieve parameters from self
|
|
165
|
+
# include_fragment_types should used for lipids queries only, not general metabolomics
|
|
166
|
+
include_fragment_types = self.parameters.lc_ms.include_fragment_types
|
|
167
|
+
min_match_score = self.parameters.lc_ms.ms2_min_fe_score
|
|
168
|
+
|
|
169
|
+
# If precursor_mz_list is empty and use_mass_features is True, get precursor m/z values from mass features for each scan in scan_list
|
|
170
|
+
if use_mass_features and len(precursor_mz_list) == 0:
|
|
171
|
+
precursor_mz_list = []
|
|
172
|
+
for scan in scan_list:
|
|
173
|
+
mf_ids = [
|
|
174
|
+
key
|
|
175
|
+
for key, value in self.mass_features.items()
|
|
176
|
+
if scan in value.ms2_mass_spectra
|
|
177
|
+
]
|
|
178
|
+
precursor_mz = [
|
|
179
|
+
value.mz
|
|
180
|
+
for key, value in self.mass_features.items()
|
|
181
|
+
if key in mf_ids
|
|
182
|
+
]
|
|
183
|
+
precursor_mz_list.append(precursor_mz)
|
|
184
|
+
|
|
185
|
+
# Check that precursor_mz_list same length as scan_list, if not, raise error
|
|
186
|
+
if len(precursor_mz_list) != len(scan_list):
|
|
187
|
+
raise ValueError("Length of precursor_mz_list is not equal to scan_list.")
|
|
188
|
+
|
|
189
|
+
# Loop through each query spectrum / precursor match and save ids of db spectrum that are decent matches
|
|
190
|
+
overall_results_dict = {}
|
|
191
|
+
for i in np.arange(len(scan_list)):
|
|
192
|
+
scan_oi = scan_list[i]
|
|
193
|
+
if len(self._ms[scan_oi].mspeaks) > 0:
|
|
194
|
+
precursor_mzs = precursor_mz_list[i]
|
|
195
|
+
overall_results_dict[scan_oi] = {}
|
|
196
|
+
for precursor_mz in precursor_mzs:
|
|
197
|
+
query_spectrum = fe_lib.clean_spectrum_for_search(
|
|
198
|
+
precursor_mz=precursor_mz,
|
|
199
|
+
peaks=np.vstack(
|
|
200
|
+
(self._ms[scan_oi].mz_exp, self._ms[scan_oi].abundance)
|
|
201
|
+
).T,
|
|
202
|
+
precursor_ions_removal_da=None,
|
|
203
|
+
noise_threshold=self._ms[
|
|
204
|
+
scan_oi
|
|
205
|
+
].parameters.mass_spectrum.noise_threshold_min_relative_abundance
|
|
206
|
+
/ 100,
|
|
207
|
+
min_ms2_difference_in_da=peak_sep_da,
|
|
208
|
+
)
|
|
209
|
+
search_results = fe_lib.search(
|
|
210
|
+
precursor_mz=precursor_mz,
|
|
211
|
+
peaks=query_spectrum,
|
|
212
|
+
ms1_tolerance_in_da=self.parameters.mass_spectrum[
|
|
213
|
+
"ms1"
|
|
214
|
+
].molecular_search.max_ppm_error
|
|
215
|
+
* 10**-6
|
|
216
|
+
* precursor_mz,
|
|
217
|
+
ms2_tolerance_in_da=peak_sep_da * 0.5,
|
|
218
|
+
method={"identity"},
|
|
219
|
+
precursor_ions_removal_da=None,
|
|
220
|
+
noise_threshold=self._ms[
|
|
221
|
+
scan_oi
|
|
222
|
+
].parameters.mass_spectrum.noise_threshold_min_relative_abundance
|
|
223
|
+
/ 100,
|
|
224
|
+
target="cpu",
|
|
225
|
+
)["identity_search"]
|
|
226
|
+
match_inds = np.where(search_results > min_match_score)[0]
|
|
227
|
+
|
|
228
|
+
# If any decent matches are found, add them to the results dictionary
|
|
229
|
+
if len(match_inds) > 0:
|
|
230
|
+
match_scores = search_results[match_inds]
|
|
231
|
+
ref_ms_ids = [fe_lib[x]["id"] for x in match_inds]
|
|
232
|
+
ref_mol_ids = [
|
|
233
|
+
fe_lib[x]["molecular_data_id"] for x in match_inds
|
|
234
|
+
]
|
|
235
|
+
ref_precursor_mzs = [
|
|
236
|
+
fe_lib[x]["precursor_mz"] for x in match_inds
|
|
237
|
+
]
|
|
238
|
+
ion_types = [fe_lib[x]["ion_type"] for x in match_inds]
|
|
239
|
+
overall_results_dict[scan_oi][precursor_mz] = {
|
|
240
|
+
"ref_mol_id": ref_mol_ids,
|
|
241
|
+
"ref_ms_id": ref_ms_ids,
|
|
242
|
+
"ref_precursor_mz": ref_precursor_mzs,
|
|
243
|
+
"precursor_mz_error_ppm": [
|
|
244
|
+
(precursor_mz - x) / precursor_mz * 10**6
|
|
245
|
+
for x in ref_precursor_mzs
|
|
246
|
+
],
|
|
247
|
+
"entropy_similarity": match_scores,
|
|
248
|
+
"ref_ion_type": ion_types,
|
|
249
|
+
}
|
|
250
|
+
# Add database name, if present
|
|
251
|
+
db_name = [
|
|
252
|
+
fe_lib[x].get("database_name") for x in match_inds
|
|
253
|
+
]
|
|
254
|
+
if db_name is not None:
|
|
255
|
+
overall_results_dict[scan_oi][precursor_mz].update(
|
|
256
|
+
{"database_name": db_name}
|
|
257
|
+
)
|
|
258
|
+
if get_additional_metrics:
|
|
259
|
+
more_match_quals = [
|
|
260
|
+
self.get_more_match_quals(
|
|
261
|
+
self._ms[scan_oi].mz_exp,
|
|
262
|
+
fe_lib[x],
|
|
263
|
+
mz_tol_da=peak_sep_da,
|
|
264
|
+
include_fragment_types=include_fragment_types,
|
|
265
|
+
)
|
|
266
|
+
for x in match_inds
|
|
267
|
+
]
|
|
268
|
+
overall_results_dict[scan_oi][precursor_mz].update(
|
|
269
|
+
{
|
|
270
|
+
"query_mz_in_ref_n": [
|
|
271
|
+
x[0] for x in more_match_quals
|
|
272
|
+
],
|
|
273
|
+
"query_mz_in_ref_fract": [
|
|
274
|
+
x[1] for x in more_match_quals
|
|
275
|
+
],
|
|
276
|
+
"ref_mz_in_query_n": [
|
|
277
|
+
x[2] for x in more_match_quals
|
|
278
|
+
],
|
|
279
|
+
"ref_mz_in_query_fract": [
|
|
280
|
+
x[3] for x in more_match_quals
|
|
281
|
+
],
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
if include_fragment_types:
|
|
285
|
+
overall_results_dict[scan_oi][precursor_mz].update(
|
|
286
|
+
{
|
|
287
|
+
"query_frag_types": [
|
|
288
|
+
x[4] for x in more_match_quals
|
|
289
|
+
],
|
|
290
|
+
"ref_frag_types": [
|
|
291
|
+
x[5] for x in more_match_quals
|
|
292
|
+
],
|
|
293
|
+
}
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Drop scans with no results from dictionary
|
|
297
|
+
overall_results_dict = {k: v for k, v in overall_results_dict.items() if v}
|
|
298
|
+
|
|
299
|
+
# Cast each entry as a MS2SearchResults object
|
|
300
|
+
for scan_id in overall_results_dict.keys():
|
|
301
|
+
for precursor_mz in overall_results_dict[scan_id].keys():
|
|
302
|
+
ms2_spectrum = self._ms[scan_id]
|
|
303
|
+
ms2_search_results = overall_results_dict[scan_id][precursor_mz]
|
|
304
|
+
overall_results_dict[scan_id][precursor_mz] = SpectrumSearchResults(
|
|
305
|
+
ms2_spectrum, precursor_mz, ms2_search_results
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Add MS2SearchResults to the existing spectral search results dictionary
|
|
309
|
+
if accumulate_results:
|
|
310
|
+
# Merge results with existing spectral_search_results
|
|
311
|
+
for scan_id, precursor_dict in overall_results_dict.items():
|
|
312
|
+
if scan_id in self.spectral_search_results:
|
|
313
|
+
# Scan already has results, merge precursor_mz dictionaries
|
|
314
|
+
self.spectral_search_results[scan_id].update(precursor_dict)
|
|
315
|
+
else:
|
|
316
|
+
# New scan, add entire dictionary
|
|
317
|
+
self.spectral_search_results[scan_id] = precursor_dict
|
|
318
|
+
else:
|
|
319
|
+
# Replace existing results (original behavior)
|
|
320
|
+
self.spectral_search_results.update(overall_results_dict)
|
|
321
|
+
|
|
322
|
+
# If there are mass features, associate the results with each mass feature
|
|
323
|
+
if len(self.mass_features) > 0:
|
|
324
|
+
# Determine which results to associate with mass features
|
|
325
|
+
if accumulate_results:
|
|
326
|
+
# When accumulating, only associate new results from this search
|
|
327
|
+
# to avoid duplicating previously associated results
|
|
328
|
+
results_to_associate = overall_results_dict
|
|
329
|
+
else:
|
|
330
|
+
# When not accumulating, clear existing associations and re-associate all results
|
|
331
|
+
for mass_feature_id in self.mass_features.keys():
|
|
332
|
+
self.mass_features[mass_feature_id].ms2_similarity_results = []
|
|
333
|
+
results_to_associate = self.spectral_search_results
|
|
334
|
+
|
|
335
|
+
for mass_feature_id, mass_feature in self.mass_features.items():
|
|
336
|
+
scan_ids = mass_feature.ms2_scan_numbers
|
|
337
|
+
for ms2_scan_id in scan_ids:
|
|
338
|
+
precursor_mz = mass_feature.mz
|
|
339
|
+
try:
|
|
340
|
+
results_to_associate[ms2_scan_id][precursor_mz]
|
|
341
|
+
except KeyError:
|
|
342
|
+
pass
|
|
343
|
+
else:
|
|
344
|
+
self.mass_features[
|
|
345
|
+
mass_feature_id
|
|
346
|
+
].ms2_similarity_results.append(
|
|
347
|
+
results_to_associate[ms2_scan_id][precursor_mz]
|
|
348
|
+
)
|