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.
Files changed (171) hide show
  1. corems/__init__.py +63 -0
  2. corems/chroma_peak/__init__.py +0 -0
  3. corems/chroma_peak/calc/ChromaPeakCalc.py +480 -0
  4. corems/chroma_peak/calc/__init__.py +0 -0
  5. corems/chroma_peak/calc/subset.py +196 -0
  6. corems/chroma_peak/factory/__init__.py +0 -0
  7. corems/chroma_peak/factory/chroma_peak_classes.py +1178 -0
  8. corems/encapsulation/__init__.py +0 -0
  9. corems/encapsulation/constant.py +1283 -0
  10. corems/encapsulation/factory/__init__.py +0 -0
  11. corems/encapsulation/factory/parameters.py +392 -0
  12. corems/encapsulation/factory/processingSetting.py +1244 -0
  13. corems/encapsulation/input/__init__.py +0 -0
  14. corems/encapsulation/input/parameter_from_json.py +579 -0
  15. corems/encapsulation/output/__init__.py +0 -0
  16. corems/encapsulation/output/parameter_to_dict.py +142 -0
  17. corems/encapsulation/output/parameter_to_json.py +332 -0
  18. corems/mass_spectra/__init__.py +0 -0
  19. corems/mass_spectra/calc/GC_Calc.py +84 -0
  20. corems/mass_spectra/calc/GC_Deconvolution.py +558 -0
  21. corems/mass_spectra/calc/GC_RI_Calibration.py +44 -0
  22. corems/mass_spectra/calc/MZSearch.py +249 -0
  23. corems/mass_spectra/calc/SignalProcessing.py +656 -0
  24. corems/mass_spectra/calc/__init__.py +0 -0
  25. corems/mass_spectra/calc/lc_calc.py +5790 -0
  26. corems/mass_spectra/calc/lc_calc_operations.py +1127 -0
  27. corems/mass_spectra/factory/GC_Class.py +791 -0
  28. corems/mass_spectra/factory/__init__.py +0 -0
  29. corems/mass_spectra/factory/chromat_data.py +51 -0
  30. corems/mass_spectra/factory/lc_class.py +2721 -0
  31. corems/mass_spectra/input/__init__.py +0 -0
  32. corems/mass_spectra/input/andiNetCDF.py +200 -0
  33. corems/mass_spectra/input/boosterHDF5.py +216 -0
  34. corems/mass_spectra/input/brukerSolarix.py +153 -0
  35. corems/mass_spectra/input/brukerSolarix_utils.py +73 -0
  36. corems/mass_spectra/input/corems_hdf5.py +1710 -0
  37. corems/mass_spectra/input/massList.py +133 -0
  38. corems/mass_spectra/input/mzml.py +668 -0
  39. corems/mass_spectra/input/parserbase.py +239 -0
  40. corems/mass_spectra/input/rawFileReader.py +1839 -0
  41. corems/mass_spectra/output/__init__.py +0 -0
  42. corems/mass_spectra/output/export.py +2800 -0
  43. corems/mass_spectrum/__init__.py +0 -0
  44. corems/mass_spectrum/calc/AutoRecalibration.py +237 -0
  45. corems/mass_spectrum/calc/Calibration.py +602 -0
  46. corems/mass_spectrum/calc/CalibrationCalc.py +253 -0
  47. corems/mass_spectrum/calc/KendrickGroup.py +239 -0
  48. corems/mass_spectrum/calc/MassErrorPrediction.py +436 -0
  49. corems/mass_spectrum/calc/MassSpectrumCalc.py +303 -0
  50. corems/mass_spectrum/calc/MeanResolvingPowerFilter.py +212 -0
  51. corems/mass_spectrum/calc/NoiseCalc.py +371 -0
  52. corems/mass_spectrum/calc/NoiseCalc_Bayes.py +93 -0
  53. corems/mass_spectrum/calc/PeakPicking.py +994 -0
  54. corems/mass_spectrum/calc/__init__.py +0 -0
  55. corems/mass_spectrum/factory/MassSpectrumClasses.py +1753 -0
  56. corems/mass_spectrum/factory/__init__.py +0 -0
  57. corems/mass_spectrum/input/__init__.py +0 -0
  58. corems/mass_spectrum/input/baseClass.py +531 -0
  59. corems/mass_spectrum/input/boosterHDF5.py +161 -0
  60. corems/mass_spectrum/input/coremsHDF5.py +475 -0
  61. corems/mass_spectrum/input/massList.py +402 -0
  62. corems/mass_spectrum/input/numpyArray.py +133 -0
  63. corems/mass_spectrum/output/__init__.py +0 -0
  64. corems/mass_spectrum/output/export.py +841 -0
  65. corems/molecular_formula/__init__.py +0 -0
  66. corems/molecular_formula/calc/MolecularFormulaCalc.py +885 -0
  67. corems/molecular_formula/calc/__init__.py +0 -0
  68. corems/molecular_formula/factory/MolecularFormulaFactory.py +895 -0
  69. corems/molecular_formula/factory/__init__.py +0 -0
  70. corems/molecular_formula/input/__init__.py +0 -0
  71. corems/molecular_formula/input/masslist_ref.py +355 -0
  72. corems/molecular_id/__init__.py +0 -0
  73. corems/molecular_id/calc/ClusterFilter.py +251 -0
  74. corems/molecular_id/calc/MolecularFilter.py +122 -0
  75. corems/molecular_id/calc/SpectralSimilarity.py +632 -0
  76. corems/molecular_id/calc/__init__.py +0 -0
  77. corems/molecular_id/calc/math_distance.py +1637 -0
  78. corems/molecular_id/data/FAMES_REF.msp +980 -0
  79. corems/molecular_id/data/PNNLMetV20191015.msp +157267 -0
  80. corems/molecular_id/factory/EI_SQL.py +650 -0
  81. corems/molecular_id/factory/MolecularLookupTable.py +914 -0
  82. corems/molecular_id/factory/__init__.py +0 -0
  83. corems/molecular_id/factory/classification.py +884 -0
  84. corems/molecular_id/factory/lipid_molecular_metadata.py +50 -0
  85. corems/molecular_id/factory/molecularSQL.py +827 -0
  86. corems/molecular_id/factory/spectrum_search_results.py +119 -0
  87. corems/molecular_id/input/__init__.py +0 -0
  88. corems/molecular_id/input/nistMSI.py +148 -0
  89. corems/molecular_id/search/__init__.py +0 -0
  90. corems/molecular_id/search/compoundSearch.py +214 -0
  91. corems/molecular_id/search/database_interfaces.py +1527 -0
  92. corems/molecular_id/search/findOxygenPeaks.py +330 -0
  93. corems/molecular_id/search/lcms_spectral_search.py +348 -0
  94. corems/molecular_id/search/molecularFormulaSearch.py +1117 -0
  95. corems/molecular_id/search/priorityAssignment.py +723 -0
  96. corems/ms_peak/__init__.py +0 -0
  97. corems/ms_peak/calc/MSPeakCalc.py +1010 -0
  98. corems/ms_peak/calc/__init__.py +0 -0
  99. corems/ms_peak/factory/MSPeakClasses.py +542 -0
  100. corems/ms_peak/factory/__init__.py +0 -0
  101. corems/transient/__init__.py +0 -0
  102. corems/transient/calc/TransientCalc.py +362 -0
  103. corems/transient/calc/__init__.py +0 -0
  104. corems/transient/factory/TransientClasses.py +457 -0
  105. corems/transient/factory/__init__.py +0 -0
  106. corems/transient/input/__init__.py +0 -0
  107. corems/transient/input/brukerSolarix.py +461 -0
  108. corems/transient/input/midasDatFile.py +172 -0
  109. corems-4.0.0.dist-info/METADATA +475 -0
  110. corems-4.0.0.dist-info/RECORD +171 -0
  111. corems-4.0.0.dist-info/WHEEL +5 -0
  112. corems-4.0.0.dist-info/licenses/LICENSE +22 -0
  113. corems-4.0.0.dist-info/top_level.txt +4 -0
  114. examples/archive/scripts/CoreMS_tutorial.py +94 -0
  115. examples/archive/scripts/DI HR-MS Halogens Bruker.py +196 -0
  116. examples/archive/scripts/DI HR-MS MassList.py +385 -0
  117. examples/archive/scripts/GC-MS MetabRef.py +213 -0
  118. examples/archive/scripts/GC-MS NetCDF.py +217 -0
  119. examples/archive/scripts/HR-MS Thermo Raw 21T.py +136 -0
  120. examples/archive/scripts/LC-ICPMS_metal_peaks.py +297 -0
  121. examples/archive/scripts/LCMS-Thermo.py +460 -0
  122. examples/archive/scripts/LCMS_isotopes.py +283 -0
  123. examples/archive/scripts/MSParams_example.py +21 -0
  124. examples/archive/scripts/Molecular Formula Data Aggreation.py +84 -0
  125. examples/archive/scripts/Single Mz Search.py +69 -0
  126. examples/test_notebooks.py +145 -0
  127. ext_lib/ChemstationMSFileReader.dll +0 -0
  128. ext_lib/ChemstationMSFileReader.xml +126 -0
  129. ext_lib/RawFileReaderLicense.doc +0 -0
  130. ext_lib/ThermoFisher.CommonCore.BackgroundSubtraction.dll +0 -0
  131. ext_lib/ThermoFisher.CommonCore.BackgroundSubtraction.xml +2307 -0
  132. ext_lib/ThermoFisher.CommonCore.Data.dll +0 -0
  133. ext_lib/ThermoFisher.CommonCore.Data.xml +28974 -0
  134. ext_lib/ThermoFisher.CommonCore.MassPrecisionEstimator.dll +0 -0
  135. ext_lib/ThermoFisher.CommonCore.MassPrecisionEstimator.xml +241 -0
  136. ext_lib/ThermoFisher.CommonCore.RawFileReader.dll +0 -0
  137. ext_lib/ThermoFisher.CommonCore.RawFileReader.xml +31174 -0
  138. ext_lib/__init__.py +0 -0
  139. ext_lib/dotnet/OpenMcdf.Extensions.dll +0 -0
  140. ext_lib/dotnet/OpenMcdf.dll +0 -0
  141. ext_lib/dotnet/OpenMcdf.xml +1154 -0
  142. ext_lib/dotnet/System.IO.FileSystem.AccessControl.dll +0 -0
  143. ext_lib/dotnet/System.IO.FileSystem.AccessControl.xml +506 -0
  144. ext_lib/dotnet/System.Security.AccessControl.dll +0 -0
  145. ext_lib/dotnet/System.Security.AccessControl.xml +2043 -0
  146. ext_lib/dotnet/System.Security.Principal.Windows.dll +0 -0
  147. ext_lib/dotnet/System.Security.Principal.Windows.xml +1011 -0
  148. ext_lib/dotnet/ThermoFisher.CommonCore.BackgroundSubtraction.dll +0 -0
  149. ext_lib/dotnet/ThermoFisher.CommonCore.BackgroundSubtraction.xml +2307 -0
  150. ext_lib/dotnet/ThermoFisher.CommonCore.Data.dll +0 -0
  151. ext_lib/dotnet/ThermoFisher.CommonCore.Data.xml +29148 -0
  152. ext_lib/dotnet/ThermoFisher.CommonCore.MassPrecisionEstimator.dll +0 -0
  153. ext_lib/dotnet/ThermoFisher.CommonCore.MassPrecisionEstimator.xml +241 -0
  154. ext_lib/dotnet/ThermoFisher.CommonCore.RawFileReader.dll +0 -0
  155. ext_lib/dotnet/ThermoFisher.CommonCore.RawFileReader.xml +31492 -0
  156. ext_lib/version +18 -0
  157. support_code/atom_parsers/AtomsDescription.py +359 -0
  158. support_code/atom_parsers/CreateAtomsDescription.py +56 -0
  159. support_code/nmdc/filefinder.py +90 -0
  160. support_code/nmdc/lipidomics/lipidomics_workflow.py +748 -0
  161. support_code/nmdc/lipidomics/manifest_examples.py +49 -0
  162. support_code/nmdc/metabolomics/gcms_workflow.py +158 -0
  163. support_code/nmdc/metabolomics/lcms_metabolomics_targeted_search.py +59 -0
  164. support_code/nmdc/metabolomics/lcms_metabolomics_workflow.py +248 -0
  165. support_code/nmdc/metabolomics/metabolomics_collection.py +628 -0
  166. support_code/nmdc/metadata/dms_api.py +42 -0
  167. support_code/nmdc/nom/archived_scripts/nmdc_metadata_gen.py +288 -0
  168. support_code/nmdc/nom/archived_scripts/nom_grow_workflow.py +209 -0
  169. support_code/nmdc/nom/nom_workflow.py +312 -0
  170. support_code/windows_only_importers/BrukerCompassXtract.py +180 -0
  171. support_code/windows_only_importers/ThermoMSFileReader.py +405 -0
