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,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
|