masster 0.2.5__py3-none-any.whl → 0.3.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.
Potentially problematic release.
This version of masster might be problematic. Click here for more details.
- masster/__init__.py +27 -27
- masster/_version.py +17 -17
- masster/chromatogram.py +497 -503
- masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.featureXML +199787 -0
- masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.sample5 +0 -0
- masster/logger.py +318 -244
- masster/sample/__init__.py +9 -9
- masster/sample/defaults/__init__.py +15 -15
- masster/sample/defaults/find_adducts_def.py +325 -325
- masster/sample/defaults/find_features_def.py +366 -366
- masster/sample/defaults/find_ms2_def.py +285 -285
- masster/sample/defaults/get_spectrum_def.py +314 -318
- masster/sample/defaults/sample_def.py +374 -378
- masster/sample/h5.py +1321 -1297
- masster/sample/helpers.py +833 -364
- masster/sample/lib.py +762 -0
- masster/sample/load.py +1220 -1187
- masster/sample/parameters.py +131 -131
- masster/sample/plot.py +1610 -1622
- masster/sample/processing.py +1402 -1416
- masster/sample/quant.py +209 -0
- masster/sample/sample.py +391 -387
- masster/sample/sample5_schema.json +181 -181
- masster/sample/save.py +737 -736
- masster/sample/sciex.py +1213 -0
- masster/spectrum.py +1287 -1319
- masster/study/__init__.py +9 -9
- masster/study/defaults/__init__.py +21 -19
- masster/study/defaults/align_def.py +267 -267
- masster/study/defaults/export_def.py +41 -40
- masster/study/defaults/fill_chrom_def.py +264 -264
- masster/study/defaults/fill_def.py +260 -0
- masster/study/defaults/find_consensus_def.py +256 -256
- masster/study/defaults/find_ms2_def.py +163 -163
- masster/study/defaults/integrate_chrom_def.py +225 -225
- masster/study/defaults/integrate_def.py +221 -0
- masster/study/defaults/merge_def.py +256 -0
- masster/study/defaults/study_def.py +272 -269
- masster/study/export.py +674 -287
- masster/study/h5.py +1398 -886
- masster/study/helpers.py +1650 -433
- masster/study/helpers_optimized.py +317 -0
- masster/study/load.py +1201 -1078
- masster/study/parameters.py +99 -99
- masster/study/plot.py +632 -645
- masster/study/processing.py +1057 -1046
- masster/study/save.py +149 -134
- masster/study/study.py +606 -522
- masster/study/study5_schema.json +247 -241
- {masster-0.2.5.dist-info → masster-0.3.0.dist-info}/METADATA +15 -10
- masster-0.3.0.dist-info/RECORD +59 -0
- {masster-0.2.5.dist-info → masster-0.3.0.dist-info}/licenses/LICENSE +661 -661
- masster-0.2.5.dist-info/RECORD +0 -50
- {masster-0.2.5.dist-info → masster-0.3.0.dist-info}/WHEEL +0 -0
- {masster-0.2.5.dist-info → masster-0.3.0.dist-info}/entry_points.txt +0 -0
masster/study/study.py
CHANGED
|
@@ -1,522 +1,606 @@
|
|
|
1
|
-
"""
|
|
2
|
-
study.py
|
|
3
|
-
|
|
4
|
-
This module provides tools for multi-sample mass spectrometry data analysis and cross-sample feature alignment.
|
|
5
|
-
It defines the `study` class, which manages collections of DDA files, performs feature alignment across samples,
|
|
6
|
-
generates consensus features, and provides study-level visualization and reporting capabilities.
|
|
7
|
-
|
|
8
|
-
Key Features:
|
|
9
|
-
- **Multi-Sample Management**: Handle collections of mass spectrometry files with metadata.
|
|
10
|
-
- **Feature Alignment**: Align features across multiple samples using retention time and m/z tolerances.
|
|
11
|
-
- **Consensus Features**: Generate consensus feature tables from aligned data.
|
|
12
|
-
- **Batch Processing**: Automated processing of entire studies with configurable parameters.
|
|
13
|
-
- **Study Visualization**: Generate comparative plots and alignment visualizations.
|
|
14
|
-
- **Export Capabilities**: Export study results in various formats for downstream analysis.
|
|
15
|
-
|
|
16
|
-
Dependencies:
|
|
17
|
-
- `pyopenms`: For mass spectrometry data handling and algorithms.
|
|
18
|
-
- `polars` and `pandas`: For efficient data manipulation and analysis.
|
|
19
|
-
- `bokeh`, `holoviews`, `panel`: For interactive visualizations and dashboards.
|
|
20
|
-
- `numpy`: For numerical computations and array operations.
|
|
21
|
-
|
|
22
|
-
Classes:
|
|
23
|
-
- `study`: Main class for multi-sample study management, providing methods for file loading,
|
|
24
|
-
feature alignment, consensus generation, and study-level analysis.
|
|
25
|
-
|
|
26
|
-
Example Usage:
|
|
27
|
-
```python
|
|
28
|
-
from study import study
|
|
29
|
-
|
|
30
|
-
# Create study from multiple files
|
|
31
|
-
study_obj = study()
|
|
32
|
-
study_obj.load_files(["sample1.mzML", "sample2.mzML", "sample3.mzML"])
|
|
33
|
-
study_obj.process_all()
|
|
34
|
-
study_obj.align()
|
|
35
|
-
study_obj.plot_alignment_bokeh()
|
|
36
|
-
study_obj.export_consensus()
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
See Also:
|
|
40
|
-
- `single.py`: For individual file processing before study-level analysis.
|
|
41
|
-
- `parameters.study_parameters`: For study-specific parameter configuration.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
from __future__ import annotations
|
|
47
|
-
|
|
48
|
-
import importlib
|
|
49
|
-
import os
|
|
50
|
-
import sys
|
|
51
|
-
|
|
52
|
-
import polars as pl
|
|
53
|
-
|
|
54
|
-
# Study-specific imports
|
|
55
|
-
from masster.study.h5 import _load_study5
|
|
56
|
-
from masster.study.h5 import _save_study5
|
|
57
|
-
from masster.study.
|
|
58
|
-
from masster.study.helpers import
|
|
59
|
-
from masster.study.helpers import
|
|
60
|
-
from masster.study.helpers import
|
|
61
|
-
from masster.study.helpers import
|
|
62
|
-
from masster.study.helpers import
|
|
63
|
-
from masster.study.helpers import
|
|
64
|
-
from masster.study.helpers import
|
|
65
|
-
from masster.study.helpers import
|
|
66
|
-
from masster.study.helpers import
|
|
67
|
-
from masster.study.helpers import
|
|
68
|
-
from masster.study.helpers import
|
|
69
|
-
from masster.study.helpers import
|
|
70
|
-
from masster.study.helpers import
|
|
71
|
-
from masster.study.
|
|
72
|
-
from masster.study.
|
|
73
|
-
from masster.study.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
from masster.study.
|
|
79
|
-
from masster.study.
|
|
80
|
-
from masster.study.
|
|
81
|
-
from masster.study.
|
|
82
|
-
from masster.study.
|
|
83
|
-
|
|
84
|
-
from masster.study.
|
|
85
|
-
from masster.study.
|
|
86
|
-
from masster.study.
|
|
87
|
-
from masster.study.
|
|
88
|
-
from masster.study.
|
|
89
|
-
from masster.study.
|
|
90
|
-
from masster.study.
|
|
91
|
-
from masster.study.
|
|
92
|
-
from masster.study.
|
|
93
|
-
from masster.study.
|
|
94
|
-
from masster.study.
|
|
95
|
-
from masster.study.
|
|
96
|
-
from masster.study.
|
|
97
|
-
from masster.study.
|
|
98
|
-
from masster.study.
|
|
99
|
-
from masster.study.
|
|
100
|
-
from masster.study.
|
|
101
|
-
from masster.study.
|
|
102
|
-
from masster.study.
|
|
103
|
-
from masster.study.
|
|
104
|
-
from masster.study.
|
|
105
|
-
|
|
106
|
-
from masster.
|
|
107
|
-
from masster.study.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
self.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
#
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
"""
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
else:
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
self.
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if
|
|
521
|
-
|
|
522
|
-
|
|
1
|
+
"""
|
|
2
|
+
study.py
|
|
3
|
+
|
|
4
|
+
This module provides tools for multi-sample mass spectrometry data analysis and cross-sample feature alignment.
|
|
5
|
+
It defines the `study` class, which manages collections of DDA files, performs feature alignment across samples,
|
|
6
|
+
generates consensus features, and provides study-level visualization and reporting capabilities.
|
|
7
|
+
|
|
8
|
+
Key Features:
|
|
9
|
+
- **Multi-Sample Management**: Handle collections of mass spectrometry files with metadata.
|
|
10
|
+
- **Feature Alignment**: Align features across multiple samples using retention time and m/z tolerances.
|
|
11
|
+
- **Consensus Features**: Generate consensus feature tables from aligned data.
|
|
12
|
+
- **Batch Processing**: Automated processing of entire studies with configurable parameters.
|
|
13
|
+
- **Study Visualization**: Generate comparative plots and alignment visualizations.
|
|
14
|
+
- **Export Capabilities**: Export study results in various formats for downstream analysis.
|
|
15
|
+
|
|
16
|
+
Dependencies:
|
|
17
|
+
- `pyopenms`: For mass spectrometry data handling and algorithms.
|
|
18
|
+
- `polars` and `pandas`: For efficient data manipulation and analysis.
|
|
19
|
+
- `bokeh`, `holoviews`, `panel`: For interactive visualizations and dashboards.
|
|
20
|
+
- `numpy`: For numerical computations and array operations.
|
|
21
|
+
|
|
22
|
+
Classes:
|
|
23
|
+
- `study`: Main class for multi-sample study management, providing methods for file loading,
|
|
24
|
+
feature alignment, consensus generation, and study-level analysis.
|
|
25
|
+
|
|
26
|
+
Example Usage:
|
|
27
|
+
```python
|
|
28
|
+
from study import study
|
|
29
|
+
|
|
30
|
+
# Create study from multiple files
|
|
31
|
+
study_obj = study()
|
|
32
|
+
study_obj.load_files(["sample1.mzML", "sample2.mzML", "sample3.mzML"])
|
|
33
|
+
study_obj.process_all()
|
|
34
|
+
study_obj.align()
|
|
35
|
+
study_obj.plot_alignment_bokeh()
|
|
36
|
+
study_obj.export_consensus()
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See Also:
|
|
40
|
+
- `single.py`: For individual file processing before study-level analysis.
|
|
41
|
+
- `parameters.study_parameters`: For study-specific parameter configuration.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import importlib
|
|
49
|
+
import os
|
|
50
|
+
import sys
|
|
51
|
+
|
|
52
|
+
import polars as pl
|
|
53
|
+
|
|
54
|
+
# Study-specific imports
|
|
55
|
+
from masster.study.h5 import _load_study5
|
|
56
|
+
from masster.study.h5 import _save_study5
|
|
57
|
+
from masster.study.h5 import _save_study5_compressed
|
|
58
|
+
from masster.study.helpers import _get_consensus_uids
|
|
59
|
+
from masster.study.helpers import _get_feature_uids
|
|
60
|
+
from masster.study.helpers import _get_sample_uids
|
|
61
|
+
from masster.study.helpers import compress
|
|
62
|
+
from masster.study.helpers import compress_features
|
|
63
|
+
from masster.study.helpers import compress_ms2
|
|
64
|
+
from masster.study.helpers import compress_chrom
|
|
65
|
+
from masster.study.helpers import restore_features
|
|
66
|
+
from masster.study.helpers import restore_chrom
|
|
67
|
+
from masster.study.helpers import fill_reset
|
|
68
|
+
from masster.study.helpers import get_chrom
|
|
69
|
+
from masster.study.helpers import get_consensus
|
|
70
|
+
from masster.study.helpers import get_consensus_matches
|
|
71
|
+
from masster.study.helpers import get_consensus_matrix
|
|
72
|
+
from masster.study.helpers import get_orphans
|
|
73
|
+
from masster.study.helpers import get_gaps_matrix
|
|
74
|
+
from masster.study.helpers import get_gaps_stats
|
|
75
|
+
from masster.study.helpers import align_reset
|
|
76
|
+
from masster.study.helpers import set_folder
|
|
77
|
+
from masster.study.helpers import set_source
|
|
78
|
+
from masster.study.helpers import features_select
|
|
79
|
+
from masster.study.helpers import features_filter
|
|
80
|
+
from masster.study.helpers import features_delete
|
|
81
|
+
from masster.study.helpers import consensus_select
|
|
82
|
+
from masster.study.helpers import consensus_filter
|
|
83
|
+
from masster.study.helpers import consensus_delete
|
|
84
|
+
from masster.study.load import add
|
|
85
|
+
from masster.study.load import add_sample
|
|
86
|
+
from masster.study.load import fill_single
|
|
87
|
+
from masster.study.load import fill
|
|
88
|
+
from masster.study.load import _process_sample_for_parallel_fill
|
|
89
|
+
from masster.study.load import _get_missing_consensus_sample_combinations
|
|
90
|
+
from masster.study.load import load
|
|
91
|
+
from masster.study.load import _load_consensusXML
|
|
92
|
+
from masster.study.load import load_features
|
|
93
|
+
from masster.study.load import sanitize
|
|
94
|
+
from masster.study.plot import plot_alignment
|
|
95
|
+
from masster.study.plot import plot_alignment_bokeh
|
|
96
|
+
from masster.study.plot import plot_chrom
|
|
97
|
+
from masster.study.plot import plot_consensus_2d
|
|
98
|
+
from masster.study.plot import plot_samples_2d
|
|
99
|
+
from masster.study.processing import align
|
|
100
|
+
from masster.study.processing import filter_consensus
|
|
101
|
+
from masster.study.processing import merge
|
|
102
|
+
from masster.study.processing import integrate
|
|
103
|
+
from masster.study.processing import find_ms2
|
|
104
|
+
from masster.study.parameters import store_history
|
|
105
|
+
from masster.study.parameters import get_parameters
|
|
106
|
+
from masster.study.parameters import update_parameters
|
|
107
|
+
from masster.study.parameters import get_parameters_property
|
|
108
|
+
from masster.study.parameters import set_parameters_property
|
|
109
|
+
from masster.study.save import save
|
|
110
|
+
from masster.study.save import save_consensus
|
|
111
|
+
from masster.study.save import _save_consensusXML
|
|
112
|
+
from masster.study.save import save_samples
|
|
113
|
+
from masster.study.export import export_mgf
|
|
114
|
+
from masster.study.export import export_mztab
|
|
115
|
+
from masster.study.export import _get_mgf_df
|
|
116
|
+
|
|
117
|
+
from masster.logger import MassterLogger
|
|
118
|
+
from masster.study.defaults.study_def import study_defaults
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class Study:
|
|
122
|
+
"""
|
|
123
|
+
A class for managing and analyzing multi-sample mass spectrometry studies.
|
|
124
|
+
|
|
125
|
+
The `study` class provides comprehensive tools for handling collections of DDA
|
|
126
|
+
(Data-Dependent Acquisition) mass spectrometry files, performing cross-sample
|
|
127
|
+
feature alignment, generating consensus features, and conducting study-level
|
|
128
|
+
analysis and visualization.
|
|
129
|
+
|
|
130
|
+
Attributes:
|
|
131
|
+
folder (str): Default directory for study files and outputs.
|
|
132
|
+
ddafiles (dict): Dictionary containing loaded ddafile objects keyed by sample names.
|
|
133
|
+
features_df (pl.DataFrame): Combined features from all samples in the study.
|
|
134
|
+
consensus_df (pl.DataFrame): Consensus features generated from alignment.
|
|
135
|
+
metadata_df (pl.DataFrame): Sample metadata and experimental information.
|
|
136
|
+
|
|
137
|
+
Key Methods:
|
|
138
|
+
- `add_folder()`: Load all files from a directory into the study.
|
|
139
|
+
- `add_sample()`: Add individual sample files to the study.
|
|
140
|
+
- `process_all()`: Batch process all samples with feature detection.
|
|
141
|
+
- `align()`: Perform cross-sample feature alignment.
|
|
142
|
+
- `plot_alignment_bokeh()`: Visualize alignment results.
|
|
143
|
+
- `export_consensus()`: Export consensus features for downstream analysis.
|
|
144
|
+
|
|
145
|
+
Example Usage:
|
|
146
|
+
>>> from masster import study
|
|
147
|
+
>>> study_obj = study(folder="./data")
|
|
148
|
+
>>> study_obj.load_folder("./mzml_files")
|
|
149
|
+
>>> study_obj.process_all()
|
|
150
|
+
>>> study_obj.align()
|
|
151
|
+
>>> study_obj.plot_alignment_bokeh()
|
|
152
|
+
>>> study_obj.export_consensus("consensus_features.csv")
|
|
153
|
+
|
|
154
|
+
See Also:
|
|
155
|
+
- `ddafile`: For individual sample processing before study-level analysis.
|
|
156
|
+
- `StudyParameters`: For configuring study-specific parameters.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def __init__(
|
|
160
|
+
self,
|
|
161
|
+
filename=None,
|
|
162
|
+
**kwargs,
|
|
163
|
+
):
|
|
164
|
+
"""
|
|
165
|
+
Initialize a Study instance for multi-sample mass spectrometry analysis.
|
|
166
|
+
|
|
167
|
+
This constructor initializes various attributes related to file handling,
|
|
168
|
+
data storage, and processing parameters used for study-level analysis.
|
|
169
|
+
|
|
170
|
+
Parameters:
|
|
171
|
+
filename (str, optional): Path to a .study5 file to load automatically.
|
|
172
|
+
If provided, the folder will be set to the
|
|
173
|
+
directory containing this file, and the study will
|
|
174
|
+
be loaded automatically.
|
|
175
|
+
**kwargs: Keyword arguments for setting study parameters. Can include:
|
|
176
|
+
- A study_defaults instance to set all parameters at once (pass as params=study_defaults(...))
|
|
177
|
+
- Individual parameter names and values (see study_defaults for available parameters)
|
|
178
|
+
|
|
179
|
+
Core initialization parameters:
|
|
180
|
+
- folder (str, optional): Default directory for study files and outputs
|
|
181
|
+
- label (str, optional): An optional label to identify the study
|
|
182
|
+
- log_level (str): The logging level to be set for the logger. Defaults to 'INFO'
|
|
183
|
+
- log_label (str, optional): Optional label for the logger
|
|
184
|
+
- log_sink (str): Output sink for logging. Default is "sys.stdout"
|
|
185
|
+
|
|
186
|
+
For backward compatibility, original signature is supported:
|
|
187
|
+
Study(folder=..., label=..., log_level=..., log_label=..., log_sink=...)
|
|
188
|
+
"""
|
|
189
|
+
# Initialize default parameters
|
|
190
|
+
|
|
191
|
+
# Handle filename parameter for automatic loading
|
|
192
|
+
auto_load_filename = None
|
|
193
|
+
if filename is not None:
|
|
194
|
+
if not filename.endswith('.study5'):
|
|
195
|
+
raise ValueError("filename must be a .study5 file")
|
|
196
|
+
if not os.path.exists(filename):
|
|
197
|
+
raise FileNotFoundError(f"Study file not found: {filename}")
|
|
198
|
+
|
|
199
|
+
# Set folder to the directory containing the file if not already specified
|
|
200
|
+
if 'folder' not in kwargs:
|
|
201
|
+
kwargs['folder'] = os.path.dirname(os.path.abspath(filename))
|
|
202
|
+
|
|
203
|
+
auto_load_filename = filename
|
|
204
|
+
|
|
205
|
+
# Check if a study_defaults instance was passed
|
|
206
|
+
if "params" in kwargs and isinstance(kwargs["params"], study_defaults):
|
|
207
|
+
params = kwargs.pop("params")
|
|
208
|
+
else:
|
|
209
|
+
# Create default parameters and update with provided values
|
|
210
|
+
params = study_defaults()
|
|
211
|
+
|
|
212
|
+
# Update with any provided parameters
|
|
213
|
+
for key, value in kwargs.items():
|
|
214
|
+
if hasattr(params, key):
|
|
215
|
+
params.set(key, value, validate=True)
|
|
216
|
+
|
|
217
|
+
# Keeps a pointer to study5 whenever it's saved or loaded
|
|
218
|
+
self.filename = None
|
|
219
|
+
|
|
220
|
+
# Store parameter instance for method access
|
|
221
|
+
self.parameters = params
|
|
222
|
+
self.history = {}
|
|
223
|
+
self.store_history(["study"], params.to_dict())
|
|
224
|
+
|
|
225
|
+
# Set instance attributes (ensure proper string values for logger)
|
|
226
|
+
self.folder = params.folder
|
|
227
|
+
self.label = params.label
|
|
228
|
+
self.polarity = params.polarity if params.polarity in ["positive", "negative", "pos", "neg"] else "positive"
|
|
229
|
+
self.log_level = params.log_level.upper() if params.log_level else "INFO"
|
|
230
|
+
self.log_label = params.log_label + " | " if params.log_label else ""
|
|
231
|
+
self.log_sink = params.log_sink
|
|
232
|
+
|
|
233
|
+
if self.folder is not None and not os.path.exists(self.folder):
|
|
234
|
+
# create the folder if it does not exist
|
|
235
|
+
os.makedirs(self.folder)
|
|
236
|
+
|
|
237
|
+
self.samples_df = pl.DataFrame(
|
|
238
|
+
{
|
|
239
|
+
"sample_uid": [],
|
|
240
|
+
"sample_name": [],
|
|
241
|
+
"sample_path": [],
|
|
242
|
+
"sample_type": [],
|
|
243
|
+
"size": [],
|
|
244
|
+
"map_id": [],
|
|
245
|
+
"file_source": [],
|
|
246
|
+
},
|
|
247
|
+
schema={
|
|
248
|
+
"sample_uid": pl.Int64,
|
|
249
|
+
"sample_name": pl.Utf8,
|
|
250
|
+
"sample_path": pl.Utf8,
|
|
251
|
+
"sample_type": pl.Utf8,
|
|
252
|
+
"size": pl.Int64,
|
|
253
|
+
"map_id": pl.Utf8,
|
|
254
|
+
"file_source": pl.Utf8,
|
|
255
|
+
},
|
|
256
|
+
)
|
|
257
|
+
self.features_maps = []
|
|
258
|
+
self.features_df = pl.DataFrame()
|
|
259
|
+
self.consensus_ms2 = pl.DataFrame()
|
|
260
|
+
self.consensus_df = pl.DataFrame()
|
|
261
|
+
self.consensus_map = None
|
|
262
|
+
self.consensus_mapping_df = pl.DataFrame()
|
|
263
|
+
self.alignment_ref_index = None
|
|
264
|
+
|
|
265
|
+
# Initialize independent logger
|
|
266
|
+
self.logger = MassterLogger(
|
|
267
|
+
instance_type="study",
|
|
268
|
+
level=self.log_level.upper(),
|
|
269
|
+
label=self.log_label,
|
|
270
|
+
sink=self.log_sink,
|
|
271
|
+
)
|
|
272
|
+
self.logger.debug(f"Study folder: {self.folder}")
|
|
273
|
+
self.logger.debug(f"Polarity: {self.polarity}")
|
|
274
|
+
|
|
275
|
+
# Auto-load study file if filename was provided
|
|
276
|
+
if auto_load_filename is not None:
|
|
277
|
+
self.load(filename=auto_load_filename)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# Attach module functions as class methods
|
|
282
|
+
load = load
|
|
283
|
+
save = save
|
|
284
|
+
save_consensus = save_consensus
|
|
285
|
+
save_samples = save_samples
|
|
286
|
+
align = align
|
|
287
|
+
fill_single = fill_single
|
|
288
|
+
fill_chrom_single = fill_single # Backward compatibility alias
|
|
289
|
+
merge = merge
|
|
290
|
+
find_consensus = merge # Backward compatibility alias
|
|
291
|
+
find_ms2 = find_ms2
|
|
292
|
+
integrate = integrate
|
|
293
|
+
integrate_chrom = integrate # Backward compatibility alias
|
|
294
|
+
store_history = store_history
|
|
295
|
+
get_parameters = get_parameters
|
|
296
|
+
update_parameters = update_parameters
|
|
297
|
+
get_parameters_property = get_parameters_property
|
|
298
|
+
set_parameters_property = set_parameters_property
|
|
299
|
+
plot_alignment = plot_alignment
|
|
300
|
+
plot_alignment_bokeh = plot_alignment_bokeh
|
|
301
|
+
plot_chrom = plot_chrom
|
|
302
|
+
plot_consensus_2d = plot_consensus_2d
|
|
303
|
+
plot_samples_2d = plot_samples_2d
|
|
304
|
+
get_consensus = get_consensus
|
|
305
|
+
get_chrom = get_chrom
|
|
306
|
+
get_consensus_matches = get_consensus_matches
|
|
307
|
+
compress = compress
|
|
308
|
+
compress_features = compress_features
|
|
309
|
+
compress_ms2 = compress_ms2
|
|
310
|
+
compress_chrom = compress_chrom
|
|
311
|
+
restore_features = restore_features
|
|
312
|
+
restore_chrom = restore_chrom
|
|
313
|
+
fill_reset = fill_reset
|
|
314
|
+
align_reset = align_reset
|
|
315
|
+
set_source = set_source
|
|
316
|
+
features_select = features_select
|
|
317
|
+
features_filter = features_filter
|
|
318
|
+
features_delete = features_delete
|
|
319
|
+
consensus_select = consensus_select
|
|
320
|
+
consensus_filter = consensus_filter
|
|
321
|
+
consensus_delete = consensus_delete
|
|
322
|
+
filter_consensus = consensus_filter
|
|
323
|
+
select_consensus = consensus_select
|
|
324
|
+
filter_features = features_filter
|
|
325
|
+
select_features = features_select
|
|
326
|
+
consensus_find = merge
|
|
327
|
+
filter_features = features_filter
|
|
328
|
+
|
|
329
|
+
# Additional method assignments for all imported functions
|
|
330
|
+
add_folder = add # backward compatibility alias
|
|
331
|
+
add = add
|
|
332
|
+
add_sample = add_sample
|
|
333
|
+
_load_study5 = _load_study5
|
|
334
|
+
_save_study5 = _save_study5
|
|
335
|
+
_save_study5_compressed = _save_study5_compressed
|
|
336
|
+
_get_consensus_uids = _get_consensus_uids
|
|
337
|
+
_get_feature_uids = _get_feature_uids
|
|
338
|
+
_get_sample_uids = _get_sample_uids
|
|
339
|
+
get_consensus_matrix = get_consensus_matrix
|
|
340
|
+
get_gaps_matrix = get_gaps_matrix
|
|
341
|
+
get_gaps_stats = get_gaps_stats
|
|
342
|
+
get_orphans = get_orphans
|
|
343
|
+
set_folder = set_folder
|
|
344
|
+
fill = fill
|
|
345
|
+
fill_chrom = fill # Backward compatibility alias
|
|
346
|
+
_process_sample_for_parallel_fill = _process_sample_for_parallel_fill
|
|
347
|
+
_get_missing_consensus_sample_combinations = _get_missing_consensus_sample_combinations
|
|
348
|
+
_load_consensusXML = _load_consensusXML
|
|
349
|
+
load_features = load_features
|
|
350
|
+
sanitize = sanitize
|
|
351
|
+
_save_consensusXML = _save_consensusXML
|
|
352
|
+
export_mgf = export_mgf
|
|
353
|
+
export_mztab = export_mztab
|
|
354
|
+
_get_mgf_df = _get_mgf_df # New function for MGF data extraction
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _reload(self):
|
|
358
|
+
"""
|
|
359
|
+
Reloads all masster modules to pick up any changes to their source code,
|
|
360
|
+
and updates the instance's class reference to the newly reloaded class version.
|
|
361
|
+
This ensures that the instance uses the latest implementation without restarting the interpreter.
|
|
362
|
+
"""
|
|
363
|
+
# Reset logger configuration flags to allow proper reconfiguration after reload
|
|
364
|
+
''' try:
|
|
365
|
+
import masster.sample.logger as logger_module
|
|
366
|
+
|
|
367
|
+
if hasattr(logger_module, "_STUDY_LOGGER_CONFIGURED"):
|
|
368
|
+
logger_module._STUDY_LOGGER_CONFIGURED = False
|
|
369
|
+
except Exception:
|
|
370
|
+
pass'''
|
|
371
|
+
|
|
372
|
+
# Get the base module name (masster)
|
|
373
|
+
base_modname = self.__class__.__module__.split(".")[0]
|
|
374
|
+
current_module = self.__class__.__module__
|
|
375
|
+
|
|
376
|
+
# Dynamically find all study submodules
|
|
377
|
+
study_modules = []
|
|
378
|
+
study_module_prefix = f"{base_modname}.study."
|
|
379
|
+
|
|
380
|
+
# Get all currently loaded modules that are part of the study package
|
|
381
|
+
for module_name in sys.modules:
|
|
382
|
+
if module_name.startswith(study_module_prefix) and module_name != current_module:
|
|
383
|
+
study_modules.append(module_name)
|
|
384
|
+
|
|
385
|
+
# Add core masster modules
|
|
386
|
+
core_modules = [
|
|
387
|
+
f"{base_modname}._version",
|
|
388
|
+
f"{base_modname}.chromatogram",
|
|
389
|
+
f"{base_modname}.spectrum",
|
|
390
|
+
f"{base_modname}.parameters",
|
|
391
|
+
]
|
|
392
|
+
|
|
393
|
+
# Add any parameters submodules that are loaded
|
|
394
|
+
for module_name in sys.modules:
|
|
395
|
+
if module_name.startswith(f"{base_modname}.parameters.") and module_name not in core_modules:
|
|
396
|
+
core_modules.append(module_name)
|
|
397
|
+
|
|
398
|
+
all_modules_to_reload = core_modules + study_modules
|
|
399
|
+
|
|
400
|
+
# Reload all discovered modules
|
|
401
|
+
for full_module_name in all_modules_to_reload:
|
|
402
|
+
try:
|
|
403
|
+
if full_module_name in sys.modules:
|
|
404
|
+
mod = sys.modules[full_module_name]
|
|
405
|
+
importlib.reload(mod)
|
|
406
|
+
self.logger.debug(f"Reloaded module: {full_module_name}")
|
|
407
|
+
except Exception as e:
|
|
408
|
+
self.logger.warning(f"Failed to reload module {full_module_name}: {e}")
|
|
409
|
+
|
|
410
|
+
# Finally, reload the current module (sample.py)
|
|
411
|
+
try:
|
|
412
|
+
mod = __import__(current_module, fromlist=[current_module.split(".")[0]])
|
|
413
|
+
importlib.reload(mod)
|
|
414
|
+
|
|
415
|
+
# Get the updated class reference from the reloaded module
|
|
416
|
+
new = getattr(mod, self.__class__.__name__)
|
|
417
|
+
# Update the class reference of the instance
|
|
418
|
+
self.__class__ = new
|
|
419
|
+
|
|
420
|
+
self.logger.debug("Module reload completed")
|
|
421
|
+
except Exception as e:
|
|
422
|
+
self.logger.error(f"Failed to reload current module {current_module}: {e}")
|
|
423
|
+
|
|
424
|
+
def __str__(self):
|
|
425
|
+
"""
|
|
426
|
+
Returns a string representation of the study.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
str: A summary string of the study.
|
|
430
|
+
"""
|
|
431
|
+
return ""
|
|
432
|
+
|
|
433
|
+
def logger_update(self, level: str | None = None, label: str | None = None, sink: str | None = None):
|
|
434
|
+
"""Update the logging configuration for this Study instance.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
level: New logging level ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
|
|
438
|
+
label: New label for log messages
|
|
439
|
+
sink: New output sink (file path, file object, or "sys.stdout")
|
|
440
|
+
"""
|
|
441
|
+
if level is not None:
|
|
442
|
+
self.log_level = level.upper()
|
|
443
|
+
self.logger.update_level(level)
|
|
444
|
+
|
|
445
|
+
if label is not None:
|
|
446
|
+
self.log_label = label + " | " if len(label) > 0 else ""
|
|
447
|
+
self.logger.update_label(self.log_label)
|
|
448
|
+
|
|
449
|
+
if sink is not None:
|
|
450
|
+
if sink == "sys.stdout":
|
|
451
|
+
self.log_sink = sys.stdout
|
|
452
|
+
else:
|
|
453
|
+
self.log_sink = sink
|
|
454
|
+
self.logger.update_sink(self.log_sink)
|
|
455
|
+
|
|
456
|
+
def info(self):
|
|
457
|
+
"""
|
|
458
|
+
Display study information with optimized performance.
|
|
459
|
+
|
|
460
|
+
Returns a summary string of the study including folder, features count,
|
|
461
|
+
samples count, and various statistics.
|
|
462
|
+
"""
|
|
463
|
+
# Cache DataFrame lengths and existence checks
|
|
464
|
+
consensus_df_len = len(self.consensus_df) if not self.consensus_df.is_empty() else 0
|
|
465
|
+
samples_df_len = len(self.samples_df) if not self.samples_df.is_empty() else 0
|
|
466
|
+
|
|
467
|
+
# Calculate consensus statistics only if consensus_df exists and has data
|
|
468
|
+
if consensus_df_len > 0:
|
|
469
|
+
# Execute the aggregation once
|
|
470
|
+
stats_result = self.consensus_df.select([
|
|
471
|
+
pl.col("number_samples").min().alias("min_samples"),
|
|
472
|
+
pl.col("number_samples").mean().alias("mean_samples"),
|
|
473
|
+
pl.col("number_samples").max().alias("max_samples"),
|
|
474
|
+
]).row(0)
|
|
475
|
+
|
|
476
|
+
min_samples = stats_result[0] if stats_result[0] is not None else 0
|
|
477
|
+
mean_samples = stats_result[1] if stats_result[1] is not None else 0
|
|
478
|
+
max_samples = stats_result[2] if stats_result[2] is not None else 0
|
|
479
|
+
else:
|
|
480
|
+
min_samples = 0
|
|
481
|
+
mean_samples = 0
|
|
482
|
+
max_samples = 0
|
|
483
|
+
|
|
484
|
+
# Count only features where 'filled' == False
|
|
485
|
+
if not self.features_df.is_empty() and 'filled' in self.features_df.columns:
|
|
486
|
+
unfilled_features_count = self.features_df.filter(~self.features_df['filled']).height
|
|
487
|
+
else:
|
|
488
|
+
unfilled_features_count = 0
|
|
489
|
+
|
|
490
|
+
# Calculate features in consensus vs not in consensus (only for unfilled features)
|
|
491
|
+
if not self.features_df.is_empty() and not self.consensus_mapping_df.is_empty():
|
|
492
|
+
# Get unfilled features only
|
|
493
|
+
unfilled_features = self.features_df.filter(~self.features_df['filled']) if 'filled' in self.features_df.columns else self.features_df
|
|
494
|
+
|
|
495
|
+
# Ensure the column and list have matching data types
|
|
496
|
+
consensus_feature_uids = self.consensus_mapping_df['feature_uid'].to_list()
|
|
497
|
+
|
|
498
|
+
# Check if we need to cast either side to match types
|
|
499
|
+
unfilled_dtype = unfilled_features['feature_uid'].dtype
|
|
500
|
+
consensus_dtype = self.consensus_mapping_df['feature_uid'].dtype
|
|
501
|
+
|
|
502
|
+
if unfilled_dtype != consensus_dtype:
|
|
503
|
+
# Cast both to Int64 if possible, otherwise keep as string
|
|
504
|
+
try:
|
|
505
|
+
unfilled_features = unfilled_features.with_columns(pl.col('feature_uid').cast(pl.Int64))
|
|
506
|
+
consensus_feature_uids = [int(uid) for uid in consensus_feature_uids]
|
|
507
|
+
except Exception:
|
|
508
|
+
# If casting fails, ensure both are strings
|
|
509
|
+
unfilled_features = unfilled_features.with_columns(pl.col('feature_uid').cast(pl.Utf8))
|
|
510
|
+
consensus_feature_uids = [str(uid) for uid in consensus_feature_uids]
|
|
511
|
+
|
|
512
|
+
# Count unfilled features that are in consensus
|
|
513
|
+
in_consensus_count = unfilled_features.filter(
|
|
514
|
+
pl.col('feature_uid').is_in(consensus_feature_uids)
|
|
515
|
+
).height
|
|
516
|
+
|
|
517
|
+
# Calculate ratios that sum to 100%
|
|
518
|
+
total_unfilled = unfilled_features.height
|
|
519
|
+
ratio_in_consensus_to_total = (in_consensus_count / total_unfilled * 100) if total_unfilled > 0 else 0
|
|
520
|
+
ratio_not_in_consensus_to_total = 100 - ratio_in_consensus_to_total if total_unfilled > 0 else 0
|
|
521
|
+
else:
|
|
522
|
+
ratio_in_consensus_to_total = 0
|
|
523
|
+
ratio_not_in_consensus_to_total = 0
|
|
524
|
+
|
|
525
|
+
# Optimize chrom completeness calculation
|
|
526
|
+
if consensus_df_len > 0 and samples_df_len > 0 and not self.features_df.is_empty():
|
|
527
|
+
# Ensure matching data types for join keys
|
|
528
|
+
features_dtype = self.features_df["feature_uid"].dtype
|
|
529
|
+
consensus_dtype = self.consensus_mapping_df["feature_uid"].dtype
|
|
530
|
+
|
|
531
|
+
if features_dtype != consensus_dtype:
|
|
532
|
+
# Try to cast both to Int64, fallback to string if needed
|
|
533
|
+
try:
|
|
534
|
+
self.features_df = self.features_df.with_columns(pl.col("feature_uid").cast(pl.Int64))
|
|
535
|
+
self.consensus_mapping_df = self.consensus_mapping_df.with_columns(pl.col("feature_uid").cast(pl.Int64))
|
|
536
|
+
except Exception:
|
|
537
|
+
# If casting to Int64 fails, cast both to string
|
|
538
|
+
self.features_df = self.features_df.with_columns(pl.col("feature_uid").cast(pl.Utf8))
|
|
539
|
+
self.consensus_mapping_df = self.consensus_mapping_df.with_columns(pl.col("feature_uid").cast(pl.Utf8))
|
|
540
|
+
|
|
541
|
+
# Use more efficient counting - count non-null chroms only for features in consensus mapping
|
|
542
|
+
if not self.consensus_mapping_df.is_empty():
|
|
543
|
+
non_null_chroms = (
|
|
544
|
+
self.features_df.join(
|
|
545
|
+
self.consensus_mapping_df.select("feature_uid"),
|
|
546
|
+
on="feature_uid",
|
|
547
|
+
how="inner",
|
|
548
|
+
)
|
|
549
|
+
.select(
|
|
550
|
+
pl.col("chrom").is_not_null().sum().alias("count"),
|
|
551
|
+
)
|
|
552
|
+
.item()
|
|
553
|
+
)
|
|
554
|
+
else:
|
|
555
|
+
non_null_chroms = 0
|
|
556
|
+
total_possible = samples_df_len * consensus_df_len
|
|
557
|
+
chrom_completeness = (
|
|
558
|
+
non_null_chroms / total_possible if total_possible > 0 else 0
|
|
559
|
+
)
|
|
560
|
+
else:
|
|
561
|
+
chrom_completeness = 0
|
|
562
|
+
|
|
563
|
+
# Calculate consensus features with MS2 (count unique consensus_uids with MS2)
|
|
564
|
+
if not self.consensus_ms2.is_empty():
|
|
565
|
+
consensus_with_ms2_count = self.consensus_ms2["consensus_uid"].n_unique()
|
|
566
|
+
else:
|
|
567
|
+
consensus_with_ms2_count = 0
|
|
568
|
+
|
|
569
|
+
# Calculate percentage of consensus features with MS2
|
|
570
|
+
consensus_with_ms2_percentage = (consensus_with_ms2_count / consensus_df_len * 100) if consensus_df_len > 0 else 0
|
|
571
|
+
|
|
572
|
+
# Total MS2 spectra count
|
|
573
|
+
total_ms2_count = len(self.consensus_ms2) if not self.consensus_ms2.is_empty() else 0
|
|
574
|
+
|
|
575
|
+
# estimate memory usage
|
|
576
|
+
memory_usage = (
|
|
577
|
+
self.samples_df.estimated_size()
|
|
578
|
+
+ self.features_df.estimated_size()
|
|
579
|
+
+ self.consensus_df.estimated_size()
|
|
580
|
+
+ self.consensus_ms2.estimated_size()
|
|
581
|
+
+ self.consensus_mapping_df.estimated_size()
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
summary = (
|
|
585
|
+
f"Study folder: {self.folder}\n"
|
|
586
|
+
f"Last save: {self.filename}\n"
|
|
587
|
+
f"Samples: {samples_df_len}\n"
|
|
588
|
+
f"Features: {unfilled_features_count}\n"
|
|
589
|
+
f"- in consensus: {ratio_in_consensus_to_total:.0f}%\n"
|
|
590
|
+
f"- not in consensus: {ratio_not_in_consensus_to_total:.0f}%\n"
|
|
591
|
+
f"Consensus: {consensus_df_len}\n"
|
|
592
|
+
f"- Min samples count: {min_samples:.0f}\n"
|
|
593
|
+
f"- Mean samples count: {mean_samples:.0f}\n"
|
|
594
|
+
f"- Max samples count: {max_samples:.0f}\n"
|
|
595
|
+
f"- with MS2: {consensus_with_ms2_percentage:.0f}%\n"
|
|
596
|
+
f"- total MS2: {total_ms2_count}\n"
|
|
597
|
+
f"Chrom completeness: {chrom_completeness*100:.0f}%\n"
|
|
598
|
+
f"Memory usage: {memory_usage / (1024 ** 2):.2f} MB\n"
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
print(summary)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
if __name__ == "__main__":
|
|
605
|
+
# This block is executed when the script is run directly
|
|
606
|
+
pass
|