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,994 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@author: Yuri E. Corilo
|
|
3
|
+
@date: Jun 27, 2019
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import warnings
|
|
7
|
+
from numpy import (
|
|
8
|
+
hstack,
|
|
9
|
+
inf,
|
|
10
|
+
isnan,
|
|
11
|
+
where,
|
|
12
|
+
array,
|
|
13
|
+
polyfit,
|
|
14
|
+
nan,
|
|
15
|
+
pad,
|
|
16
|
+
zeros,
|
|
17
|
+
searchsorted,
|
|
18
|
+
)
|
|
19
|
+
from corems.encapsulation.constant import Labels
|
|
20
|
+
from corems.mass_spectra.calc import SignalProcessing as sp
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PeakPicking:
|
|
24
|
+
"""Class for peak picking.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
None
|
|
29
|
+
|
|
30
|
+
Attributes
|
|
31
|
+
----------
|
|
32
|
+
None
|
|
33
|
+
|
|
34
|
+
Methods
|
|
35
|
+
-------
|
|
36
|
+
* prepare_peak_picking_data().
|
|
37
|
+
Prepare the mz, abundance, and frequence data for peak picking.
|
|
38
|
+
* cut_mz_domain_peak_picking().
|
|
39
|
+
Cut the m/z domain for peak picking.
|
|
40
|
+
* extrapolate_axes_for_pp(mz=None, abund=None, freq=None).
|
|
41
|
+
Extrapolate the m/z axis and fill the abundance axis with 0s.
|
|
42
|
+
* do_peak_picking().
|
|
43
|
+
Perform peak picking.
|
|
44
|
+
* find_minima(apex_index, abundance, len_abundance, right=True).
|
|
45
|
+
Find the minima of a peak.
|
|
46
|
+
* linear_fit_calc(intes, massa, index_term, index_sign).
|
|
47
|
+
Algebraic solution to a linear fit.
|
|
48
|
+
* calculate_resolving_power(intes, massa, current_index).
|
|
49
|
+
Calculate the resolving power of a peak.
|
|
50
|
+
* cal_minima(mass, abun).
|
|
51
|
+
Calculate the minima of a peak.
|
|
52
|
+
* calc_centroid(mass, abund, freq).
|
|
53
|
+
Calculate the centroid of a peak.
|
|
54
|
+
* get_threshold(intes).
|
|
55
|
+
Get the intensity threshold for peak picking.
|
|
56
|
+
* algebraic_quadratic(list_mass, list_y).
|
|
57
|
+
Find the apex of a peak - algebraically.
|
|
58
|
+
* find_apex_fit_quadratic(mass, abund, freq, current_index).
|
|
59
|
+
Find the apex of a peak.
|
|
60
|
+
* check_prominence(abun, current_index, len_abundance, peak_height_diff).
|
|
61
|
+
Check the prominence of a peak.
|
|
62
|
+
* use_the_max(mass, abund, current_index, len_abundance, peak_height_diff).
|
|
63
|
+
Use the max peak height as the centroid.
|
|
64
|
+
* calc_centroid_legacy(mass, abund, freq).
|
|
65
|
+
Legacy centroid calculation. Deprecated - for deletion.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def prepare_peak_picking_data(self):
|
|
70
|
+
"""Prepare the data for peak picking.
|
|
71
|
+
|
|
72
|
+
This function will prepare the m/z, abundance, and frequency data for peak picking according to the settings.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
mz : ndarray
|
|
77
|
+
The m/z axis.
|
|
78
|
+
abundance : ndarray
|
|
79
|
+
The abundance axis.
|
|
80
|
+
freq : ndarray or None
|
|
81
|
+
The frequency axis, if available.
|
|
82
|
+
"""
|
|
83
|
+
# First apply cut_mz_domain_peak_picking
|
|
84
|
+
mz, abundance, freq = self.cut_mz_domain_peak_picking()
|
|
85
|
+
|
|
86
|
+
# Then extrapolate the axes for peak picking
|
|
87
|
+
if self.settings.picking_point_extrapolate > 0:
|
|
88
|
+
mz, abundance, freq = self.extrapolate_axes_for_pp(mz, abundance, freq)
|
|
89
|
+
return mz, abundance, freq
|
|
90
|
+
|
|
91
|
+
def cut_mz_domain_peak_picking(self):
|
|
92
|
+
"""
|
|
93
|
+
Cut the m/z domain for peak picking.
|
|
94
|
+
|
|
95
|
+
Simplified function
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
mz_domain_X_low_cutoff : ndarray
|
|
100
|
+
The m/z values within the specified range.
|
|
101
|
+
mz_domain_low_Y_cutoff : ndarray
|
|
102
|
+
The abundance values within the specified range.
|
|
103
|
+
freq_domain_low_Y_cutoff : ndarray or None
|
|
104
|
+
The frequency values within the specified range, if available.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
max_picking_mz = self.settings.max_picking_mz
|
|
108
|
+
min_picking_mz = self.settings.min_picking_mz
|
|
109
|
+
|
|
110
|
+
# min_start = where(self.mz_exp_profile > min_picking_mz)[0][0]
|
|
111
|
+
# max_final = where(self.mz_exp_profile < max_picking_mz)[-1][-1]
|
|
112
|
+
min_start = searchsorted(a=self.mz_exp_profile, v=min_picking_mz)
|
|
113
|
+
max_final = searchsorted(a=self.mz_exp_profile, v=max_picking_mz)
|
|
114
|
+
|
|
115
|
+
if self.has_frequency:
|
|
116
|
+
if self.freq_exp_profile.any():
|
|
117
|
+
return (
|
|
118
|
+
self.mz_exp_profile[min_start:max_final],
|
|
119
|
+
self.abundance_profile[min_start:max_final],
|
|
120
|
+
self.freq_exp_profile[min_start:max_final],
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
else:
|
|
124
|
+
return (
|
|
125
|
+
self.mz_exp_profile[min_start:max_final],
|
|
126
|
+
self.abundance_profile[min_start:max_final],
|
|
127
|
+
None,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def legacy_cut_mz_domain_peak_picking(self):
|
|
131
|
+
"""
|
|
132
|
+
Cut the m/z domain for peak picking.
|
|
133
|
+
DEPRECATED
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
mz_domain_X_low_cutoff : ndarray
|
|
137
|
+
The m/z values within the specified range.
|
|
138
|
+
mz_domain_low_Y_cutoff : ndarray
|
|
139
|
+
The abundance values within the specified range.
|
|
140
|
+
freq_domain_low_Y_cutoff : ndarray or None
|
|
141
|
+
The frequency values within the specified range, if available.
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
max_picking_mz = self.settings.max_picking_mz
|
|
145
|
+
min_picking_mz = self.settings.min_picking_mz
|
|
146
|
+
|
|
147
|
+
min_final = where(self.mz_exp_profile > min_picking_mz)[-1][-1]
|
|
148
|
+
min_start = where(self.mz_exp_profile > min_picking_mz)[0][0]
|
|
149
|
+
|
|
150
|
+
(
|
|
151
|
+
mz_domain_X_low_cutoff,
|
|
152
|
+
mz_domain_low_Y_cutoff,
|
|
153
|
+
) = (
|
|
154
|
+
self.mz_exp_profile[min_start:min_final],
|
|
155
|
+
self.abundance_profile[min_start:min_final],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
max_final = where(self.mz_exp_profile < max_picking_mz)[-1][-1]
|
|
159
|
+
max_start = where(self.mz_exp_profile < max_picking_mz)[0][0]
|
|
160
|
+
|
|
161
|
+
if self.has_frequency:
|
|
162
|
+
if self.freq_exp_profile.any():
|
|
163
|
+
freq_domain_low_Y_cutoff = self.freq_exp_profile[min_start:min_final]
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
mz_domain_X_low_cutoff[max_start:max_final],
|
|
167
|
+
mz_domain_low_Y_cutoff[max_start:max_final],
|
|
168
|
+
freq_domain_low_Y_cutoff[max_start:max_final],
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
else:
|
|
172
|
+
return (
|
|
173
|
+
mz_domain_X_low_cutoff[max_start:max_final],
|
|
174
|
+
mz_domain_low_Y_cutoff[max_start:max_final],
|
|
175
|
+
None,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def extrapolate_axis(initial_array, pts):
|
|
180
|
+
"""
|
|
181
|
+
This function will extrapolate an input array in both directions by N pts.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
initial_array : ndarray
|
|
186
|
+
The input array.
|
|
187
|
+
pts : int
|
|
188
|
+
The number of points to extrapolate.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
ndarray
|
|
193
|
+
The extrapolated array.
|
|
194
|
+
|
|
195
|
+
Notes
|
|
196
|
+
--------
|
|
197
|
+
This is a static method.
|
|
198
|
+
"""
|
|
199
|
+
initial_array_len = len(initial_array)
|
|
200
|
+
right_delta = initial_array[-1] - initial_array[-2]
|
|
201
|
+
left_delta = initial_array[1] - initial_array[0]
|
|
202
|
+
|
|
203
|
+
# Create an array with extra space for extrapolation
|
|
204
|
+
pad_array = zeros(initial_array_len + 2 * pts)
|
|
205
|
+
|
|
206
|
+
# Copy original array into the middle of the padded array
|
|
207
|
+
pad_array[pts : pts + initial_array_len] = initial_array
|
|
208
|
+
|
|
209
|
+
# Extrapolate the right side
|
|
210
|
+
for pt in range(pts):
|
|
211
|
+
final_value = initial_array[-1]
|
|
212
|
+
value_to_add = right_delta * (pt + 1)
|
|
213
|
+
new_value = final_value + value_to_add
|
|
214
|
+
pad_array[initial_array_len + pts + pt] = new_value
|
|
215
|
+
|
|
216
|
+
# Extrapolate the left side
|
|
217
|
+
for pt in range(pts):
|
|
218
|
+
first_value = initial_array[0]
|
|
219
|
+
value_to_subtract = left_delta * (pt + 1)
|
|
220
|
+
new_value = first_value - value_to_subtract
|
|
221
|
+
pad_array[pts - pt - 1] = new_value
|
|
222
|
+
|
|
223
|
+
return pad_array
|
|
224
|
+
|
|
225
|
+
def extrapolate_axes_for_pp(self, mz=None, abund=None, freq=None):
|
|
226
|
+
"""Extrapolate the m/z axis and fill the abundance axis with 0s.
|
|
227
|
+
|
|
228
|
+
Parameters
|
|
229
|
+
----------
|
|
230
|
+
mz : ndarray or None
|
|
231
|
+
The m/z axis, if available. If None, the experimental m/z axis is used.
|
|
232
|
+
abund : ndarray or None
|
|
233
|
+
The abundance axis, if available. If None, the experimental abundance axis is used.
|
|
234
|
+
freq : ndarray or None
|
|
235
|
+
The frequency axis, if available. If None, the experimental frequency axis is used.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
mz : ndarray
|
|
240
|
+
The extrapolated m/z axis.
|
|
241
|
+
abund : ndarray
|
|
242
|
+
The abundance axis with 0s filled.
|
|
243
|
+
freq : ndarray or None
|
|
244
|
+
The extrapolated frequency axis, if available.
|
|
245
|
+
|
|
246
|
+
Notes
|
|
247
|
+
--------
|
|
248
|
+
This function will extrapolate the mz axis by the number of datapoints specified in the settings,
|
|
249
|
+
and fill the abundance axis with 0s.
|
|
250
|
+
This should prevent peak picking issues at the spectrum edge.
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
# Check if the input arrays are provided
|
|
254
|
+
if mz is None or abund is None:
|
|
255
|
+
mz, abund = self.mz_exp_profile, self.abundance_profile
|
|
256
|
+
if self.has_frequency:
|
|
257
|
+
freq = self.freq_exp_profile
|
|
258
|
+
else:
|
|
259
|
+
freq = None
|
|
260
|
+
pts = self.settings.picking_point_extrapolate
|
|
261
|
+
if pts == 0:
|
|
262
|
+
return mz, abund, freq
|
|
263
|
+
|
|
264
|
+
mz = self.extrapolate_axis(mz, pts)
|
|
265
|
+
abund = pad(abund, (pts, pts), mode="constant", constant_values=(0, 0))
|
|
266
|
+
if freq is not None:
|
|
267
|
+
freq = self.extrapolate_axis(freq, pts)
|
|
268
|
+
return mz, abund, freq
|
|
269
|
+
|
|
270
|
+
def do_peak_picking(self):
|
|
271
|
+
"""Perform peak picking."""
|
|
272
|
+
mz, abundance, freq = self.prepare_peak_picking_data()
|
|
273
|
+
|
|
274
|
+
if (
|
|
275
|
+
self.label == Labels.bruker_frequency
|
|
276
|
+
or self.label == Labels.midas_frequency
|
|
277
|
+
):
|
|
278
|
+
self.calc_centroid(mz, abundance, freq)
|
|
279
|
+
|
|
280
|
+
elif self.label == Labels.thermo_profile:
|
|
281
|
+
self.calc_centroid(mz, abundance, freq)
|
|
282
|
+
|
|
283
|
+
elif self.label == Labels.bruker_profile:
|
|
284
|
+
self.calc_centroid(mz, abundance, freq)
|
|
285
|
+
|
|
286
|
+
elif self.label == Labels.booster_profile:
|
|
287
|
+
self.calc_centroid(mz, abundance, freq)
|
|
288
|
+
|
|
289
|
+
elif self.label == Labels.simulated_profile:
|
|
290
|
+
self.calc_centroid(mz, abundance, freq)
|
|
291
|
+
|
|
292
|
+
else:
|
|
293
|
+
raise Exception("Unknow mass spectrum type", self.label)
|
|
294
|
+
|
|
295
|
+
def find_minima(self, apex_index, abundance, len_abundance, right=True):
|
|
296
|
+
"""Find the minima of a peak.
|
|
297
|
+
|
|
298
|
+
Parameters
|
|
299
|
+
----------
|
|
300
|
+
apex_index : int
|
|
301
|
+
The index of the peak apex.
|
|
302
|
+
abundance : ndarray
|
|
303
|
+
The abundance values.
|
|
304
|
+
len_abundance : int
|
|
305
|
+
The length of the abundance array.
|
|
306
|
+
right : bool, optional
|
|
307
|
+
Flag indicating whether to search for minima to the right of the apex (default is True).
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
int
|
|
312
|
+
The index of the minima.
|
|
313
|
+
|
|
314
|
+
"""
|
|
315
|
+
j = apex_index
|
|
316
|
+
|
|
317
|
+
if right:
|
|
318
|
+
minima = abundance[j] > abundance[j + 1]
|
|
319
|
+
else:
|
|
320
|
+
minima = abundance[j] > abundance[j - 1]
|
|
321
|
+
|
|
322
|
+
while minima:
|
|
323
|
+
if j == 1 or j == len_abundance - 2:
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
if right:
|
|
327
|
+
j += 1
|
|
328
|
+
|
|
329
|
+
minima = abundance[j] >= abundance[j + 1]
|
|
330
|
+
|
|
331
|
+
else:
|
|
332
|
+
j -= 1
|
|
333
|
+
minima = abundance[j] >= abundance[j - 1]
|
|
334
|
+
|
|
335
|
+
if right:
|
|
336
|
+
return j
|
|
337
|
+
else:
|
|
338
|
+
return j
|
|
339
|
+
|
|
340
|
+
@staticmethod
|
|
341
|
+
def linear_fit_calc(intes, massa, index_term, index_sign):
|
|
342
|
+
"""
|
|
343
|
+
Algebraic solution to a linear fit - roughly 25-50x faster than numpy polyfit when passing only two vals and doing a 1st order fit
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
intes : ndarray
|
|
348
|
+
The intensity values.
|
|
349
|
+
massa : ndarray
|
|
350
|
+
The mass values.
|
|
351
|
+
index_term : int
|
|
352
|
+
The index of the current term.
|
|
353
|
+
index_sign : str
|
|
354
|
+
The index sign
|
|
355
|
+
|
|
356
|
+
Returns
|
|
357
|
+
-------
|
|
358
|
+
ndarray
|
|
359
|
+
The coefficients of the linear fit.
|
|
360
|
+
|
|
361
|
+
Notes
|
|
362
|
+
--------
|
|
363
|
+
This is a static method.
|
|
364
|
+
"""
|
|
365
|
+
if index_sign == "+":
|
|
366
|
+
x1, x2 = massa[index_term], massa[index_term + 1]
|
|
367
|
+
y1, y2 = intes[index_term], intes[index_term + 1]
|
|
368
|
+
elif index_sign == "-":
|
|
369
|
+
x1, x2 = massa[index_term], massa[index_term - 1]
|
|
370
|
+
y1, y2 = intes[index_term], intes[index_term - 1]
|
|
371
|
+
else:
|
|
372
|
+
warnings.warn("error in linear fit calc, unknown index sign")
|
|
373
|
+
|
|
374
|
+
# Calculate the slope (m)
|
|
375
|
+
slope = (y2 - y1) / (x2 - x1)
|
|
376
|
+
|
|
377
|
+
# Calculate the intercept (b)
|
|
378
|
+
intercept = y1 - slope * x1
|
|
379
|
+
|
|
380
|
+
# The coefficients array would be [slope, intercept]
|
|
381
|
+
coefficients = array([slope, intercept])
|
|
382
|
+
return coefficients
|
|
383
|
+
|
|
384
|
+
def calculate_resolving_power(self, intes, massa, current_index):
|
|
385
|
+
"""Calculate the resolving power of a peak.
|
|
386
|
+
|
|
387
|
+
Parameters
|
|
388
|
+
----------
|
|
389
|
+
intes : ndarray
|
|
390
|
+
The intensity values.
|
|
391
|
+
massa : ndarray
|
|
392
|
+
The mass values.
|
|
393
|
+
current_index : int
|
|
394
|
+
The index of the current peak.
|
|
395
|
+
|
|
396
|
+
Returns
|
|
397
|
+
-------
|
|
398
|
+
float
|
|
399
|
+
The resolving power of the peak.
|
|
400
|
+
|
|
401
|
+
Notes
|
|
402
|
+
--------
|
|
403
|
+
This is a conservative calculation of resolving power,
|
|
404
|
+
the peak need to be resolved at least at the half-maximum magnitude,
|
|
405
|
+
otherwise, the combined full width at half maximum is used to calculate resolving power.
|
|
406
|
+
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
peak_height = intes[current_index]
|
|
410
|
+
target_peak_height = peak_height / 2
|
|
411
|
+
|
|
412
|
+
peak_height_minus = peak_height
|
|
413
|
+
peak_height_plus = peak_height
|
|
414
|
+
|
|
415
|
+
# There are issues when a peak is at the high or low limit of a spectrum in finding its local minima and maxima
|
|
416
|
+
# This solution will return nan for resolving power when a peak is possibly too close to an edge to avoid the issue
|
|
417
|
+
|
|
418
|
+
if current_index < 5:
|
|
419
|
+
warnings.warn("peak at low spectrum edge, returning no resolving power")
|
|
420
|
+
return nan
|
|
421
|
+
elif abs(current_index - len(intes)) < 5:
|
|
422
|
+
warnings.warn("peak at high spectrum edge, returning no resolving power")
|
|
423
|
+
return nan
|
|
424
|
+
else:
|
|
425
|
+
pass
|
|
426
|
+
|
|
427
|
+
index_minus = current_index
|
|
428
|
+
while peak_height_minus >= target_peak_height:
|
|
429
|
+
index_minus = index_minus - 1
|
|
430
|
+
if index_minus < 0:
|
|
431
|
+
warnings.warn(
|
|
432
|
+
"Res. calc. warning - peak index minus adjacent to spectrum edge \n \
|
|
433
|
+
Zeroing the first 5 data points of abundance. Peaks at spectrum edge may be incorrectly reported \n \
|
|
434
|
+
Perhaps try to increase picking_point_extrapolate (e.g. to 3)"
|
|
435
|
+
)
|
|
436
|
+
# Pad the first 5 data points with zeros and restart the loop
|
|
437
|
+
intes[:5] = 0
|
|
438
|
+
peak_height_minus = target_peak_height
|
|
439
|
+
index_minus = current_index
|
|
440
|
+
else:
|
|
441
|
+
peak_height_minus = intes[index_minus]
|
|
442
|
+
|
|
443
|
+
if self.mspeaks_settings.legacy_centroid_polyfit:
|
|
444
|
+
x = [massa[index_minus], massa[index_minus + 1]]
|
|
445
|
+
y = [intes[index_minus], intes[index_minus + 1]]
|
|
446
|
+
coefficients = polyfit(x, y, 1)
|
|
447
|
+
else:
|
|
448
|
+
coefficients = self.linear_fit_calc(
|
|
449
|
+
intes, massa, index_minus, index_sign="+"
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
a = coefficients[0]
|
|
453
|
+
b = coefficients[1]
|
|
454
|
+
if self.mspeaks_settings.legacy_resolving_power:
|
|
455
|
+
y_intercept = intes[index_minus] + (
|
|
456
|
+
(intes[index_minus + 1] - intes[index_minus]) / 2
|
|
457
|
+
)
|
|
458
|
+
else:
|
|
459
|
+
y_intercept = target_peak_height
|
|
460
|
+
massa1 = (y_intercept - b) / a
|
|
461
|
+
|
|
462
|
+
index_plus = current_index
|
|
463
|
+
while peak_height_plus >= target_peak_height:
|
|
464
|
+
index_plus = index_plus + 1
|
|
465
|
+
|
|
466
|
+
try:
|
|
467
|
+
peak_height_plus = intes[index_plus]
|
|
468
|
+
except IndexError:
|
|
469
|
+
warnings.warn(
|
|
470
|
+
"Res. calc. warning - peak index plus adjacent to spectrum edge \n \
|
|
471
|
+
Zeroing the last 5 data points of abundance. Peaks at spectrum edge may be incorrectly reported\
|
|
472
|
+
Perhaps try to increase picking_point_extrapolate (e.g. to 3)"
|
|
473
|
+
)
|
|
474
|
+
# Pad the first 5 data points with zeros and restart the loop
|
|
475
|
+
intes[-5:] = 0
|
|
476
|
+
peak_height_plus = target_peak_height
|
|
477
|
+
index_plus = current_index
|
|
478
|
+
|
|
479
|
+
if self.mspeaks_settings.legacy_centroid_polyfit:
|
|
480
|
+
x = [massa[index_plus], massa[index_plus - 1]]
|
|
481
|
+
y = [intes[index_plus], intes[index_plus - 1]]
|
|
482
|
+
coefficients = polyfit(x, y, 1)
|
|
483
|
+
else:
|
|
484
|
+
coefficients = self.linear_fit_calc(
|
|
485
|
+
intes, massa, index_plus, index_sign="-"
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
a = coefficients[0]
|
|
489
|
+
b = coefficients[1]
|
|
490
|
+
|
|
491
|
+
if self.mspeaks_settings.legacy_resolving_power:
|
|
492
|
+
y_intercept = intes[index_plus - 1] + (
|
|
493
|
+
(intes[index_plus] - intes[index_plus - 1]) / 2
|
|
494
|
+
)
|
|
495
|
+
else:
|
|
496
|
+
y_intercept = target_peak_height
|
|
497
|
+
|
|
498
|
+
massa2 = (y_intercept - b) / a
|
|
499
|
+
|
|
500
|
+
if massa1 > massa2:
|
|
501
|
+
resolvingpower = massa[current_index] / (massa1 - massa2)
|
|
502
|
+
|
|
503
|
+
else:
|
|
504
|
+
resolvingpower = massa[current_index] / (massa2 - massa1)
|
|
505
|
+
|
|
506
|
+
return resolvingpower
|
|
507
|
+
|
|
508
|
+
def cal_minima(self, mass, abun):
|
|
509
|
+
"""Calculate the minima of a peak.
|
|
510
|
+
|
|
511
|
+
Parameters
|
|
512
|
+
----------
|
|
513
|
+
mass : ndarray
|
|
514
|
+
The mass values.
|
|
515
|
+
abun : ndarray
|
|
516
|
+
The abundance values.
|
|
517
|
+
|
|
518
|
+
Returns
|
|
519
|
+
-------
|
|
520
|
+
ndarray or None
|
|
521
|
+
The mass values at the minima, if found.
|
|
522
|
+
|
|
523
|
+
"""
|
|
524
|
+
abun = -abun
|
|
525
|
+
|
|
526
|
+
dy = abun[1:] - abun[:-1]
|
|
527
|
+
|
|
528
|
+
# replaces nan for infinity
|
|
529
|
+
indices_nan = where(isnan(abun))[0]
|
|
530
|
+
|
|
531
|
+
if indices_nan.size:
|
|
532
|
+
abun[indices_nan] = inf
|
|
533
|
+
dy[where(isnan(dy))[0]] = inf
|
|
534
|
+
|
|
535
|
+
indexes = where((hstack((dy, 0)) < 0) & (hstack((0, dy)) > 0))[0]
|
|
536
|
+
|
|
537
|
+
if indexes.size:
|
|
538
|
+
return mass[indexes], abun[indexes]
|
|
539
|
+
|
|
540
|
+
def calc_centroid(self, mass, abund, freq):
|
|
541
|
+
"""Calculate the centroid of a peak.
|
|
542
|
+
|
|
543
|
+
Parameters
|
|
544
|
+
----------
|
|
545
|
+
mass : ndarray
|
|
546
|
+
The mass values.
|
|
547
|
+
abund : ndarray
|
|
548
|
+
The abundance values.
|
|
549
|
+
freq : ndarray or None
|
|
550
|
+
The frequency values, if available.
|
|
551
|
+
|
|
552
|
+
Returns
|
|
553
|
+
-------
|
|
554
|
+
None
|
|
555
|
+
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
max_height = self.mspeaks_settings.peak_height_max_percent
|
|
559
|
+
max_prominence = self.mspeaks_settings.peak_max_prominence_percent
|
|
560
|
+
min_peak_datapoints = self.mspeaks_settings.min_peak_datapoints
|
|
561
|
+
peak_derivative_threshold = self.mspeaks_settings.peak_derivative_threshold
|
|
562
|
+
max_abun = max(abund)
|
|
563
|
+
peak_height_diff = lambda hi, li: ((abund[hi] - abund[li]) / max_abun) * 100
|
|
564
|
+
|
|
565
|
+
domain = mass
|
|
566
|
+
signal = abund
|
|
567
|
+
len_signal = len(signal)
|
|
568
|
+
|
|
569
|
+
signal_threshold, factor = self.get_threshold(abund)
|
|
570
|
+
max_signal = factor
|
|
571
|
+
|
|
572
|
+
correct_baseline = False
|
|
573
|
+
|
|
574
|
+
include_indexes = sp.peak_picking_first_derivative(
|
|
575
|
+
domain,
|
|
576
|
+
signal,
|
|
577
|
+
max_height,
|
|
578
|
+
max_prominence,
|
|
579
|
+
max_signal,
|
|
580
|
+
min_peak_datapoints,
|
|
581
|
+
peak_derivative_threshold,
|
|
582
|
+
signal_threshold=signal_threshold,
|
|
583
|
+
correct_baseline=correct_baseline,
|
|
584
|
+
abun_norm=1,
|
|
585
|
+
plot_res=False,
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
for indexes_tuple in include_indexes:
|
|
589
|
+
apex_index = indexes_tuple[1]
|
|
590
|
+
|
|
591
|
+
peak_indexes = self.check_prominence(
|
|
592
|
+
abund, apex_index, len_signal, peak_height_diff
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
if peak_indexes:
|
|
596
|
+
mz_exp_centroid, freq_centr, intes_centr = self.find_apex_fit_quadratic(
|
|
597
|
+
mass, abund, freq, apex_index
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
if mz_exp_centroid:
|
|
601
|
+
peak_resolving_power = self.calculate_resolving_power(
|
|
602
|
+
abund, mass, apex_index
|
|
603
|
+
)
|
|
604
|
+
s2n = intes_centr / self.baseline_noise_std
|
|
605
|
+
self.add_mspeak(
|
|
606
|
+
self.polarity,
|
|
607
|
+
mz_exp_centroid,
|
|
608
|
+
abund[apex_index],
|
|
609
|
+
peak_resolving_power,
|
|
610
|
+
s2n,
|
|
611
|
+
indexes_tuple,
|
|
612
|
+
exp_freq=freq_centr,
|
|
613
|
+
ms_parent=self,
|
|
614
|
+
)
|
|
615
|
+
# pyplot.plot(domain[start_index: final_index + 1], signal[start_index:final_index + 1], c='black')
|
|
616
|
+
# pyplot.show()
|
|
617
|
+
|
|
618
|
+
def get_threshold(self, intes):
|
|
619
|
+
"""Get the intensity threshold for peak picking.
|
|
620
|
+
|
|
621
|
+
Parameters
|
|
622
|
+
----------
|
|
623
|
+
intes : ndarray
|
|
624
|
+
The intensity values.
|
|
625
|
+
|
|
626
|
+
Returns
|
|
627
|
+
-------
|
|
628
|
+
float
|
|
629
|
+
The intensity threshold.
|
|
630
|
+
float
|
|
631
|
+
The factor to multiply the intensity threshold by.
|
|
632
|
+
"""
|
|
633
|
+
|
|
634
|
+
intes = array(intes).astype(float)
|
|
635
|
+
|
|
636
|
+
noise_threshold_method = self.settings.noise_threshold_method
|
|
637
|
+
|
|
638
|
+
if noise_threshold_method == "minima":
|
|
639
|
+
if self.is_centroid:
|
|
640
|
+
warnings.warn(
|
|
641
|
+
"Auto threshould is disabled for centroid data, returning 0"
|
|
642
|
+
)
|
|
643
|
+
factor = 1
|
|
644
|
+
abundance_threshold = 1e-20
|
|
645
|
+
# print(self.settings.noise_threshold_min_std)
|
|
646
|
+
else:
|
|
647
|
+
abundance_threshold = self.baseline_noise + (
|
|
648
|
+
self.settings.noise_threshold_min_std * self.baseline_noise_std
|
|
649
|
+
)
|
|
650
|
+
factor = 1
|
|
651
|
+
|
|
652
|
+
elif noise_threshold_method == "signal_noise":
|
|
653
|
+
abundance_threshold = self.settings.noise_threshold_min_s2n
|
|
654
|
+
if self.is_centroid:
|
|
655
|
+
factor = 1
|
|
656
|
+
else:
|
|
657
|
+
factor = self.baseline_noise_std
|
|
658
|
+
|
|
659
|
+
elif noise_threshold_method == "relative_abundance":
|
|
660
|
+
abundance_threshold = self.settings.noise_threshold_min_relative_abundance
|
|
661
|
+
factor = intes.max() / 100
|
|
662
|
+
|
|
663
|
+
elif noise_threshold_method == "absolute_abundance":
|
|
664
|
+
abundance_threshold = self.settings.noise_threshold_absolute_abundance
|
|
665
|
+
factor = 1
|
|
666
|
+
|
|
667
|
+
elif noise_threshold_method == "log":
|
|
668
|
+
if self.is_centroid:
|
|
669
|
+
raise Exception("log noise Not tested for centroid data")
|
|
670
|
+
abundance_threshold = self.settings.noise_threshold_log_nsigma
|
|
671
|
+
factor = self.baseline_noise_std
|
|
672
|
+
|
|
673
|
+
else:
|
|
674
|
+
raise Exception(
|
|
675
|
+
"%s method was not implemented, please refer to corems.mass_spectrum.calc.NoiseCalc Class"
|
|
676
|
+
% noise_threshold_method
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
return abundance_threshold, factor
|
|
680
|
+
|
|
681
|
+
@staticmethod
|
|
682
|
+
def algebraic_quadratic(list_mass, list_y):
|
|
683
|
+
"""
|
|
684
|
+
Find the apex of a peak - algebraically.
|
|
685
|
+
Faster than using numpy polyfit by ~28x per fit.
|
|
686
|
+
|
|
687
|
+
Parameters
|
|
688
|
+
----------
|
|
689
|
+
list_mass : ndarray
|
|
690
|
+
list of m/z values (3 points)
|
|
691
|
+
list_y : ndarray
|
|
692
|
+
list of abundance values (3 points)
|
|
693
|
+
|
|
694
|
+
Returns
|
|
695
|
+
-------
|
|
696
|
+
a, b, c: float
|
|
697
|
+
coefficients of the quadratic equation.
|
|
698
|
+
|
|
699
|
+
Notes
|
|
700
|
+
--------
|
|
701
|
+
This is a static method.
|
|
702
|
+
"""
|
|
703
|
+
x_1, x_2, x_3 = list_mass
|
|
704
|
+
y_1, y_2, y_3 = list_y
|
|
705
|
+
|
|
706
|
+
a = (
|
|
707
|
+
y_1 / ((x_1 - x_2) * (x_1 - x_3))
|
|
708
|
+
+ y_2 / ((x_2 - x_1) * (x_2 - x_3))
|
|
709
|
+
+ y_3 / ((x_3 - x_1) * (x_3 - x_2))
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
b = (
|
|
713
|
+
-y_1 * (x_2 + x_3) / ((x_1 - x_2) * (x_1 - x_3))
|
|
714
|
+
- y_2 * (x_1 + x_3) / ((x_2 - x_1) * (x_2 - x_3))
|
|
715
|
+
- y_3 * (x_1 + x_2) / ((x_3 - x_1) * (x_3 - x_2))
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
c = (
|
|
719
|
+
y_1 * x_2 * x_3 / ((x_1 - x_2) * (x_1 - x_3))
|
|
720
|
+
+ y_2 * x_1 * x_3 / ((x_2 - x_1) * (x_2 - x_3))
|
|
721
|
+
+ y_3 * x_1 * x_2 / ((x_3 - x_1) * (x_3 - x_2))
|
|
722
|
+
)
|
|
723
|
+
return a, b, c
|
|
724
|
+
|
|
725
|
+
def find_apex_fit_quadratic(self, mass, abund, freq, current_index):
|
|
726
|
+
"""
|
|
727
|
+
Find the apex of a peak.
|
|
728
|
+
|
|
729
|
+
Parameters
|
|
730
|
+
----------
|
|
731
|
+
mass : ndarray
|
|
732
|
+
The mass values.
|
|
733
|
+
abund : ndarray
|
|
734
|
+
The abundance values.
|
|
735
|
+
freq : ndarray or None
|
|
736
|
+
The frequency values, if available.
|
|
737
|
+
current_index : int
|
|
738
|
+
The index of the current peak.
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
Returns
|
|
742
|
+
-------
|
|
743
|
+
float
|
|
744
|
+
The m/z value of the peak apex.
|
|
745
|
+
float
|
|
746
|
+
The frequency value of the peak apex, if available.
|
|
747
|
+
float
|
|
748
|
+
The abundance value of the peak apex.
|
|
749
|
+
|
|
750
|
+
"""
|
|
751
|
+
# calc prominence
|
|
752
|
+
# peak_indexes = self.check_prominence(abund, current_index, len_abundance, peak_height_diff )
|
|
753
|
+
|
|
754
|
+
# if not peak_indexes:
|
|
755
|
+
|
|
756
|
+
# return None, None, None, None
|
|
757
|
+
|
|
758
|
+
# else:
|
|
759
|
+
|
|
760
|
+
# fit parabola to three most abundant datapoints
|
|
761
|
+
list_mass = [
|
|
762
|
+
mass[current_index - 1],
|
|
763
|
+
mass[current_index],
|
|
764
|
+
mass[current_index + 1],
|
|
765
|
+
]
|
|
766
|
+
list_y = [
|
|
767
|
+
abund[current_index - 1],
|
|
768
|
+
abund[current_index],
|
|
769
|
+
abund[current_index + 1],
|
|
770
|
+
]
|
|
771
|
+
|
|
772
|
+
if self.mspeaks_settings.legacy_centroid_polyfit:
|
|
773
|
+
z = polyfit(list_mass, list_y, 2)
|
|
774
|
+
a = z[0]
|
|
775
|
+
b = z[1]
|
|
776
|
+
else:
|
|
777
|
+
a, b, c = self.algebraic_quadratic(list_mass, list_y)
|
|
778
|
+
|
|
779
|
+
calculated = -b / (2 * a)
|
|
780
|
+
|
|
781
|
+
if calculated < 1 or int(calculated) != int(list_mass[1]):
|
|
782
|
+
mz_exp_centroid = list_mass[1]
|
|
783
|
+
|
|
784
|
+
else:
|
|
785
|
+
mz_exp_centroid = calculated
|
|
786
|
+
|
|
787
|
+
if (
|
|
788
|
+
self.label == Labels.bruker_frequency
|
|
789
|
+
or self.label == Labels.midas_frequency
|
|
790
|
+
):
|
|
791
|
+
# fit parabola to three most abundant frequency datapoints
|
|
792
|
+
list_freq = [
|
|
793
|
+
freq[current_index - 1],
|
|
794
|
+
freq[current_index],
|
|
795
|
+
freq[current_index + 1],
|
|
796
|
+
]
|
|
797
|
+
if self.mspeaks_settings.legacy_centroid_polyfit:
|
|
798
|
+
z = polyfit(list_mass, list_y, 2)
|
|
799
|
+
a = z[0]
|
|
800
|
+
b = z[1]
|
|
801
|
+
else:
|
|
802
|
+
a, b, c = self.algebraic_quadratic(list_mass, list_y)
|
|
803
|
+
|
|
804
|
+
calculated_freq = -b / (2 * a)
|
|
805
|
+
|
|
806
|
+
if calculated_freq < 1 or int(calculated_freq) != freq[current_index]:
|
|
807
|
+
freq_centr = list_freq[1]
|
|
808
|
+
|
|
809
|
+
else:
|
|
810
|
+
freq_centr = calculated_freq
|
|
811
|
+
|
|
812
|
+
else:
|
|
813
|
+
freq_centr = None
|
|
814
|
+
|
|
815
|
+
if self.mspeaks_settings.legacy_centroid_polyfit:
|
|
816
|
+
abundance_centroid = abund[current_index]
|
|
817
|
+
else:
|
|
818
|
+
abundance_centroid = a * mz_exp_centroid**2 + b * mz_exp_centroid + c
|
|
819
|
+
|
|
820
|
+
return mz_exp_centroid, freq_centr, abundance_centroid
|
|
821
|
+
|
|
822
|
+
def check_prominence(
|
|
823
|
+
self, abun, current_index, len_abundance, peak_height_diff
|
|
824
|
+
) -> tuple or False:
|
|
825
|
+
"""Check the prominence of a peak.
|
|
826
|
+
|
|
827
|
+
Parameters
|
|
828
|
+
----------
|
|
829
|
+
abun : ndarray
|
|
830
|
+
The abundance values.
|
|
831
|
+
current_index : int
|
|
832
|
+
The index of the current peak.
|
|
833
|
+
len_abundance : int
|
|
834
|
+
The length of the abundance array.
|
|
835
|
+
peak_height_diff : function
|
|
836
|
+
The function to calculate the peak height difference.
|
|
837
|
+
|
|
838
|
+
Returns
|
|
839
|
+
-------
|
|
840
|
+
tuple or False
|
|
841
|
+
A tuple containing the indexes of the peak, if the prominence is above the threshold.
|
|
842
|
+
Otherwise, False.
|
|
843
|
+
|
|
844
|
+
"""
|
|
845
|
+
|
|
846
|
+
final_index = self.find_minima(current_index, abun, len_abundance, right=True)
|
|
847
|
+
|
|
848
|
+
start_index = self.find_minima(current_index, abun, len_abundance, right=False)
|
|
849
|
+
|
|
850
|
+
peak_indexes = (current_index - 1, current_index, current_index + 1)
|
|
851
|
+
|
|
852
|
+
if (
|
|
853
|
+
min(
|
|
854
|
+
peak_height_diff(current_index, start_index),
|
|
855
|
+
peak_height_diff(current_index, final_index),
|
|
856
|
+
)
|
|
857
|
+
> self.mspeaks_settings.peak_min_prominence_percent
|
|
858
|
+
):
|
|
859
|
+
return peak_indexes
|
|
860
|
+
|
|
861
|
+
else:
|
|
862
|
+
return False
|
|
863
|
+
|
|
864
|
+
def use_the_max(self, mass, abund, current_index, len_abundance, peak_height_diff):
|
|
865
|
+
"""Use the max peak height as the centroid
|
|
866
|
+
|
|
867
|
+
Parameters
|
|
868
|
+
----------
|
|
869
|
+
mass : ndarray
|
|
870
|
+
The mass values.
|
|
871
|
+
abund : ndarray
|
|
872
|
+
The abundance values.
|
|
873
|
+
current_index : int
|
|
874
|
+
The index of the current peak.
|
|
875
|
+
len_abundance : int
|
|
876
|
+
The length of the abundance array.
|
|
877
|
+
peak_height_diff : function
|
|
878
|
+
The function to calculate the peak height difference.
|
|
879
|
+
|
|
880
|
+
Returns
|
|
881
|
+
-------
|
|
882
|
+
float
|
|
883
|
+
The m/z value of the peak apex.
|
|
884
|
+
float
|
|
885
|
+
The abundance value of the peak apex.
|
|
886
|
+
tuple or None
|
|
887
|
+
A tuple containing the indexes of the peak, if the prominence is above the threshold.
|
|
888
|
+
Otherwise, None.
|
|
889
|
+
"""
|
|
890
|
+
|
|
891
|
+
peak_indexes = self.check_prominence(
|
|
892
|
+
abund, current_index, len_abundance, peak_height_diff
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
if not peak_indexes:
|
|
896
|
+
return None, None, None
|
|
897
|
+
|
|
898
|
+
else:
|
|
899
|
+
return mass[current_index], abund[current_index], peak_indexes
|
|
900
|
+
|
|
901
|
+
def calc_centroid_legacy(self, mass, abund, freq):
|
|
902
|
+
"""Legacy centroid calculation
|
|
903
|
+
Deprecated - for deletion.
|
|
904
|
+
|
|
905
|
+
"""
|
|
906
|
+
warnings.warn(
|
|
907
|
+
"Legacy centroid calculation is deprecated. Please use the new centroid calculation method."
|
|
908
|
+
)
|
|
909
|
+
pass
|
|
910
|
+
if False:
|
|
911
|
+
len_abundance = len(abund)
|
|
912
|
+
|
|
913
|
+
max_abundance = max(abund)
|
|
914
|
+
|
|
915
|
+
peak_height_diff = (
|
|
916
|
+
lambda hi, li: ((abund[hi] - abund[li]) / max_abundance) * 100
|
|
917
|
+
)
|
|
918
|
+
|
|
919
|
+
abundance_threshold, factor = self.get_threshold(abund)
|
|
920
|
+
# print(abundance_threshold, factor)
|
|
921
|
+
# find indices of all peaks
|
|
922
|
+
dy = abund[1:] - abund[:-1]
|
|
923
|
+
|
|
924
|
+
# replaces nan for infi nity
|
|
925
|
+
indices_nan = where(isnan(abund))[0]
|
|
926
|
+
|
|
927
|
+
if indices_nan.size:
|
|
928
|
+
abund[indices_nan] = inf
|
|
929
|
+
dy[where(isnan(dy))[0]] = inf
|
|
930
|
+
|
|
931
|
+
indexes = where((hstack((dy, 0)) < 0) & (hstack((0, dy)) > 0))[0]
|
|
932
|
+
|
|
933
|
+
# noise threshold
|
|
934
|
+
if indexes.size and abundance_threshold is not None:
|
|
935
|
+
indexes = indexes[abund[indexes] / factor >= abundance_threshold]
|
|
936
|
+
# filter out 'peaks' within 3 points of the spectrum limits
|
|
937
|
+
# remove entries within 3 points of upper limit
|
|
938
|
+
indexes = [x for x in indexes if (len_abundance - x) > 3]
|
|
939
|
+
# remove entries within 3 points of zero
|
|
940
|
+
indexes = [x for x in indexes if x > 3]
|
|
941
|
+
|
|
942
|
+
for current_index in indexes:
|
|
943
|
+
if self.label == Labels.simulated_profile:
|
|
944
|
+
mz_exp_centroid, intes_centr, peak_indexes = self.use_the_max(
|
|
945
|
+
mass, abund, current_index, len_abundance, peak_height_diff
|
|
946
|
+
)
|
|
947
|
+
if mz_exp_centroid:
|
|
948
|
+
peak_resolving_power = self.calculate_resolving_power(
|
|
949
|
+
abund, mass, current_index
|
|
950
|
+
)
|
|
951
|
+
s2n = intes_centr / self.baseline_noise_std
|
|
952
|
+
freq_centr = None
|
|
953
|
+
self.add_mspeak(
|
|
954
|
+
self.polarity,
|
|
955
|
+
mz_exp_centroid,
|
|
956
|
+
abund[current_index],
|
|
957
|
+
peak_resolving_power,
|
|
958
|
+
s2n,
|
|
959
|
+
peak_indexes,
|
|
960
|
+
exp_freq=freq_centr,
|
|
961
|
+
ms_parent=self,
|
|
962
|
+
)
|
|
963
|
+
|
|
964
|
+
else:
|
|
965
|
+
mz_exp_centroid, freq_centr, intes_centr, peak_indexes = (
|
|
966
|
+
self.find_apex_fit_quadratic(
|
|
967
|
+
mass,
|
|
968
|
+
abund,
|
|
969
|
+
freq,
|
|
970
|
+
current_index,
|
|
971
|
+
len_abundance,
|
|
972
|
+
peak_height_diff,
|
|
973
|
+
)
|
|
974
|
+
)
|
|
975
|
+
if mz_exp_centroid:
|
|
976
|
+
try:
|
|
977
|
+
peak_resolving_power = self.calculate_resolving_power(
|
|
978
|
+
abund, mass, current_index
|
|
979
|
+
)
|
|
980
|
+
except IndexError:
|
|
981
|
+
print("index error, skipping peak")
|
|
982
|
+
continue
|
|
983
|
+
|
|
984
|
+
s2n = intes_centr / self.baseline_noise_std
|
|
985
|
+
self.add_mspeak(
|
|
986
|
+
self.polarity,
|
|
987
|
+
mz_exp_centroid,
|
|
988
|
+
abund[current_index],
|
|
989
|
+
peak_resolving_power,
|
|
990
|
+
s2n,
|
|
991
|
+
peak_indexes,
|
|
992
|
+
exp_freq=freq_centr,
|
|
993
|
+
ms_parent=self,
|
|
994
|
+
)
|