msfiddle 0.1.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.
@@ -0,0 +1,196 @@
1
+ # ------------------------
2
+ # QTOF train & validation
3
+ # ------------------------
4
+ agilent_qtof:
5
+ instrument_type: ESI-QTOF
6
+ instrument: ['Unknown']
7
+ ms_level: '2'
8
+ atom_type: ['C', 'O', 'N', 'H', 'P', 'S', 'F', 'Cl', 'B', 'Br', 'I', 'Na', 'K']
9
+ precursor_type: ['[M+H]+', '[M-H]-']
10
+ min_mz: 50
11
+ max_mz: 1500
12
+ min_peak_num: 5
13
+ max_atom_num: 300
14
+ min_atom_num: 10
15
+ ppm_tolerance: 10
16
+
17
+ nist20_qtof:
18
+ instrument_type: Q-TOF
19
+ instrument: ['Agilent QTOF 6530']
20
+ ms_level: 'MS2'
21
+ atom_type: ['C', 'O', 'N', 'H', 'P', 'S', 'F', 'Cl', 'B', 'Br', 'I', 'Na', 'K']
22
+ precursor_type: ['[M+H]+', '[M+Na]+', '[2M+H]+', # +1
23
+ '[M+H-H2O]+', '[M-H2O+H]+', '[M+H-2H2O]+', '[M+H-NH3]+', '[M+H+NH3]+', '[M+NH4]+', '[M+H-CH2O2]+', '[M+H-CH4O2]+', # +1+-neutrue
24
+ '[M-H]-', '[2M-H]-', # -1
25
+ '[M-H-CO2]-', '[M-CHO2]-', '[M-H-H2O]-', '[M-H2O-H]-', # -1+-neutrue
26
+ '[M+2H]2+', ] # +2
27
+ min_mz: 50
28
+ max_mz: 1500
29
+ min_peak_num: 5
30
+ max_atom_num: 300
31
+ min_atom_num: 10
32
+ ppm_tolerance: 10
33
+
34
+ nist23_qtof:
35
+ instrument_type: Q-TOF
36
+ instrument: ['Agilent QTOF 6530']
37
+ ms_level: 'MS2'
38
+ atom_type: ['C', 'O', 'N', 'H', 'P', 'S', 'F', 'Cl', 'B', 'Br', 'I', 'Na', 'K']
39
+ precursor_type: ['[M+H]+', '[M+Na]+', '[2M+H]+', # +1
40
+ '[M+H-H2O]+', '[M-H2O+H]+', '[M+H-2H2O]+', '[M+H-NH3]+', '[M+H+NH3]+', '[M+NH4]+', '[M+H-CH2O2]+', '[M+H-CH4O2]+', # +1+-neutrue
41
+ '[M-H]-', '[2M-H]-', # -1
42
+ '[M-H-CO2]-', '[M-CHO2]-', '[M-H-H2O]-', '[M-H2O-H]-', # -1+-neutrue
43
+ '[M+2H]2+', ] # +2
44
+ min_mz: 50
45
+ max_mz: 1500
46
+ min_peak_num: 5
47
+ max_atom_num: 300
48
+ min_atom_num: 10
49
+ ppm_tolerance: 10
50
+
51
+ mona_qtof:
52
+ instrument_type: ['LC-ESI-QTOF',
53
+ 'ESI-QFT']
54
+ instrument: ['Agilent 6530 Q-TOF', 'Waters Xevo G2 Q-Tof', 'Agilent qTOF 6545', 'Agilent 6550 iFunnel']
55
+ ms_level: 'MS2'
56
+ atom_type: ['C', 'O', 'N', 'H', 'P', 'S', 'F', 'Cl', 'B', 'Br', 'I', 'Na', 'K']
57
+ precursor_type: ['[M+H]+', '[M+Na]+', '[2M+H]+', # +1
58
+ '[M+H-H2O]+', '[M-H2O+H]+', '[M+H-2H2O]+', '[M+H-NH3]+', '[M+H+NH3]+', '[M+NH4]+', '[M+H-CH2O2]+', '[M+H-CH4O2]+', # +1+-neutrue
59
+ '[M-H]-', '[2M-H]-', # -1
60
+ '[M-H-CO2]-', '[M-CHO2]-', '[M-H-H2O]-', '[M-H2O-H]-', # -1+-neutrue
61
+ '[M+2H]2+', ] # +2
62
+ min_mz: 50
63
+ max_mz: 1500
64
+ min_peak_num: 5
65
+ max_atom_num: 300
66
+ min_atom_num: 10
67
+ ppm_tolerance: 10
68
+
69
+ waters_qtof:
70
+ instrument_type: ['QTOF']
71
+ instrument: ['Waters QTOF']
72
+ atom_type: ['C', 'O', 'N', 'H', 'P', 'S', 'F', 'Cl', 'B', 'Br', 'I', 'Na', 'K']
73
+ precursor_type: ['[M+H]+', '[M-H]-']
74
+ min_mz: 50
75
+ max_mz: 1500
76
+ min_peak_num: 5
77
+ max_atom_num: 300
78
+ min_atom_num: 10
79
+ ppm_tolerance: 10
80
+
81
+ gnps_qtof:
82
+ instrument_type: ['hcd']
83
+ instrument: ['qtof', 'quadrupole']
84
+ atom_type: ['C', 'O', 'N', 'H', 'P', 'S', 'F', 'Cl', 'B', 'Br', 'I', 'Na', 'K']
85
+ precursor_type: ['[M+H]+', '[M+Na]+', '[2M+H]+', # +1
86
+ '[M+H-H2O]+', '[M-H2O+H]+', '[M+H-2H2O]+', '[M+H-NH3]+', '[M+H+NH3]+', '[M+NH4]+', '[M+H-CH2O2]+', '[M+H-CH4O2]+', # +1+-neutrue
87
+ '[M-H]-', '[2M-H]-', # -1
88
+ '[M-H-CO2]-', '[M-CHO2]-', '[M-H-H2O]-', '[M-H2O-H]-', # -1+-neutrue
89
+ '[M+2H]2+', ] # +2
90
+ min_mz: 50
91
+ max_mz: 1500
92
+ min_peak_num: 5
93
+ max_atom_num: 300
94
+ min_atom_num: 10
95
+ ppm_tolerance: 10
96
+
97
+ # ------------------------
98
+ # model settings
99
+ # ------------------------
100
+ encoding:
101
+ resolution: 0.2
102
+ max_mz: 1500
103
+ max_atom_num: 300
104
+ use_simulated_precursor_mz: False
105
+
106
+ precursor_type:
107
+ '[M+H]+': 1
108
+ '[M-H]-': 2
109
+ '[M+Na]+': 3
110
+ '[M+2H]2+': 4
111
+ '[2M+H]+': 5
112
+ '[2M-H]-': 6
113
+
114
+ type2charge:
115
+ '[M+H]+': '+1'
116
+ '[M-H]-': '-1'
117
+ '[M+Na]+': '+1'
118
+ '[M+2H]2+': '+2'
119
+ '[2M+H]+': '+1'
120
+ '[2M-H]-': '-1'
121
+
122
+ '[M+H-H2O]+': '+1'
123
+ '[M-H2O+H]+': '+1'
124
+ '[M+H-2H2O]+': '+1'
125
+ '[M+H-NH3]+': '+1'
126
+ '[M+H+NH3]+': '+1'
127
+ '[M+NH4]+': '+1'
128
+ '[M+H-CH2O2]+': '+1'
129
+ '[M+H-CH4O2]+': '+1'
130
+
131
+ '[M-H-CO2]-': '-1'
132
+ '[M-CHO2]-': '-1'
133
+ '[M-H-H2O]-': '-1'
134
+ '[M-H2O-H]+': '-1'
135
+
136
+ model:
137
+ resolution: 0.2
138
+ max_mz: 1500
139
+ padding_dim: 20 # 1500 / 0.2 = 7500; 7500 / 2**5 (# pooling layer) = 234.375; 235 * (2**5) = 7520
140
+ num_add: 6
141
+ input_channels: 1
142
+
143
+ tcn_channels: [32, 32, 64, 128, 256, 512]
144
+ tcn_dilations: [1, 2, 4, 8, 8, 8]
145
+ tcn_kernel_sizes: [45, 43, 41, 39, 37, 35]
146
+ tcn_dropout: 0.2
147
+
148
+ # metadata_dim: 16
149
+ add_embedding_dim: 4
150
+ ce_embedding_dim: 4
151
+ mass_embedding_dim: 4
152
+ embedding_dim: 512
153
+
154
+ formula_decoder_layers: [416, 208, 104, 52, 26, 13]
155
+ mass_decoder_layers: [416, 208, 104, 26, 13, 1]
156
+ atomnum_decoder_layers: [416, 208, 104, 26, 13, 1]
157
+ hcnum_decoder_layers: [416, 208, 104, 26, 13, 1]
158
+ output_dim: 13
159
+
160
+ fdr_decoder_layers: [416, 208, 104, 26, 13]
161
+
162
+ # ------------------------
163
+ # training settings
164
+ # ------------------------
165
+ train:
166
+ epochs: 200
167
+ batch_size: 512 # 128
168
+ num_workers: 8
169
+ lr: 0.002 # 0.001
170
+ weight_decay: 0.01
171
+ warmup_ratio: 0.1
172
+ early_stop_step: 10
173
+ patience: 5
174
+ noised_times: 3
175
+
176
+ train_fdr:
177
+ epochs: 100
178
+ batch_size: 128
179
+ num_workers: 8
180
+ lr: 0.0001
181
+ early_stop_step: 10
182
+
183
+ # ------------------------
184
+ # postprocessing settings
185
+ # ------------------------
186
+ post_processing:
187
+ refine_atom_type: ['C', 'O', 'N', 'H']
188
+ refine_atom_num: [5, 5, 5, -1]
189
+
190
+ mass_tolerance: 10.0 # target space
191
+ ppm_mode: True
192
+ time_out: -1 # do not limit the time
193
+ maxium_miss_atom_num: 50 # search space
194
+ top_k: 5
195
+ buddy_fdr_thr: 0.6
196
+ sirius_score_thr: -5
msfiddle/dataset.py ADDED
@@ -0,0 +1,152 @@
1
+ import re
2
+ import copy
3
+ import pickle
4
+ from pyteomics import mgf
5
+ from tqdm import tqdm
6
+ import numpy as np
7
+ from decimal import *
8
+ import pandas as pd
9
+ import math
10
+ from copy import deepcopy
11
+ import torch
12
+ from torch.utils.data import Dataset
13
+
14
+ from .utils import ATOMS_INDEX, generate_ms, parse_collision_energy, unify_precursor_type, formula_to_dict, formula_to_vector
15
+
16
+
17
+
18
+ # Used for testing
19
+ class MGFDataset(Dataset):
20
+ def __init__(self, path, encoder):
21
+ self.data = []
22
+ self.general_filter_config = {
23
+ 'min_mz': 50,
24
+ 'max_mz': 1500,
25
+ 'min_peak_num': 5,
26
+ }
27
+ self.use_simulated_precursor_mz = encoder['use_simulated_precursor_mz']
28
+ if self.use_simulated_precursor_mz:
29
+ self.precursor_mz_key = 'simulated_precursor_mz'
30
+ else:
31
+ self.precursor_mz_key = 'precursor_mz'
32
+
33
+ # read data from mgf file
34
+ spectra = mgf.read(path)
35
+ print(len(spectra), 'spectra loaded from', path)
36
+
37
+ # filter out invalid data (add the other rules if needed)
38
+ spectra, _ = self.filter_spec(spectra, self.general_filter_config, type2charge=encoder['type2charge'])
39
+ print(len(spectra), 'spectra left after filtering')
40
+
41
+ # convert mgf to pkl
42
+ self.load_mgf_spectra(spectra, encoder)
43
+ print(len(self.data), 'spectra loaded into the dataset')
44
+
45
+ def __len__(self):
46
+ return len(self.data)
47
+
48
+ def __getitem__(self, idx):
49
+ return self.data[idx]['title'], self.data[idx]['precursor_type'], self.data[idx]['spec'][:, 0], self.data[idx]['env'], self.data[idx]['neutral_add']
50
+
51
+ def filter_spec(self, spectra, general_filter_config, type2charge):
52
+ clean_spectra = []
53
+ invalid_spectra = []
54
+ for spectrum in spectra:
55
+ if not self.has_all_keys(spectrum):
56
+ print('MGFError: lacking necessary keys in mgf file, skip this spectrum')
57
+ print('expected keys in mgf: (\'title\', \'{}\', \'precursor_type\', \'collision_energy\')'.format(self.precursor_mz_key))
58
+ continue
59
+
60
+ # filter out invalid data
61
+ maxium_mz = np.max(spectrum['m/z array'])
62
+ minium_mz = np.min(spectrum['m/z array'])
63
+ if maxium_mz < general_filter_config['min_mz'] or maxium_mz > general_filter_config['max_mz'] or \
64
+ len(spectrum['m/z array']) < general_filter_config['min_peak_num']:
65
+ invalid_spectra.append(spectrum)
66
+ else:
67
+ clean_spectra.append(spectrum)
68
+ return clean_spectra, invalid_spectra
69
+
70
+ def has_all_keys(self, spec):
71
+ if self.use_simulated_precursor_mz:
72
+ keys = ['title', 'simulated_precursor_mz', 'precursor_type', 'collision_energy']
73
+ else:
74
+ keys = ['title', 'precursor_mz', 'precursor_type', 'collision_energy']
75
+ for k in keys:
76
+ if k not in spec['params'].keys():
77
+ return False
78
+ return True
79
+
80
+ def load_mgf_spectra(self, spectra, encoder):
81
+ for spectrum in spectra:
82
+ good_spec, _, _, spec_arr = generate_ms(x=spectrum['m/z array'],
83
+ y=spectrum['intensity array'],
84
+ precursor_mz=float(spectrum['params'][self.precursor_mz_key]),
85
+ resolution=encoder['resolution'],
86
+ max_mz=encoder['max_mz'],
87
+ charge=int(encoder['type2charge'][spectrum['params']['precursor_type']]))
88
+ if not good_spec:
89
+ continue
90
+
91
+ adjust_neutral_add_vec, adjust_precursor_type = self.melt_neutral_precursor(spectrum['params']['precursor_type'])
92
+
93
+ ce, nce = parse_collision_energy(ce_str=spectrum['params']['collision_energy'],
94
+ precursor_mz=float(spectrum['params'][self.precursor_mz_key]),
95
+ charge=abs(int(encoder['type2charge'][spectrum['params']['precursor_type']])))
96
+ if ce == None and nce == None: # can not process '30-50 eV'
97
+ continue
98
+
99
+ env_arr = np.array([float(spectrum['params'][self.precursor_mz_key]),
100
+ nce,
101
+ encoder['precursor_type'][adjust_precursor_type]])
102
+
103
+ na_arr = np.array(adjust_neutral_add_vec)
104
+
105
+ self.data.append({'title': spectrum['params']['title'], 'precursor_type': spectrum['params']['precursor_type'],
106
+ 'spec': spec_arr, 'env': env_arr, 'neutral_add': na_arr})
107
+
108
+ def melt_neutral_precursor(self, precursor_type): # Used for testing only
109
+ precursor_type = unify_precursor_type(precursor_type)
110
+
111
+ neutrue_list = ['CH4O2', 'CH2O2', 'H2O', 'NH3', 'CO2']
112
+ neutrue_counts = {}
113
+
114
+ # Step 1: Get the count of neutral losses/adducts
115
+ for neutrue in neutrue_list:
116
+ if neutrue in precursor_type:
117
+ pattern = r'([-+]?\d*)' + neutrue
118
+ count = re.findall(pattern, precursor_type)
119
+ if count:
120
+ count = count[0]
121
+ if count == '+': count = 1
122
+ elif count == '-': count = -1
123
+ else: count = int(count)
124
+ neutrue_counts[neutrue] = count
125
+ break # only one neutral adduct is allowed
126
+
127
+ # Step 2: Remove neutral losses/adducts from the precursor type string
128
+ pattern = r'([-+]?\d*)(?:' + '|'.join(neutrue_list) + ')'
129
+ adjust_precursor_type = re.sub(pattern, '', precursor_type)
130
+
131
+ # Step 3: Convert the neutral losses/adducts into vector
132
+ adjust_neutral_add = {}
133
+ for neutrue, count in neutrue_counts.items():
134
+ n_dict = formula_to_dict(neutrue)
135
+ for k, v in n_dict.items():
136
+ if k in adjust_neutral_add:
137
+ adjust_neutral_add[k] += v * count
138
+ else:
139
+ adjust_neutral_add[k] = v * count
140
+ adjust_neutral_add_vec = self.formula_dict_to_vector(adjust_neutral_add)
141
+
142
+ return adjust_neutral_add_vec, adjust_precursor_type
143
+
144
+ def formula_dict_to_vector(self, formula_dict):
145
+ vector = [0] * len(ATOMS_INDEX)
146
+
147
+ for atom, count in formula_dict.items():
148
+ index = ATOMS_INDEX.get(atom, None)
149
+ if index is not None:
150
+ vector[index] = int(count) if count else 1
151
+
152
+ return vector