msnetloader 0.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.
@@ -0,0 +1,6 @@
1
+
2
+ __version__ = "0.0.1"
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ ]
@@ -0,0 +1,128 @@
1
+ import torch
2
+ import numpy as np
3
+ from torch.utils.data import IterableDataset
4
+ import duckdb
5
+
6
+
7
+ class DeNovoIterableDataset(IterableDataset):
8
+
9
+ def __init__(self, parquet_path, max_peaks=150, batch_size=32,
10
+ min_consensus_support=None,
11
+ max_pep=None):
12
+
13
+ con = duckdb.connect()
14
+ self.min_consensus_support = min_consensus_support
15
+ self.max_pep = max_pep
16
+
17
+ conditions = []
18
+ params = []
19
+
20
+ if self.min_consensus_support is not None:
21
+ conditions.append("consensus_support >= ?")
22
+ params.append(self.min_consensus_support)
23
+
24
+ if self.max_pep is not None:
25
+ conditions.append("posterior_error_probability <= ?")
26
+ params.append(self.max_pep)
27
+
28
+ where_clause = ""
29
+ if conditions:
30
+ where_clause = "WHERE " + " AND ".join(conditions)
31
+
32
+ query = f"""
33
+ SELECT
34
+ peptidoform,
35
+ exp_mass_to_charge AS precursor_mz,
36
+ precursor_charge AS charge,
37
+ mz_array,
38
+ consensus_support,
39
+ posterior_error_probability,
40
+ intensity_array
41
+ FROM parquet_scan(?)
42
+ {where_clause}
43
+ """
44
+
45
+ self.cursor = con.execute(query, [parquet_path] + params)
46
+
47
+ self.batch_size = batch_size
48
+ self.max_peaks = max_peaks
49
+
50
+ def __iter__(self):
51
+ reader = self.cursor.fetch_record_batch(self.batch_size)
52
+
53
+ for batch in reader:
54
+ if batch.num_rows == 0:
55
+ continue
56
+ yield self.process_batch(batch)
57
+
58
+ # -----------------------------
59
+ def process_batch(self, batch):
60
+
61
+ peptidoform = batch["peptidoform"].to_pylist()
62
+ charges = batch["charge"].to_pylist()
63
+ mz_list = batch["mz_array"].to_pylist()
64
+ int_list = batch["intensity_array"].to_pylist()
65
+ precursor_mz = batch["precursor_mz"].to_pylist()
66
+ consensus_supports = batch["consensus_support"].to_pylist()
67
+ peps = batch["posterior_error_probability"].to_pylist()
68
+
69
+ spectra_out = []
70
+ seq_out = []
71
+ charge_out = []
72
+ precursor_out = []
73
+
74
+ # -----------------------------
75
+ # per spectrum processing
76
+ # -----------------------------
77
+ for i in range(len(peptidoform)):
78
+ mz = np.asarray(mz_list[i], dtype=np.float32)
79
+ intensity = np.asarray(int_list[i], dtype=np.float32)
80
+
81
+ if len(mz) == 0:
82
+ continue
83
+
84
+ # -------------------------
85
+ # top-k peaks
86
+ # -------------------------
87
+ if len(mz) > self.max_peaks:
88
+ idx = np.argsort(intensity)[-self.max_peaks:]
89
+ mz = mz[idx]
90
+ intensity = intensity[idx]
91
+
92
+ # -------------------------
93
+ # normalize
94
+ # -------------------------
95
+ max_int = intensity.max() if len(intensity) > 0 else 1.0
96
+ intensity = intensity / max_int
97
+
98
+ spectrum = np.stack([mz, intensity], axis=1)
99
+
100
+ spectra_out.append(torch.tensor(spectrum, dtype=torch.float32))
101
+ seq_out.append(peptidoform[i])
102
+ charge_out.append(charges[i])
103
+ precursor_out.append(precursor_mz[i])
104
+
105
+ return {
106
+ "spectrum": spectra_out,
107
+ "sequence": seq_out,
108
+ "precursor_mz": torch.tensor(precursor_out, dtype=torch.float32),
109
+ "charge": torch.tensor(charge_out, dtype=torch.long),
110
+ }
111
+
112
+
113
+ if __name__ == '__main__':
114
+ """Test dataset + sampler + dataloader pipeline."""
115
+ file_path = ["D:/gitrepo/MSnet/MSNetLoader/tests/test_data/PXD014877-Akkermansia_muciniphilia-MSNet.parquet",
116
+ "D:/gitrepo/MSnet/MSNetLoader/tests/test_data/PXD014877_Clostridium_Bolteae-MSNet.parquet"]
117
+ dataset = DeNovoIterableDataset(file_path)
118
+ from torch.utils.data import DataLoader
119
+ loader = DataLoader(
120
+ dataset,
121
+ batch_size=None,
122
+ num_workers=0,
123
+ pin_memory=False
124
+ )
125
+
126
+ batch = next(iter(loader))
127
+ print(batch)
128
+
@@ -0,0 +1,143 @@
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ import duckdb
4
+
5
+
6
+ class DeNovoTFDataset:
7
+
8
+ def __init__(self, parquet_path, max_peaks=150, batch_size=32,
9
+ min_consensus_support=None,
10
+ max_pep=None):
11
+
12
+ con = duckdb.connect()
13
+ self.min_consensus_support = min_consensus_support
14
+ self.max_pep = max_pep
15
+
16
+ conditions = []
17
+ params = []
18
+
19
+ if self.min_consensus_support is not None:
20
+ conditions.append("consensus_support >= ?")
21
+ params.append(self.min_consensus_support)
22
+
23
+ if self.max_pep is not None:
24
+ conditions.append("posterior_error_probability <= ?")
25
+ params.append(self.max_pep)
26
+
27
+ where_clause = ""
28
+ if conditions:
29
+ where_clause = "WHERE " + " AND ".join(conditions)
30
+
31
+ query = f"""
32
+ SELECT
33
+ peptidoform,
34
+ exp_mass_to_charge AS precursor_mz,
35
+ precursor_charge AS charge,
36
+ mz_array,
37
+ consensus_support,
38
+ posterior_error_probability,
39
+ intensity_array
40
+ FROM parquet_scan(?)
41
+ {where_clause}
42
+ """
43
+
44
+ self.cursor = con.execute(query, [parquet_path] + params)
45
+
46
+ self.batch_size = batch_size
47
+ self.max_peaks = max_peaks
48
+
49
+ # =========================================================
50
+ # Generator (核心)
51
+ # =========================================================
52
+ def generator(self):
53
+ reader = self.cursor.fetch_record_batch(self.batch_size)
54
+ for batch in reader:
55
+ if batch.num_rows == 0:
56
+ continue
57
+ result = self.process_batch(batch)
58
+ for i in range(len(result["sequence"])):
59
+ yield (
60
+ result["spectrum"][i],
61
+ result["sequence"][i],
62
+ result["precursor_mz"][i],
63
+ result["charge"][i],
64
+ )
65
+
66
+ # =========================================================
67
+ # TF Dataset接口
68
+ # =========================================================
69
+ def get_dataset(self):
70
+
71
+ output_signature = (
72
+ tf.TensorSpec(shape=(None, 2), dtype=tf.float32), # spectrum
73
+ tf.TensorSpec(shape=(), dtype=tf.string), # sequence
74
+ tf.TensorSpec(shape=(), dtype=tf.float32), # precursor_mz
75
+ tf.TensorSpec(shape=(), dtype=tf.int32), # charge
76
+ )
77
+
78
+ ds = tf.data.Dataset.from_generator(
79
+ self.generator,
80
+ output_signature=output_signature
81
+ )
82
+
83
+ return ds
84
+
85
+ # =========================================================
86
+ # batch处理(基本不变)
87
+ # =========================================================
88
+ def process_batch(self, batch):
89
+
90
+ peptidoform = batch["peptidoform"].to_pylist()
91
+ charges = batch["charge"].to_pylist()
92
+ mz_list = batch["mz_array"].to_pylist()
93
+ int_list = batch["intensity_array"].to_pylist()
94
+ precursor_mz = batch["precursor_mz"].to_pylist()
95
+ consensus_supports = batch["consensus_support"].to_pylist()
96
+ peps = batch["posterior_error_probability"].to_pylist()
97
+
98
+ spectra_out = []
99
+ seq_out = []
100
+ charge_out = []
101
+ precursor_out = []
102
+
103
+ for i in range(len(peptidoform)):
104
+
105
+ if not self.filter_by_consensus_support(consensus_supports[i]):
106
+ continue
107
+
108
+ if not self.filter_by_pep(peps[i]):
109
+ continue
110
+
111
+ mz = np.asarray(mz_list[i], dtype=np.float32)
112
+ intensity = np.asarray(int_list[i], dtype=np.float32)
113
+
114
+ if len(mz) == 0:
115
+ continue
116
+
117
+ # -------------------------
118
+ # top-k peaks
119
+ # -------------------------
120
+ if len(mz) > self.max_peaks:
121
+ idx = np.argsort(intensity)[-self.max_peaks:]
122
+ mz = mz[idx]
123
+ intensity = intensity[idx]
124
+
125
+ # -------------------------
126
+ # normalize
127
+ # -------------------------
128
+ max_int = intensity.max() if len(intensity) > 0 else 1.0
129
+ intensity = intensity / max_int
130
+
131
+ spectrum = np.stack([mz, intensity], axis=1)
132
+
133
+ spectra_out.append(spectrum.astype(np.float32))
134
+ seq_out.append(peptidoform[i].encode("utf-8")) # TF需要bytes
135
+ charge_out.append(np.int32(charges[i]))
136
+ precursor_out.append(np.float32(precursor_mz[i]))
137
+
138
+ return {
139
+ "spectrum": spectra_out,
140
+ "sequence": seq_out,
141
+ "precursor_mz": precursor_out,
142
+ "charge": charge_out,
143
+ }
@@ -0,0 +1,207 @@
1
+ import torch
2
+ import numpy as np
3
+ from torch.utils.data import IterableDataset
4
+ import duckdb
5
+ import re
6
+
7
+
8
+ class MS2TorchDataset(IterableDataset):
9
+
10
+ def __init__(self, parquet_path, batch_size=8, ion_types=("b", "y"), charges=(1, 2),
11
+ min_consensus_support=None,
12
+ max_pep=None
13
+ ):
14
+
15
+ con = duckdb.connect()
16
+ self.min_consensus_support = min_consensus_support
17
+ self.max_pep = max_pep
18
+
19
+ conditions = []
20
+ params = []
21
+
22
+ if self.min_consensus_support is not None:
23
+ conditions.append("consensus_support >= ?")
24
+ params.append(self.min_consensus_support)
25
+
26
+ if self.max_pep is not None:
27
+ conditions.append("posterior_error_probability <= ?")
28
+ params.append(self.max_pep)
29
+
30
+ where_clause = ""
31
+ if conditions:
32
+ where_clause = "WHERE " + " AND ".join(conditions)
33
+
34
+ query = f"""
35
+ SELECT
36
+ sequence,
37
+ peptidoform,
38
+ precursor_charge AS charge,
39
+ cv_params.Instrument AS instrument,
40
+ CAST(cv_params."Collision Energy" AS DOUBLE) AS nce,
41
+ ion_type_array,
42
+ charge_array,
43
+ intensity_array
44
+ FROM parquet_scan(?)
45
+ {where_clause}
46
+ ORDER BY length(sequence)
47
+ """
48
+
49
+ self.cursor = con.execute(query, [parquet_path] + params)
50
+ self.batch_size = batch_size
51
+
52
+ self.ion_types = set(ion_types)
53
+ self.charges = set(charges)
54
+
55
+ self.channel_map = {
56
+ ("b", 1): 0,
57
+ ("b", 2): 1,
58
+ ("y", 1): 2,
59
+ ("y", 2): 3,
60
+ }
61
+
62
+ self.active_channels = [
63
+ self.channel_map[(t, z)]
64
+ for t in ion_types
65
+ for z in charges
66
+ if (t, z) in self.channel_map
67
+ ]
68
+
69
+ # -----------------------------
70
+ def __iter__(self):
71
+ reader = self.cursor.fetch_record_batch(self.batch_size)
72
+
73
+ for batch in reader:
74
+ if batch.num_rows == 0:
75
+ continue
76
+ yield self.process_batch(batch)
77
+
78
+ # -----------------------------
79
+ def process_batch(self, batch):
80
+ sequences = batch["sequence"].to_pylist()
81
+ peptidoform = batch["peptidoform"].to_pylist()
82
+ charges = batch["charge"].to_pylist()
83
+ nces = batch["nce"].to_pylist()
84
+ instruments = batch["instrument"].to_pylist()
85
+ fragments = batch["ion_type_array"].to_pylist()
86
+ fragment_charges = batch["charge_array"].to_pylist()
87
+ intensities = batch["intensity_array"].to_pylist()
88
+
89
+ targets = self.build_batch_fragments(
90
+ sequences,
91
+ fragments,
92
+ fragment_charges,
93
+ intensities
94
+ )
95
+
96
+ charge_tensor = torch.tensor(charges, dtype=torch.long)
97
+ nce_tensor = torch.tensor(nces, dtype=torch.float32)
98
+
99
+ return {
100
+ "peptide": peptidoform,
101
+ "charge": charge_tensor,
102
+ "nce": nce_tensor,
103
+ "instruments": instruments,
104
+ "targets": targets
105
+ }
106
+
107
+ # -----------------------------
108
+ def build_batch_fragments(
109
+ self,
110
+ sequences,
111
+ fragments_list,
112
+ frag_charges_list,
113
+ intensity_list
114
+ ):
115
+
116
+ B = len(sequences)
117
+ Lmax = max(len(s) for s in sequences)
118
+
119
+ out = np.zeros((B, Lmax - 1, 4), dtype=np.float32)
120
+
121
+ # -----------------------------
122
+ for b in range(B):
123
+
124
+ ions = fragments_list[b]
125
+ charges = frag_charges_list[b]
126
+ ints = intensity_list[b]
127
+
128
+ if len(ions) == 0:
129
+ continue
130
+
131
+ ions = np.asarray(ions)
132
+ charges = np.asarray(charges)
133
+ ints = np.asarray(ints, dtype=np.float32)
134
+
135
+ valid = (ions != None)
136
+ ions = ions[valid]
137
+ charges = charges[valid]
138
+ ints = ints[valid]
139
+
140
+ if len(ions) == 0:
141
+ continue
142
+
143
+ # remove neutral loss
144
+ mask = np.char.find(ions.astype(str), "-") == -1
145
+ ions = ions[mask]
146
+ charges = charges[mask]
147
+ ints = ints[mask]
148
+
149
+ if len(ions) == 0:
150
+ continue
151
+
152
+ # -----------------------------
153
+ # parse ion string safely
154
+ # -----------------------------
155
+ ion_str = ions.astype(str)
156
+
157
+ ion_type = np.array([x[0] for x in ion_str])
158
+
159
+ # SAFE regex position parsing
160
+ pos = np.array([
161
+ int(re.findall(r"\d+", x)[0]) if re.findall(r"\d+", x) else -1
162
+ for x in ion_str
163
+ ])
164
+
165
+ seq_len = len(sequences[b])
166
+
167
+ valid = (pos >= 1) & (pos < seq_len)
168
+
169
+ ion_type = ion_type[valid]
170
+ pos = pos[valid] - 1
171
+ charges = charges[valid]
172
+ ints = ints[valid]
173
+
174
+ if len(pos) == 0:
175
+ continue
176
+
177
+ # -----------------------------
178
+ # channel mapping (fixed)
179
+ # -----------------------------
180
+ ch = np.full(len(ion_type), -1, dtype=np.int32)
181
+
182
+ for (t, z), c in self.channel_map.items():
183
+ if t in self.ion_types and z in self.charges:
184
+ ch[(ion_type == t) & (charges == z)] = c
185
+
186
+ valid_ch = ch >= 0
187
+
188
+ pos = pos[valid_ch]
189
+ ch = ch[valid_ch]
190
+ ints = ints[valid_ch]
191
+
192
+ if len(pos) == 0:
193
+ continue
194
+
195
+ # -----------------------------
196
+ # intensity normalize
197
+ # -----------------------------
198
+ max_int = ints.max() if len(ints) > 0 else 1.0
199
+ ints = ints / max_int
200
+
201
+ # -----------------------------
202
+ # scatter
203
+ # -----------------------------
204
+ out[b, pos, ch] += ints
205
+
206
+ # -----------------------------
207
+ return torch.from_numpy(out[:, :, self.active_channels])
msnetloader/ms2_tf.py ADDED
@@ -0,0 +1,204 @@
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ import duckdb
4
+ import re
5
+
6
+
7
+ class MS2TFDataset:
8
+
9
+ def __init__(
10
+ self,
11
+ parquet_path,
12
+ batch_size=8,
13
+ ion_types=("b", "y"),
14
+ charges=(1, 2),
15
+ min_consensus_support=None,
16
+ max_pep=None
17
+ ):
18
+ con = duckdb.connect()
19
+ self.min_consensus_support = min_consensus_support
20
+ self.max_pep = max_pep
21
+
22
+ conditions = []
23
+ params = []
24
+
25
+ if self.min_consensus_support is not None:
26
+ conditions.append("consensus_support >= ?")
27
+ params.append(self.min_consensus_support)
28
+
29
+ if self.max_pep is not None:
30
+ conditions.append("posterior_error_probability <= ?")
31
+ params.append(self.max_pep)
32
+
33
+ where_clause = ""
34
+ if conditions:
35
+ where_clause = "WHERE " + " AND ".join(conditions)
36
+
37
+ query = f"""
38
+ SELECT
39
+ sequence,
40
+ peptidoform,
41
+ precursor_charge AS charge,
42
+ cv_params.Instrument AS instrument,
43
+ CAST(cv_params."Collision Energy" AS DOUBLE) AS nce,
44
+ ion_type_array,
45
+ charge_array,
46
+ intensity_array
47
+ FROM parquet_scan(?)
48
+ {where_clause}
49
+ ORDER BY length(sequence)
50
+ """
51
+
52
+ self.cursor = con.execute(query, [parquet_path] + params)
53
+
54
+ self.batch_size = batch_size
55
+
56
+ self.ion_types = set(ion_types)
57
+ self.charges = set(charges)
58
+
59
+ self.channel_map = {
60
+ ("b", 1): 0,
61
+ ("b", 2): 1,
62
+ ("y", 1): 2,
63
+ ("y", 2): 3,
64
+ }
65
+
66
+ self.active_channels = [
67
+ self.channel_map[(t, z)]
68
+ for t in ion_types for z in charges
69
+ if (t, z) in self.channel_map
70
+ ]
71
+
72
+ # =========================================================
73
+ # generator(核心替代 __iter__)
74
+ # =========================================================
75
+ def generator(self):
76
+ reader = self.cursor.fetch_record_batch(self.batch_size)
77
+ for batch in reader:
78
+ if batch.num_rows == 0:
79
+ continue
80
+ yield self.process_batch(batch)
81
+
82
+ # =========================================================
83
+ def get_dataset(self):
84
+ output_signature = {
85
+ "peptide": tf.TensorSpec(shape=(None,), dtype=tf.string),
86
+ "charge": tf.TensorSpec(shape=(None,), dtype=tf.int32),
87
+ "nce": tf.TensorSpec(shape=(None,), dtype=tf.float32),
88
+ "instruments": tf.TensorSpec(shape=(None,), dtype=tf.string),
89
+ "targets": tf.TensorSpec(shape=(None, None, len(self.active_channels)), dtype=tf.float32),
90
+ }
91
+
92
+ return tf.data.Dataset.from_generator(
93
+ self.generator,
94
+ output_signature=output_signature
95
+ )
96
+
97
+ # =========================================================
98
+ def process_batch(self, batch):
99
+ sequences = batch["sequence"].to_pylist()
100
+ peptidoform = batch["peptidoform"].to_pylist()
101
+ charges = batch["charge"].to_pylist()
102
+ nces = batch["nce"].to_pylist()
103
+ instruments = batch["instrument"].to_pylist()
104
+
105
+ fragments = batch["ion_type_array"].to_pylist()
106
+ fragment_charges = batch["charge_array"].to_pylist()
107
+ intensities = batch["intensity_array"].to_pylist()
108
+
109
+ targets = self.build_batch_fragments(
110
+ sequences,
111
+ fragments,
112
+ fragment_charges,
113
+ intensities
114
+ )
115
+
116
+ return {
117
+ "peptide": np.array(peptidoform, dtype=np.string_),
118
+ "charge": np.array(charges, dtype=np.int32),
119
+ "nce": np.array(nces, dtype=np.float32),
120
+ "instruments": np.array(instruments, dtype=np.string_),
121
+ "targets": targets.numpy(), # TF expects numpy
122
+ }
123
+
124
+ # =========================================================
125
+ def build_batch_fragments(
126
+ self,
127
+ sequences,
128
+ fragments_list,
129
+ frag_charges_list,
130
+ intensity_list
131
+ ):
132
+ B = len(sequences)
133
+ Lmax = max(len(s) for s in sequences)
134
+
135
+ out = np.zeros((B, Lmax - 1, 4), dtype=np.float32)
136
+
137
+ for b in range(B):
138
+ ions = fragments_list[b]
139
+ charges = frag_charges_list[b]
140
+ ints = intensity_list[b]
141
+
142
+ if len(ions) == 0:
143
+ continue
144
+
145
+ ions = np.asarray(ions)
146
+ charges = np.asarray(charges)
147
+ ints = np.asarray(ints, dtype=np.float32)
148
+
149
+ valid = (ions != None)
150
+ ions = ions[valid]
151
+ charges = charges[valid]
152
+ ints = ints[valid]
153
+
154
+ if len(ions) == 0:
155
+ continue
156
+
157
+ # remove neutral loss
158
+ mask = np.char.find(ions.astype(str), "-") == -1
159
+ ions = ions[mask]
160
+ charges = charges[mask]
161
+ ints = ints[mask]
162
+
163
+ if len(ions) == 0:
164
+ continue
165
+
166
+ ion_str = ions.astype(str)
167
+ ion_type = np.array([x[0] for x in ion_str])
168
+
169
+ pos = np.array([
170
+ int(re.findall(r"\d+", x)[0]) if re.findall(r"\d+", x) else -1
171
+ for x in ion_str
172
+ ])
173
+
174
+ seq_len = len(sequences[b])
175
+ valid = (pos >= 1) & (pos < seq_len)
176
+
177
+ ion_type = ion_type[valid]
178
+ pos = pos[valid] - 1
179
+ charges = charges[valid]
180
+ ints = ints[valid]
181
+
182
+ if len(pos) == 0:
183
+ continue
184
+
185
+ ch = np.full(len(ion_type), -1, dtype=np.int32)
186
+
187
+ for (t, z), c in self.channel_map.items():
188
+ if t in self.ion_types and z in self.charges:
189
+ ch[(ion_type == t) & (charges == z)] = c
190
+
191
+ valid_ch = ch >= 0
192
+ pos = pos[valid_ch]
193
+ ch = ch[valid_ch]
194
+ ints = ints[valid_ch]
195
+
196
+ if len(pos) == 0:
197
+ continue
198
+
199
+ max_int = ints.max() if len(ints) > 0 else 1.0
200
+ ints = ints / max_int
201
+
202
+ out[b, pos, ch] += ints
203
+
204
+ return tf.convert_to_tensor(out[:, :, self.active_channels], dtype=tf.float32)
@@ -0,0 +1,78 @@
1
+ from torch.utils.data import IterableDataset
2
+ import duckdb
3
+ import numpy as np
4
+
5
+
6
+ class RTIterableDataset(IterableDataset):
7
+
8
+ def __init__(self, parquet_path, batch_size=32,
9
+ min_consensus_support=None,
10
+ max_pep=None
11
+ ):
12
+ con = duckdb.connect()
13
+ self.min_consensus_support = min_consensus_support
14
+ self.max_pep = max_pep
15
+
16
+ conditions = []
17
+ params = []
18
+
19
+ if self.min_consensus_support is not None:
20
+ conditions.append("consensus_support >= ?")
21
+ params.append(self.min_consensus_support)
22
+
23
+ if self.max_pep is not None:
24
+ conditions.append("posterior_error_probability <= ?")
25
+ params.append(self.max_pep)
26
+
27
+ where_clause = ""
28
+ if conditions:
29
+ where_clause = "WHERE " + " AND ".join(conditions)
30
+
31
+ query = f"""
32
+ SELECT
33
+ peptidoform,
34
+ retention_time,
35
+ consensus_support,
36
+ posterior_error_probability
37
+ FROM parquet_scan(?)
38
+ {where_clause}
39
+ ORDER BY length(sequence)
40
+ """
41
+
42
+ self.cursor = con.execute(query, [parquet_path] + params)
43
+ self.batch_size = batch_size
44
+
45
+ def __iter__(self):
46
+ reader = self.cursor.fetch_record_batch(self.batch_size)
47
+
48
+ for batch in reader:
49
+ if batch.num_rows == 0:
50
+ continue
51
+ yield self.process_batch(batch)
52
+
53
+ def process_batch(self, batch):
54
+ peptidoform = batch["peptidoform"].to_pylist()
55
+ retention_time = batch["retention_time"].to_pylist()
56
+ rt = np.array(retention_time, dtype=np.float32) / 60.0
57
+
58
+ return {
59
+ "peptide": peptidoform,
60
+ "rt": rt
61
+ }
62
+
63
+
64
+ if __name__ == '__main__':
65
+ """Test dataset + sampler + dataloader pipeline."""
66
+ file_path = ["D:/gitrepo/MSnet/MSNetLoader/tests/test_data/PXD014877-Akkermansia_muciniphilia-MSNet.parquet",
67
+ "D:/gitrepo/MSnet/MSNetLoader/tests/test_data/PXD014877_Clostridium_Bolteae-MSNet.parquet"]
68
+ dataset = RTIterableDataset(file_path)
69
+ from torch.utils.data import DataLoader
70
+ loader = DataLoader(
71
+ dataset,
72
+ batch_size=None,
73
+ num_workers=0,
74
+ pin_memory=False
75
+ )
76
+
77
+ batch = next(iter(loader))
78
+ print(batch)
msnetloader/rt_tf.py ADDED
@@ -0,0 +1,102 @@
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ import duckdb
4
+
5
+
6
+ class RTTFDataset:
7
+
8
+ def __init__(
9
+ self,
10
+ parquet_path,
11
+ batch_size=100_000,
12
+ min_consensus_support=None,
13
+ max_pep=None
14
+ ):
15
+ con = duckdb.connect()
16
+ self.min_consensus_support = min_consensus_support
17
+ self.max_pep = max_pep
18
+
19
+ conditions = []
20
+ params = []
21
+
22
+ if self.min_consensus_support is not None:
23
+ conditions.append("consensus_support >= ?")
24
+ params.append(self.min_consensus_support)
25
+
26
+ if self.max_pep is not None:
27
+ conditions.append("posterior_error_probability <= ?")
28
+ params.append(self.max_pep)
29
+
30
+ where_clause = ""
31
+ if conditions:
32
+ where_clause = "WHERE " + " AND ".join(conditions)
33
+
34
+ query = f"""
35
+ SELECT
36
+ peptidoform,
37
+ retention_time,
38
+ consensus_support,
39
+ posterior_error_probability
40
+ FROM parquet_scan(?)
41
+ {where_clause}
42
+ ORDER BY length(sequence)
43
+ """
44
+
45
+ self.cursor = con.execute(query, [parquet_path] + params)
46
+
47
+ self.batch_size = batch_size
48
+
49
+ # =========================================================
50
+ # generator
51
+ # =========================================================
52
+ def generator(self):
53
+ reader = self.cursor.fetch_record_batch(self.batch_size)
54
+ for batch in reader:
55
+ if batch.num_rows == 0:
56
+ continue
57
+ yield self.process_batch(batch)
58
+
59
+ # =========================================================
60
+ def get_dataset(self):
61
+ output_signature = {
62
+ "peptide": tf.TensorSpec(shape=(None,), dtype=tf.string),
63
+ "rt": tf.TensorSpec(shape=(None,), dtype=tf.float32),
64
+ }
65
+
66
+ return tf.data.Dataset.from_generator(
67
+ self.generator,
68
+ output_signature=output_signature
69
+ )
70
+
71
+ # =========================================================
72
+ def process_batch(self, batch):
73
+ peptidoform = batch["peptidoform"].to_pylist()
74
+ retention_time = batch["retention_time"].to_pylist()
75
+ consensus_support = batch["consensus_support"].to_pylist()
76
+ pep = batch["posterior_error_probability"].to_pylist()
77
+
78
+ # ✅ 转 numpy
79
+ peptidoform = np.array(peptidoform, dtype=np.string_)
80
+ retention_time = np.array(retention_time, dtype=np.float32)
81
+ consensus_support = np.array(consensus_support)
82
+ pep = np.array(pep)
83
+
84
+ # =====================================================
85
+ # ✅ 应用 filter(重点)
86
+ # =====================================================
87
+ mask = np.ones(len(peptidoform), dtype=bool)
88
+
89
+ if self.min_consensus_support is not None:
90
+ mask &= (consensus_support >= self.min_consensus_support)
91
+
92
+ if self.max_pep is not None:
93
+ mask &= (pep <= self.max_pep)
94
+
95
+ peptidoform = peptidoform[mask]
96
+ retention_time = retention_time[mask]
97
+
98
+ # =====================================================
99
+ return {
100
+ "peptide": peptidoform,
101
+ "rt": retention_time / 60.0 # s → m
102
+ }
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: msnetloader
3
+ Version: 0.0.0
4
+ Summary: A Python API for seamless integration of π-MSNet into AI workflows
5
+ Author-email: chengxin dai <chengxin2024@126.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/daichengxin/MSnet/
8
+ Project-URL: Repository, https://github.com/daichengxin/MSnet/
9
+ Keywords: sdrf,python,multiomics,proteomics
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: torch==2.6.0
19
+ Requires-Dist: pandas
20
+ Requires-Dist: numpy
21
+ Requires-Dist: pyarrow==17.0.0
22
+ Requires-Dist: tensorflow
23
+ Requires-Dist: duckdb
24
+
25
+ # MSNetLoader
26
+ A Python API for seamless integration of π-MSNet into AI workflows
@@ -0,0 +1,11 @@
1
+ msnetloader/__init__.py,sha256=nRNl6VJc36MR8ufz1qV_uNgyLW_NqhOgnkW5_24pbow,56
2
+ msnetloader/denovo_loader.py,sha256=vGPeNUuNOGxmNGZB-Dfxf_5Pwsh-EgYBpWQZ5wzFBBI,4143
3
+ msnetloader/denovo_tf.py,sha256=Qx4x_s4jasOMwcuNPuDbXM1Xr5ggqyVZdnMAv50gEFU,4780
4
+ msnetloader/ms2_loader.py,sha256=ob7f4EWZOKrBuLG4cRg-sPRBjYnY618aXfnL_ZKo91M,5940
5
+ msnetloader/ms2_tf.py,sha256=-lFvBvmHALEY7HHhmp9ZAubZYpy9izAmq4-Fy1xCXj8,6132
6
+ msnetloader/rt_loader.py,sha256=cCs0jTuAGMiyDGo3YxNEs0I2UIqMRe-buDAQQnd0iy4,2308
7
+ msnetloader/rt_tf.py,sha256=G-Daheu2xpL8qfRGBlufgeAw5wBY2NtxWSIV-9Vmt-M,3289
8
+ msnetloader-0.0.0.dist-info/METADATA,sha256=4u_1npAT9pYmTsMgLvHLcZngDsbRPI0to3pEnJc66VY,989
9
+ msnetloader-0.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ msnetloader-0.0.0.dist-info/top_level.txt,sha256=TdIQxnFnHC7AFlv8Rl0R1wrdmzrF30XuQFr0RSw3uOA,12
11
+ msnetloader-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ msnetloader