@@ -0,0 +1,303 @@
1
+ __author__ = "Yuri E. Corilo"
2
+ __date__ = "Jun 27, 2019"
3
+
4
+ from numpy import power, multiply, sqrt, array, mean
5
+ from corems.mass_spectrum.calc.NoiseCalc import NoiseThresholdCalc
6
+ from corems.mass_spectrum.calc.PeakPicking import PeakPicking
7
+
8
+
9
+ class MassSpecCalc(PeakPicking, NoiseThresholdCalc):
10
+ """Class for Mass Spectrum Calculations
11
+
12
+ Class including numerical calculations related to mass spectrum class
13
+ Inherited PeakPicking and NoiseThresholdCalc ensuring its methods are
14
+ available to the instantiated mass spectrum class object
15
+
16
+ Parameters
17
+ -------
18
+ mass_spectrum : MassSpectrum
19
+ CoreMS mass spectrum object
20
+
21
+ Attributes
22
+ --------
23
+ All Attributes are derivative from the MassSpecBase Class
24
+
25
+ Methods
26
+ --------
27
+ * check_mspeaks().
28
+ Check if the mspeaks attribute is populated
29
+ * sort_by_abundance().
30
+ Sort the mspeaks by abundance
31
+ * percentile_assigned(report_error=False).
32
+ Calculate the percentage of assigned peaks
33
+ * resolving_power_calc(B, T).
34
+ Calculate the resolving power
35
+ * number_average_molecular_weight(profile=False).
36
+ Calculate the number average molecular weight
37
+ * weight_average_molecular_weight(profile=False).
38
+ Calculate the weight average molecular weight
39
+ """
40
+
41
+ def percentage_assigned(self, report_error: bool = False, mute_output: bool = False):
42
+ """Percentage of peaks which are assigned
43
+
44
+ Calculates the percentage and relative abundance of assigned peaks in the spectrum.
45
+ Includes protection against division by zero with explicit handling of edge cases.
46
+
47
+ Parameters
48
+ -----------
49
+ report_error: bool, optional
50
+ Report the RMS error of the assigned peaks. Default is False.
51
+ mute_output: bool, optional
52
+ Override the verbose setting. Default is False.
53
+ If True, the function will silence results
54
+
55
+ Returns
56
+ -------
57
+ tuple
58
+ If report_error is False:
59
+ (assigned_count, unassigned_count, total_percent, total_relative_abundance)
60
+ If report_error is True:
61
+ (assigned_count, unassigned_count, total_percent, total_relative_abundance, rms_error)
62
+ where rms_error is None if no assigned peaks exist
63
+
64
+ Notes
65
+ -----
66
+ Edge cases are handled with explicit reporting:
67
+ - If no peaks detected: returns (0, 0, 0.0, 0.0[, None]) with message
68
+ - If no abundance data: returns (i, j, 0.0, 0.0[, None]) with message
69
+ - If no assigned peaks but peaks exist: returns with rms_error=None and explanatory message
70
+ """
71
+ verbose = self.parameters.mass_spectrum.verbose_processing
72
+ assign_abun = 0
73
+ not_assign_abun = 0
74
+ i = 0
75
+ j = 0
76
+ if report_error:
77
+ error = []
78
+ for mspeak in self.sort_by_abundance():
79
+ if mspeak.is_assigned:
80
+ i += 1
81
+ assign_abun += mspeak.abundance
82
+ if report_error:
83
+ error.append(mspeak.best_molecular_formula_candidate.mz_error)
84
+
85
+ else:
86
+ j += 1
87
+ not_assign_abun += mspeak.abundance
88
+
89
+ # Protect against division by zero
90
+ total_peaks = i + j
91
+ total_abundance = assign_abun + not_assign_abun
92
+
93
+ # Handle edge cases
94
+ if total_peaks == 0:
95
+ if verbose and not mute_output:
96
+ print("No peaks detected in spectrum")
97
+ if report_error:
98
+ return i, j, 0.0, 0.0, None
99
+ else:
100
+ return i, j, 0.0, 0.0
101
+
102
+ if total_abundance == 0:
103
+ if verbose and not mute_output:
104
+ print("No abundance data detected in spectrum")
105
+ if report_error:
106
+ return i, j, 0.0, 0.0, None
107
+ else:
108
+ return i, j, 0.0, 0.0
109
+
110
+ total_percent = (i / total_peaks * 100) if total_peaks > 0 else 0.0
111
+ total_relative_abundance = (assign_abun / total_abundance * 100) if total_abundance > 0 else 0.0
112
+
113
+ if report_error:
114
+ rms_error = None
115
+ if i > 0:
116
+ rms_error = sqrt(mean(array(error) ** 2))
117
+ if verbose and not mute_output:
118
+ if i == 0:
119
+ print(
120
+ "No assigned peaks detected - cannot calculate RMS error. %i unassigned peaks, total = %.2f %%, relative abundance = %.2f %%"
121
+ % (j, total_percent, total_relative_abundance)
122
+ )
123
+ else:
124
+ print(
125
+ "%i assigned peaks and %i unassigned peaks, total = %.2f %%, relative abundance = %.2f %%, RMS error (best candidate) (ppm) = %.3f"
126
+ % (i, j, total_percent, total_relative_abundance, rms_error)
127
+ )
128
+ return i, j, total_percent, total_relative_abundance, rms_error
129
+
130
+ else:
131
+ if verbose and not mute_output:
132
+ print(
133
+ "%i assigned peaks and %i unassigned peaks , total = %.2f %%, relative abundance = %.2f %%"
134
+ % (
135
+ i,
136
+ j,
137
+ total_percent,
138
+ total_relative_abundance,
139
+ )
140
+ )
141
+ return i, j, total_percent, total_relative_abundance
142
+
143
+ def percentile_assigned(self, report_error: bool = False, mute_output: bool = False):
144
+ """Deprecated: Use percentage_assigned() instead.
145
+
146
+ This method is deprecated and will be removed in a future version.
147
+ The function returns a percentage, not a percentile, so the name has been corrected.
148
+
149
+ Parameters
150
+ -----------
151
+ report_error: bool, optional
152
+ Report the error of the assigned peaks. Default is False.
153
+ mute_output: bool, optional
154
+ Override the verbose setting. Default is False.
155
+
156
+ Returns
157
+ -------
158
+ tuple
159
+ Refer to percentage_assigned() for return value details.
160
+ """
161
+ import warnings
162
+ warnings.warn(
163
+ "percentile_assigned() is deprecated and will be removed in a future version. "
164
+ "Use percentage_assigned() instead, as the function returns a percentage, not a percentile.",
165
+ DeprecationWarning,
166
+ stacklevel=2
167
+ )
168
+ return self.percentage_assigned(report_error=report_error, mute_output=mute_output)
169
+
170
+ def resolving_power_calc(self, B: float, T: float):
171
+ """Calculate the theoretical resolving power
172
+
173
+ Calls on the MSPeak object function to calculate the resolving power of a peak, this calcs for all peaks in a spectrum.
174
+
175
+ Parameters
176
+ -----------
177
+ T : float
178
+ transient time
179
+ B : float
180
+ Magnetic Filed Strength (Tesla)
181
+
182
+ References
183
+ ----------
184
+ 1. Marshall et al. (Mass Spectrom Rev. 1998 Jan-Feb;17(1):1-35.)
185
+ DOI: 10.1002/(SICI)1098-2787(1998)17:1<1::AID-MAS1>3.0.CO;2-K
186
+
187
+ """
188
+
189
+ self.check_mspeaks()
190
+ return array([mspeak.resolving_power_calc(B, T) for mspeak in self.mspeaks])
191
+
192
+ def _f_to_mz(self):
193
+ """Ledford equation for converting frequency(Hz) to m/z
194
+
195
+ Returns
196
+ ----------
197
+ mz_domain : numpy array
198
+ m/z domain after conversion from frequency
199
+ """
200
+ Aterm, Bterm, Cterm = self.Aterm, self.Bterm, self.Cterm
201
+ # Check if the Bterm of Ledford equation scales with the ICR trap voltage or not then Bterm = Bterm*trap_voltage
202
+
203
+ if Cterm == 0:
204
+ if Bterm == 0:
205
+ # uncalibrated data
206
+ mz_domain = Aterm / self.freq_exp_profile
207
+
208
+ else:
209
+ mz_domain = (Aterm / (self.freq_exp_profile)) + (
210
+ Bterm / power((self.freq_exp_profile), 2)
211
+ )
212
+
213
+ # @will I need you insight here, not sure what is the inverted ledford equation that Bruker refers to
214
+ else:
215
+ mz_domain = (
216
+ (Aterm / self.freq_exp_profile)
217
+ + (Bterm / power(self.freq_exp_profile, 2))
218
+ + Cterm
219
+ )
220
+
221
+ return mz_domain
222
+
223
+ def _f_to_mz_bruker(self):
224
+ """Frequency to m/z conversion (Bruker)
225
+ Bruker equations for converting frequency (Hz) to m/z,
226
+ nOmega acquisition is not yet implemented here.
227
+ However, nOmega should work for commerical Bruker 2xR systems as A Term is automatically defined for 2X or 1X by the instrument
228
+
229
+
230
+ Returns
231
+ ----------
232
+ numpy.array(float)
233
+ m/z domain after conversion from frequency
234
+ """
235
+ Aterm, Bterm, Cterm = self.Aterm, self.Bterm, self.Cterm
236
+ # Check if the Bterm of Ledford equation scales with the ICR trap voltage or not then Bterm = Bterm*trap_voltage
237
+ if Cterm == 0:
238
+ if Bterm == 0:
239
+ # uncalibrated data
240
+ return Aterm / self.freq_exp_profile
241
+
242
+ else:
243
+ # calc2
244
+ return Aterm / (self.freq_exp_profile + Bterm)
245
+
246
+ # @will I need you insight here, not sure what is the inverted ledford equation that Bruker refers to
247
+ else:
248
+ diff = Aterm * Aterm
249
+
250
+ # this sign(diff + 4) changes on older aquistion software
251
+ diff = diff + 4 * Cterm * (self.freq_exp_profile - Bterm)
252
+ diff = sqrt(diff)
253
+ diff = -Aterm + diff
254
+ # calc3
255
+ return (2 * Cterm) / diff
256
+ return diff / 2 * (self.freq_exp_profile - Bterm)
257
+
258
+ def number_average_molecular_weight(self, profile: bool = False):
259
+ """Average molecular weight calculation
260
+
261
+ Parameters
262
+ ----------
263
+ profile : bool, optional
264
+ is data profile or centroid mode. The default is False (e.g. Centroid data)
265
+
266
+ Returns
267
+ -------
268
+ float
269
+ The average molecular weight.
270
+
271
+ """
272
+ # mode is profile or centroid data
273
+ if profile:
274
+ a = multiply(self.mz_exp_profile, self.abundance_profile)
275
+ b = self.abundance_profile
276
+ return a.sum() / b.sum()
277
+
278
+ else:
279
+ return sum(self.mz_exp * self.abundance) / sum(self.abundance)
280
+
281
+ def weight_average_molecular_weight(self, profile: bool = False):
282
+ """
283
+ Weighted Average molecular weight calculation
284
+
285
+
286
+ Returns
287
+ -------
288
+ float
289
+ The weight average molecular weight.
290
+
291
+ """
292
+
293
+ # implement from MassSpectralPeaks objs
294
+
295
+ if profile:
296
+ a = multiply(power(self.mz_exp_profile, 2), self.abundance_profile)
297
+ b = self.mz_exp_profile * self.abundance_profile
298
+ return a.sum() / b.sum()
299
+
300
+ else:
301
+ return sum(power(self.mz_exp, 2) * self.abundance) / sum(
302
+ self.mz_exp * self.abundance
303
+ )
@@ -0,0 +1,212 @@
1
+ """
2
+ Created on June 2nd 2023
3
+
4
+ @author: Will Kew
5
+
6
+ Module for mean resolving power filtration
7
+ Based upon the work in:
8
+
9
+ Kanawati, B, Bader, TM, Wanczek, K-P, Li, Y, Schmitt-Kopplin, P.
10
+ Fourier transform (FT)-artifacts and power-function resolution filter in Fourier transform mass spectrometry.
11
+ Rapid Commun Mass Spectrom. 2017; 31: 1607- 1615. https://doi.org/10.1002/rcm.7940
12
+
13
+ Calculates a m/z normalised resolving power, fits a gaussian distribution to this, and then filters out peaks which are outside of the user defined number of standard deviations
14
+
15
+
16
+ """
17
+
18
+ import warnings
19
+ from lmfit.models import GaussianModel
20
+ import seaborn as sns
21
+ import pandas as pd
22
+ import numpy as np
23
+ import matplotlib.pyplot as plt
24
+
25
+
26
+ class MeanResolvingPowerFilter:
27
+ """Class for for mean resolving power filtration.
28
+
29
+ This module implements a mean resolving power filter based on the work described [1]
30
+
31
+ The MeanResolvingPowerFilter class provides methods to calculate the m/z normalized resolving power, fit a Gaussian distribution to it, and filter out peaks that are outside of the user-defined number of standard deviations.
32
+
33
+ Attributes
34
+ -------
35
+ mass_spectrum (object): The mass spectrum object.
36
+ ndeviations (int): The number of standard deviations used for filtering.
37
+ plot (bool): Flag indicating whether to plot the results.
38
+ guess_pars (bool): Flag indicating whether to guess the parameters for the Gaussian model.
39
+ return_rps (bool): Flag indicating whether to return the calculated resolving powers, will return the NORMALISED resolving power, the mean, and the standard deviation.
40
+
41
+ Methods
42
+ ------
43
+ * extract_peaks(): Extracts the peaks from the mass spectrum.
44
+ * normalise_rps(tmpdf_ms): Normalizes the resolving powers to be independent of m/z.
45
+ * calculate_distribution(tmpdf_ms): Calculates the distribution of the resolving powers.
46
+ * create_index_list_to_remove(tmpdf_ms, rps_thresh): Creates an index list of peaks to remove based on the calculated thresholds.
47
+ * main(): Executes the main filtering process and returns the index list of peaks to remove.
48
+
49
+ References
50
+ ----------
51
+ 1. Kanawati, B, Bader, TM, Wanczek, K-P, Li, Y, Schmitt-Kopplin, P.
52
+ Fourier transform (FT)-artifacts and power-function resolution filter in Fourier transform mass spectrometry.
53
+ Rapid Commun Mass Spectrom. 2017; 31: 1607- 1615. https://doi.org/10.1002/rcm.7940
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ mass_spectrum,
59
+ ndeviations: float = 3,
60
+ plot: bool = False,
61
+ guess_pars: bool = False,
62
+ return_rps: bool = False,
63
+ ):
64
+ # we dont want the assignments made in this exploratory class to copy to the original object, so we make a copy of it.
65
+ # Possible future task - make mass spectrum base class copyable...
66
+ # TODO see if there is redundancy in the AutoRecalibration function we can minimise here?
67
+ # self.mass_spectrum
68
+ self.mass_spectrum = mass_spectrum
69
+ self.plot = plot
70
+ self.ndeviations = ndeviations
71
+ self.guess_pars = guess_pars
72
+ self.return_rps = return_rps
73
+
74
+ def extract_peaks(self):
75
+ """Extracts the peaks from the mass spectrum.
76
+
77
+ Returns
78
+ ----------
79
+ tmpdf_ms : Pandas DataFrame
80
+ A DataFrame containing the extracted peaks.
81
+ """
82
+ ids = []
83
+ mzs = []
84
+ rps = []
85
+ for mspeak in self.mass_spectrum.mspeaks:
86
+ ids.append(mspeak.index)
87
+ mzs.append(mspeak.mz_exp)
88
+ rps.append(mspeak.resolving_power)
89
+ mzs = np.array(mzs)
90
+ rps = np.array(rps)
91
+
92
+ tmpdf_ms = pd.DataFrame(index=ids, columns=["mz", "rp", "crp"])
93
+ tmpdf_ms["mz"] = mzs
94
+ tmpdf_ms["rp"] = rps
95
+ return tmpdf_ms
96
+
97
+ def normalise_rps(self, tmpdf_ms):
98
+ """Normalizes the resolving powers to be independent of m/z.
99
+
100
+ Parameters
101
+ ------
102
+ tmpdf_ms : Pandas DataFrame
103
+ A DataFrame containing the extracted peaks.
104
+
105
+ Returns
106
+ --------
107
+ tmpdf_ms : Pandas DataFrame
108
+ A DataFrame with the resolving powers normalized.
109
+ """
110
+
111
+ if self.mass_spectrum.analyzer == "ICR":
112
+ tmpdf_ms["crp"] = tmpdf_ms["rp"] * np.sqrt(tmpdf_ms["mz"] ** 2)
113
+ else:
114
+ warnings.warn(
115
+ f"Analyzer type {self.mass_spectrum.analyzer} not yet supported.",
116
+ UserWarning,
117
+ )
118
+ return tmpdf_ms
119
+
120
+ def calculate_distribution(self, tmpdf_ms):
121
+ """Calculates the distribution of the resolving powers.
122
+
123
+ Parameters
124
+ --------
125
+ tmpdf_ms : Pandas DataFrame
126
+ A DataFrame containing the extracted peaks with normalized resolving powers.
127
+
128
+ Returns
129
+ --------
130
+ rps_thresh : list
131
+ A list of the calculated thresholds for filtering.
132
+ """
133
+
134
+ # Use Seaborn to create a KDE of the normalised resolving powers
135
+ rps = sns.kdeplot(tmpdf_ms["crp"])
136
+ rps_data = rps.get_lines()[0].get_data()
137
+ tmpdf = pd.Series(index=rps_data[0], data=rps_data[1])
138
+ rps_apex_ppm = tmpdf.idxmax()
139
+ rps_apex_val = tmpdf.max()
140
+ plt.close(rps.figure)
141
+ plt.close("all")
142
+
143
+ # Use LMFIT to create a gaussian model of the distribution
144
+ lmmodel = GaussianModel()
145
+ lmpars = lmmodel.guess(rps_data[1], x=rps_data[0])
146
+ if self.guess_pars:
147
+ lmpars["sigma"].value = rps_data[0][-1] * 0.01
148
+ lmpars["center"].value = rps_apex_ppm
149
+ lmpars["amplitude"].value = rps_apex_val
150
+ lmout = lmmodel.fit(rps_data[1], lmpars, x=rps_data[0])
151
+
152
+ if self.plot:
153
+ fig, ax = plt.subplots(figsize=(8, 4))
154
+ lmout.plot_fit(
155
+ ax=ax, data_kws={"color": "tab:blue"}, fit_kws={"color": "tab:red"}
156
+ )
157
+ ax.set_xlabel("Normalised Resolving Power")
158
+ ax.set_ylabel("Density")
159
+ plt.legend(facecolor="white", framealpha=0)
160
+
161
+ mean_res = lmout.best_values["center"]
162
+ std_res = lmout.best_values["sigma"]
163
+ fwhm_res = std_res * np.sqrt(8 * np.log(2))
164
+
165
+ ndevs = self.ndeviations / 2
166
+ rps_thresh = [mean_res - (fwhm_res * ndevs), mean_res + (fwhm_res * ndevs)]
167
+ if self.return_rps:
168
+ return rps_thresh, mean_res, std_res
169
+ return rps_thresh
170
+
171
+ def create_index_list_to_remove(self, tmpdf_ms, rps_thresh: list):
172
+ """Creates an index list of peaks to remove based on the calculated thresholds.
173
+
174
+ Parameters
175
+ ---------
176
+ tmpdf_ms : Pandas DataFrame
177
+ A DataFrame containing the extracted peaks with normalized resolving powers.
178
+ rps_thresh : list
179
+ A list of the calculated thresholds for filtering.
180
+
181
+ Returns
182
+ ----------
183
+ index_to_keep :list
184
+ A list of indices of peaks to keep.
185
+ """
186
+ # Subset the list of mspeaks to only the ones to keep, return an index list which can be passed back to the main
187
+
188
+ tmpdf_ms = tmpdf_ms[
189
+ (tmpdf_ms["crp"] < min(rps_thresh)) | (tmpdf_ms["crp"] > max(rps_thresh))
190
+ ]
191
+ index_to_keep = list(tmpdf_ms.index)
192
+ return index_to_keep
193
+
194
+ def main(self):
195
+ """Executes the main filtering process and returns the index list of peaks to remove.
196
+
197
+ Returns
198
+ --------
199
+ index_to_remove : list
200
+ A list of indices of peaks to remove.
201
+ """
202
+ tmpdf_ms = self.extract_peaks()
203
+ tmpdf_ms = self.normalise_rps(tmpdf_ms)
204
+ if self.return_rps:
205
+ rps_thresh, mean_res, std_res = self.calculate_distribution(tmpdf_ms)
206
+ else:
207
+ rps_thresh = self.calculate_distribution(tmpdf_ms)
208
+ index_to_remove = self.create_index_list_to_remove(tmpdf_ms, rps_thresh)
209
+ if self.return_rps:
210
+ return index_to_remove, rps_thresh, mean_res, std_res
211
+ else:
212
+ return index_to_remove