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.
- msfiddle/__init__.py +44 -0
- msfiddle/config/fiddle_tcn_casmi.yml +113 -0
- msfiddle/config/fiddle_tcn_demo.yml +121 -0
- msfiddle/config/fiddle_tcn_demo_wo_da.yml +121 -0
- msfiddle/config/fiddle_tcn_embl.yml +100 -0
- msfiddle/config/fiddle_tcn_orbitrap.yml +163 -0
- msfiddle/config/fiddle_tcn_qtof.yml +196 -0
- msfiddle/dataset.py +152 -0
- msfiddle/demo/input_msms.mgf +491 -0
- msfiddle/demo/output_buddy.csv +22 -0
- msfiddle/demo/output_fiddle.csv +22 -0
- msfiddle/demo/output_fiddle_all.csv +22 -0
- msfiddle/demo/output_sirius.csv +22 -0
- msfiddle/download.py +194 -0
- msfiddle/main.py +356 -0
- msfiddle/model_tcn.py +222 -0
- msfiddle/utils/__init__.py +40 -0
- msfiddle/utils/mol_utils.py +138 -0
- msfiddle/utils/msms_utils.py +312 -0
- msfiddle/utils/pkl_utils.py +372 -0
- msfiddle/utils/refine_utils.py +246 -0
- msfiddle-0.1.0.dist-info/METADATA +151 -0
- msfiddle-0.1.0.dist-info/RECORD +27 -0
- msfiddle-0.1.0.dist-info/WHEEL +5 -0
- msfiddle-0.1.0.dist-info/entry_points.txt +4 -0
- msfiddle-0.1.0.dist-info/licenses/LICENSE +661 -0
- msfiddle-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -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
|