masster 0.3.10__py3-none-any.whl → 0.3.11__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/docs/SCX_API_Documentation.md +0 -0
- masster/docs/SCX_DLL_Analysis.md +0 -0
- masster/logger.py +92 -78
- masster/sample/defaults/find_features_def.py +16 -6
- masster/sample/defaults/sample_def.py +1 -1
- masster/sample/h5.py +2 -2
- masster/sample/helpers.py +137 -136
- masster/sample/load.py +13 -9
- masster/sample/plot.py +156 -131
- masster/sample/processing.py +18 -12
- masster/sample/sample.py +4 -4
- masster/sample/sample5_schema.json +62 -62
- masster/sample/save.py +16 -13
- masster/sample/sciex.py +187 -176
- masster/study/defaults/align_def.py +224 -6
- masster/study/defaults/fill_chrom_def.py +1 -5
- masster/study/defaults/integrate_chrom_def.py +1 -5
- masster/study/defaults/study_def.py +2 -2
- masster/study/export.py +144 -131
- masster/study/h5.py +193 -133
- masster/study/helpers.py +293 -245
- masster/study/helpers_optimized.py +99 -57
- masster/study/load.py +51 -25
- masster/study/plot.py +453 -17
- masster/study/processing.py +159 -76
- masster/study/save.py +7 -7
- masster/study/study.py +97 -88
- masster/study/study5_schema.json +82 -82
- {masster-0.3.10.dist-info → masster-0.3.11.dist-info}/METADATA +1 -1
- {masster-0.3.10.dist-info → masster-0.3.11.dist-info}/RECORD +33 -31
- {masster-0.3.10.dist-info → masster-0.3.11.dist-info}/WHEEL +0 -0
- {masster-0.3.10.dist-info → masster-0.3.11.dist-info}/entry_points.txt +0 -0
- {masster-0.3.10.dist-info → masster-0.3.11.dist-info}/licenses/LICENSE +0 -0
masster/sample/load.py
CHANGED
|
@@ -85,9 +85,9 @@ def load(
|
|
|
85
85
|
filename = self.file_path
|
|
86
86
|
filename = os.path.abspath(filename)
|
|
87
87
|
if not os.path.exists(filename):
|
|
88
|
-
raise FileNotFoundError(
|
|
88
|
+
raise FileNotFoundError("Filename not valid. Provide a valid file path.")
|
|
89
89
|
self.ondisk = ondisk
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
# check if file is mzML
|
|
92
92
|
if filename.lower().endswith(".mzml"):
|
|
93
93
|
self._load_mzML(filename)
|
|
@@ -97,7 +97,7 @@ def load(
|
|
|
97
97
|
self._load_raw(filename)
|
|
98
98
|
elif filename.lower().endswith(".sample5"):
|
|
99
99
|
self._load_sample5(filename)
|
|
100
|
-
#elif filename.lower().endswith(".h5"):
|
|
100
|
+
# elif filename.lower().endswith(".h5"):
|
|
101
101
|
# self._load_h5(filename)
|
|
102
102
|
else:
|
|
103
103
|
raise ValueError("File must be .mzML, .wiff, *.raw, or .sample5")
|
|
@@ -137,7 +137,7 @@ def _load_mzML(
|
|
|
137
137
|
# check if filename exists
|
|
138
138
|
if filename is None:
|
|
139
139
|
raise ValueError("Filename must be provided.")
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
filename = os.path.abspath(filename)
|
|
142
142
|
# check if it exists
|
|
143
143
|
if not os.path.exists(filename):
|
|
@@ -310,7 +310,7 @@ def _load_raw(
|
|
|
310
310
|
- Initiates further analysis by invoking analyze_dda().
|
|
311
311
|
"""
|
|
312
312
|
from alpharaw.thermo import ThermoRawData
|
|
313
|
-
|
|
313
|
+
|
|
314
314
|
if not filename:
|
|
315
315
|
raise ValueError("Filename must be provided.")
|
|
316
316
|
|
|
@@ -318,7 +318,7 @@ def _load_raw(
|
|
|
318
318
|
# check if it exists
|
|
319
319
|
if not os.path.exists(filename):
|
|
320
320
|
raise FileNotFoundError(f"File {filename} not found.")
|
|
321
|
-
|
|
321
|
+
|
|
322
322
|
raw_data = ThermoRawData(centroided=False)
|
|
323
323
|
raw_data.keep_k_peaks_per_spec = self.parameters.max_points_per_spectrum
|
|
324
324
|
# check thatupdat filename ends with .raw
|
|
@@ -470,10 +470,12 @@ def _load_wiff(
|
|
|
470
470
|
try:
|
|
471
471
|
# Use masster's own implementation first
|
|
472
472
|
from masster.sample.sciex import SciexWiffData as MassterSciexWiffData
|
|
473
|
+
|
|
473
474
|
SciexWiffDataClass = MassterSciexWiffData
|
|
474
475
|
except ImportError:
|
|
475
476
|
# Fallback to alpharaw if masster implementation fails
|
|
476
477
|
from alpharaw.sciex import SciexWiffData as AlpharawSciexWiffData
|
|
478
|
+
|
|
477
479
|
SciexWiffDataClass = AlpharawSciexWiffData
|
|
478
480
|
|
|
479
481
|
if not filename:
|
|
@@ -483,7 +485,7 @@ def _load_wiff(
|
|
|
483
485
|
# check if it exists
|
|
484
486
|
if not os.path.exists(filename):
|
|
485
487
|
raise FileNotFoundError(f"File {filename} not found.")
|
|
486
|
-
|
|
488
|
+
|
|
487
489
|
raw_data = SciexWiffDataClass(centroided=False)
|
|
488
490
|
raw_data.keep_k_peaks_per_spec = self.parameters.max_points_per_spectrum
|
|
489
491
|
|
|
@@ -911,7 +913,7 @@ def index_file(self):
|
|
|
911
913
|
oms.MzMLFile().load(self.file_source, omsexp)
|
|
912
914
|
self.file_obj = omsexp
|
|
913
915
|
elif os.path.exists(self.file_source) and self.file_source.lower().endswith(".sample5"):
|
|
914
|
-
# this is an old save, try to see if
|
|
916
|
+
# this is an old save, try to see if
|
|
915
917
|
if os.path.exists(self.file_source.replace(".sample5", ".wiff")):
|
|
916
918
|
self.set_source(self.file_source.replace(".sample5", ".wiff"))
|
|
917
919
|
elif os.path.exists(self.file_source.replace(".sample5", ".raw")):
|
|
@@ -919,7 +921,9 @@ def index_file(self):
|
|
|
919
921
|
elif os.path.exists(self.file_source.replace(".sample5", ".mzml")):
|
|
920
922
|
self.set_source(self.file_source.replace(".sample5", ".mzml"))
|
|
921
923
|
else:
|
|
922
|
-
raise FileNotFoundError(
|
|
924
|
+
raise FileNotFoundError(
|
|
925
|
+
f"File {self.file_source} not found. Did the path change? Consider running source()."
|
|
926
|
+
)
|
|
923
927
|
self.index_file()
|
|
924
928
|
else:
|
|
925
929
|
raise FileNotFoundError(f"File {self.file_source} not found. Did the path change? Consider running source().")
|
masster/sample/plot.py
CHANGED
|
@@ -66,49 +66,51 @@ hv.extension("bokeh")
|
|
|
66
66
|
def _is_notebook_environment():
|
|
67
67
|
"""
|
|
68
68
|
Detect if code is running in a notebook environment (Jupyter, JupyterLab, or Marimo).
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
Returns:
|
|
71
71
|
bool: True if running in a notebook, False otherwise
|
|
72
72
|
"""
|
|
73
73
|
try:
|
|
74
74
|
# Check for Jupyter/JupyterLab
|
|
75
75
|
from IPython import get_ipython
|
|
76
|
+
|
|
76
77
|
if get_ipython() is not None:
|
|
77
78
|
# Check if we're in a notebook context
|
|
78
79
|
shell = get_ipython().__class__.__name__
|
|
79
|
-
if shell in [
|
|
80
|
+
if shell in ["ZMQInteractiveShell", "Shell"]: # Jupyter notebook/lab
|
|
80
81
|
return True
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
# Check for Marimo
|
|
83
84
|
import sys
|
|
84
|
-
|
|
85
|
+
|
|
86
|
+
if "marimo" in sys.modules:
|
|
85
87
|
return True
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
# Additional check for notebook environments
|
|
88
|
-
if hasattr(__builtins__,
|
|
90
|
+
if hasattr(__builtins__, "__IPYTHON__") or hasattr(__builtins__, "_ih"):
|
|
89
91
|
return True
|
|
90
|
-
|
|
92
|
+
|
|
91
93
|
except ImportError:
|
|
92
94
|
pass
|
|
93
|
-
|
|
95
|
+
|
|
94
96
|
return False
|
|
95
97
|
|
|
96
98
|
|
|
97
99
|
def _display_plot(plot_object, layout=None):
|
|
98
100
|
"""
|
|
99
101
|
Display a plot object in the appropriate way based on the environment.
|
|
100
|
-
|
|
102
|
+
|
|
101
103
|
Args:
|
|
102
104
|
plot_object: The plot object to display (holoviews overlay, etc.)
|
|
103
105
|
layout: Optional panel layout object
|
|
104
|
-
|
|
106
|
+
|
|
105
107
|
Returns:
|
|
106
108
|
The layout object if in notebook environment, None otherwise
|
|
107
109
|
"""
|
|
108
110
|
if _is_notebook_environment():
|
|
109
111
|
# Display inline in notebook
|
|
110
112
|
try:
|
|
111
|
-
# For Jupyter notebooks, just return the plot object -
|
|
113
|
+
# For Jupyter notebooks, just return the plot object -
|
|
112
114
|
# holoviews will handle the display automatically
|
|
113
115
|
return plot_object
|
|
114
116
|
except Exception:
|
|
@@ -395,17 +397,18 @@ def plot_2d(
|
|
|
395
397
|
# Configure marker and size behavior based on size parameter
|
|
396
398
|
use_dynamic_sizing = size.lower() in ["dyn", "dynamic"]
|
|
397
399
|
use_slider_sizing = size.lower() == "slider"
|
|
398
|
-
|
|
400
|
+
|
|
399
401
|
def dynamic_sizing_hook(plot, element):
|
|
400
402
|
"""Hook to convert size-based markers to radius-based for dynamic behavior"""
|
|
401
403
|
try:
|
|
402
|
-
if use_dynamic_sizing and hasattr(plot,
|
|
404
|
+
if use_dynamic_sizing and hasattr(plot, "state") and hasattr(plot.state, "renderers"):
|
|
403
405
|
from bokeh.models import Circle
|
|
406
|
+
|
|
404
407
|
for renderer in plot.state.renderers:
|
|
405
|
-
if hasattr(renderer,
|
|
408
|
+
if hasattr(renderer, "glyph"):
|
|
406
409
|
glyph = renderer.glyph
|
|
407
410
|
# Check if it's a circle/scatter glyph that we can convert
|
|
408
|
-
if hasattr(glyph,
|
|
411
|
+
if hasattr(glyph, "size") and marker_type == "circle":
|
|
409
412
|
# Create a new Circle glyph with radius instead of size
|
|
410
413
|
new_glyph = Circle(
|
|
411
414
|
x=glyph.x,
|
|
@@ -420,7 +423,7 @@ def plot_2d(
|
|
|
420
423
|
except Exception:
|
|
421
424
|
# Silently fail and use regular sizing if hook doesn't work
|
|
422
425
|
pass
|
|
423
|
-
|
|
426
|
+
|
|
424
427
|
if use_dynamic_sizing:
|
|
425
428
|
# Dynamic sizing: use coordinate-based sizing that scales with zoom
|
|
426
429
|
marker_type = "circle"
|
|
@@ -446,7 +449,7 @@ def plot_2d(
|
|
|
446
449
|
size_2 = markersize
|
|
447
450
|
base_radius = None # Not used in static mode
|
|
448
451
|
hooks = []
|
|
449
|
-
|
|
452
|
+
|
|
450
453
|
color_1 = "forestgreen"
|
|
451
454
|
color_2 = "darkorange"
|
|
452
455
|
if filename is not None:
|
|
@@ -519,14 +522,16 @@ def plot_2d(
|
|
|
519
522
|
# find features with ms2_scans not None and iso==0
|
|
520
523
|
features_df = feats[feats["ms2_scans"].notnull()]
|
|
521
524
|
# Create feature points with proper sizing method
|
|
522
|
-
feature_hover_1 = HoverTool(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
525
|
+
feature_hover_1 = HoverTool(
|
|
526
|
+
tooltips=[
|
|
527
|
+
("rt", "@rt"),
|
|
528
|
+
("m/z", "@mz{0.0000}"),
|
|
529
|
+
("feature_uid", "@feature_uid"),
|
|
530
|
+
("inty", "@inty"),
|
|
531
|
+
("quality", "@quality"),
|
|
532
|
+
("rt_delta", "@rt_delta"),
|
|
533
|
+
],
|
|
534
|
+
)
|
|
530
535
|
feature_points_1 = hv.Points(
|
|
531
536
|
features_df,
|
|
532
537
|
kdims=["rt", "mz"],
|
|
@@ -549,14 +554,16 @@ def plot_2d(
|
|
|
549
554
|
)
|
|
550
555
|
# find features without MS2 data
|
|
551
556
|
features_df = feats[feats["ms2_scans"].isnull()]
|
|
552
|
-
feature_hover_2 = HoverTool(
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
557
|
+
feature_hover_2 = HoverTool(
|
|
558
|
+
tooltips=[
|
|
559
|
+
("rt", "@rt"),
|
|
560
|
+
("m/z", "@mz{0.0000}"),
|
|
561
|
+
("feature_uid", "@feature_uid"),
|
|
562
|
+
("inty", "@inty"),
|
|
563
|
+
("quality", "@quality"),
|
|
564
|
+
("rt_delta", "@rt_delta"),
|
|
565
|
+
],
|
|
566
|
+
)
|
|
560
567
|
feature_points_2 = hv.Points(
|
|
561
568
|
features_df,
|
|
562
569
|
kdims=["rt", "mz"],
|
|
@@ -583,16 +590,18 @@ def plot_2d(
|
|
|
583
590
|
# Convert to pandas for plotting compatibility
|
|
584
591
|
if hasattr(features_df, "to_pandas"):
|
|
585
592
|
features_df = features_df.to_pandas()
|
|
586
|
-
feature_hover_iso = HoverTool(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
593
|
+
feature_hover_iso = HoverTool(
|
|
594
|
+
tooltips=[
|
|
595
|
+
("rt", "@rt"),
|
|
596
|
+
("m/z", "@mz{0.0000}"),
|
|
597
|
+
("feature_uid", "@feature_uid"),
|
|
598
|
+
("inty", "@inty"),
|
|
599
|
+
("quality", "@quality"),
|
|
600
|
+
("rt_delta", "@rt_delta"),
|
|
601
|
+
("iso", "@iso"),
|
|
602
|
+
("iso_of", "@iso_of"),
|
|
603
|
+
],
|
|
604
|
+
)
|
|
596
605
|
feature_points_iso = hv.Points(
|
|
597
606
|
features_df,
|
|
598
607
|
kdims=["rt", "mz"],
|
|
@@ -623,13 +632,15 @@ def plot_2d(
|
|
|
623
632
|
if len(ms2_orphan) > 0:
|
|
624
633
|
# pandalize
|
|
625
634
|
ms2 = ms2_orphan.to_pandas()
|
|
626
|
-
ms2_hover_3 = HoverTool(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
635
|
+
ms2_hover_3 = HoverTool(
|
|
636
|
+
tooltips=[
|
|
637
|
+
("rt", "@rt"),
|
|
638
|
+
("prec_mz", "@prec_mz{0.0000}"),
|
|
639
|
+
("index", "@index"),
|
|
640
|
+
("inty_tot", "@inty_tot"),
|
|
641
|
+
("bl", "@bl"),
|
|
642
|
+
],
|
|
643
|
+
)
|
|
633
644
|
feature_points_3 = hv.Points(
|
|
634
645
|
ms2,
|
|
635
646
|
kdims=["rt", "prec_mz"],
|
|
@@ -648,13 +659,15 @@ def plot_2d(
|
|
|
648
659
|
if len(ms2_linked) > 0:
|
|
649
660
|
# pandalize
|
|
650
661
|
ms2 = ms2_linked.to_pandas()
|
|
651
|
-
ms2_hover_4 = HoverTool(
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
662
|
+
ms2_hover_4 = HoverTool(
|
|
663
|
+
tooltips=[
|
|
664
|
+
("rt", "@rt"),
|
|
665
|
+
("prec_mz", "@prec_mz{0.0000}"),
|
|
666
|
+
("index", "@index"),
|
|
667
|
+
("inty_tot", "@inty_tot"),
|
|
668
|
+
("bl", "@bl"),
|
|
669
|
+
],
|
|
670
|
+
)
|
|
658
671
|
feature_points_4 = hv.Points(
|
|
659
672
|
ms2,
|
|
660
673
|
kdims=["rt", "prec_mz"],
|
|
@@ -688,17 +701,17 @@ def plot_2d(
|
|
|
688
701
|
# For slider functionality, we need to work with the feature points directly
|
|
689
702
|
# and not nest DynamicMaps. We'll create the slider using param and panel.
|
|
690
703
|
import param
|
|
691
|
-
import panel as
|
|
692
|
-
|
|
704
|
+
import panel as on
|
|
705
|
+
|
|
693
706
|
class MarkerSizeController(param.Parameterized):
|
|
694
707
|
size_slider = param.Number(default=markersize, bounds=(1, 20), step=0.5)
|
|
695
|
-
|
|
708
|
+
|
|
696
709
|
controller = MarkerSizeController()
|
|
697
|
-
|
|
710
|
+
|
|
698
711
|
# Create a function that generates just the feature overlays with different sizes
|
|
699
712
|
def create_feature_overlay(size_val):
|
|
700
713
|
feature_overlay = None
|
|
701
|
-
|
|
714
|
+
|
|
702
715
|
if feature_points_4 is not None:
|
|
703
716
|
updated_points_4 = feature_points_4.opts(size=size_val)
|
|
704
717
|
feature_overlay = updated_points_4 if feature_overlay is None else feature_overlay * updated_points_4
|
|
@@ -713,22 +726,24 @@ def plot_2d(
|
|
|
713
726
|
feature_overlay = updated_points_2 if feature_overlay is None else feature_overlay * updated_points_2
|
|
714
727
|
if feature_points_iso is not None:
|
|
715
728
|
updated_points_iso = feature_points_iso.opts(size=size_val)
|
|
716
|
-
feature_overlay =
|
|
717
|
-
|
|
729
|
+
feature_overlay = (
|
|
730
|
+
updated_points_iso if feature_overlay is None else feature_overlay * updated_points_iso
|
|
731
|
+
)
|
|
732
|
+
|
|
718
733
|
# Combine with the static raster background
|
|
719
734
|
if feature_overlay is not None:
|
|
720
735
|
combined_overlay = raster * feature_overlay
|
|
721
736
|
else:
|
|
722
737
|
combined_overlay = raster
|
|
723
|
-
|
|
738
|
+
|
|
724
739
|
if title is not None:
|
|
725
740
|
combined_overlay = combined_overlay.opts(title=title)
|
|
726
|
-
|
|
741
|
+
|
|
727
742
|
return combined_overlay
|
|
728
|
-
|
|
743
|
+
|
|
729
744
|
# Create a horizontal control widget on top of the plot
|
|
730
745
|
# Create the slider widget with explicit visibility
|
|
731
|
-
size_slider =
|
|
746
|
+
size_slider = on.widgets.FloatSlider(
|
|
732
747
|
name="Marker Size",
|
|
733
748
|
start=1.0,
|
|
734
749
|
end=20.0,
|
|
@@ -737,19 +752,19 @@ def plot_2d(
|
|
|
737
752
|
width=300,
|
|
738
753
|
height=40,
|
|
739
754
|
margin=(5, 5),
|
|
740
|
-
show_value=True
|
|
755
|
+
show_value=True,
|
|
741
756
|
)
|
|
742
|
-
|
|
757
|
+
|
|
743
758
|
# Create the slider widget row with clear styling
|
|
744
|
-
slider_widget =
|
|
745
|
-
|
|
759
|
+
slider_widget = on.Row(
|
|
760
|
+
on.pane.HTML("<b>Marker Size Control:</b>", width=150, height=40, margin=(5, 10)),
|
|
746
761
|
size_slider,
|
|
747
762
|
height=60,
|
|
748
|
-
margin=10
|
|
763
|
+
margin=10,
|
|
749
764
|
)
|
|
750
|
-
|
|
765
|
+
|
|
751
766
|
# Create slider widget
|
|
752
|
-
size_slider =
|
|
767
|
+
size_slider = on.widgets.FloatSlider(
|
|
753
768
|
name="Marker Size",
|
|
754
769
|
start=1.0,
|
|
755
770
|
end=20.0,
|
|
@@ -758,18 +773,18 @@ def plot_2d(
|
|
|
758
773
|
width=300,
|
|
759
774
|
height=40,
|
|
760
775
|
margin=(5, 5),
|
|
761
|
-
show_value=True
|
|
776
|
+
show_value=True,
|
|
762
777
|
)
|
|
763
|
-
|
|
764
|
-
slider_widget =
|
|
765
|
-
|
|
778
|
+
|
|
779
|
+
slider_widget = on.Row(
|
|
780
|
+
on.pane.HTML("<b>Marker Size:</b>", width=100, height=40, margin=(5, 10)),
|
|
766
781
|
size_slider,
|
|
767
782
|
height=60,
|
|
768
|
-
margin=10
|
|
783
|
+
margin=10,
|
|
769
784
|
)
|
|
770
|
-
|
|
785
|
+
|
|
771
786
|
# Simple reactive plot - slider mode doesn't use dynamic rasterization
|
|
772
|
-
@
|
|
787
|
+
@on.depends(size_slider.param.value)
|
|
773
788
|
def reactive_plot(size_val):
|
|
774
789
|
overlay = create_feature_overlay(float(size_val))
|
|
775
790
|
# Apply static rasterization for slider mode
|
|
@@ -779,19 +794,19 @@ def plot_2d(
|
|
|
779
794
|
aggregator=ds.count(),
|
|
780
795
|
width=raster_max_px,
|
|
781
796
|
height=raster_max_px,
|
|
782
|
-
dynamic=False # Static raster for slider mode
|
|
797
|
+
dynamic=False, # Static raster for slider mode
|
|
783
798
|
).opts(
|
|
784
|
-
cnorm=
|
|
785
|
-
tools=[
|
|
799
|
+
cnorm="eq_hist",
|
|
800
|
+
tools=["hover"],
|
|
786
801
|
width=width,
|
|
787
|
-
height=height
|
|
802
|
+
height=height,
|
|
788
803
|
)
|
|
789
804
|
else:
|
|
790
805
|
return overlay
|
|
791
|
-
|
|
806
|
+
|
|
792
807
|
# Create layout
|
|
793
|
-
layout =
|
|
794
|
-
|
|
808
|
+
layout = on.Column(slider_widget, reactive_plot, sizing_mode="stretch_width")
|
|
809
|
+
|
|
795
810
|
return layout
|
|
796
811
|
else:
|
|
797
812
|
# Create a panel layout without slider
|
|
@@ -1081,18 +1096,20 @@ def plot_2d_oracle(
|
|
|
1081
1096
|
feat_df = feats.copy()
|
|
1082
1097
|
feat_df = feat_df[feat_df["id_level"] == 2]
|
|
1083
1098
|
|
|
1084
|
-
oracle_hover_1 = HoverTool(
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1099
|
+
oracle_hover_1 = HoverTool(
|
|
1100
|
+
tooltips=[
|
|
1101
|
+
("rt", "@rt"),
|
|
1102
|
+
("m/z", "@mz{0.0000}"),
|
|
1103
|
+
("feature_uid", "@feature_uid"),
|
|
1104
|
+
("id_level", "@id_level"),
|
|
1105
|
+
("id_class", "@id_class"),
|
|
1106
|
+
("id_label", "@id_label"),
|
|
1107
|
+
("id_ion", "@id_ion"),
|
|
1108
|
+
("id_evidence", "@id_evidence"),
|
|
1109
|
+
("score", "@score"),
|
|
1110
|
+
("score2", "@score2"),
|
|
1111
|
+
],
|
|
1112
|
+
)
|
|
1096
1113
|
feature_points_1 = hv.Points(
|
|
1097
1114
|
feat_df,
|
|
1098
1115
|
kdims=["rt", "mz"],
|
|
@@ -1122,15 +1139,17 @@ def plot_2d_oracle(
|
|
|
1122
1139
|
feat_df = feats.copy()
|
|
1123
1140
|
feat_df = feat_df[(feat_df["ms2_scans"].notnull()) & (feat_df["id_level"] == 1)]
|
|
1124
1141
|
if len(feat_df) > 0:
|
|
1125
|
-
oracle_hover_2 = HoverTool(
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1142
|
+
oracle_hover_2 = HoverTool(
|
|
1143
|
+
tooltips=[
|
|
1144
|
+
("rt", "@rt"),
|
|
1145
|
+
("m/z", "@mz{0.0000}"),
|
|
1146
|
+
("feature_uid", "@feature_uid"),
|
|
1147
|
+
("id_level", "@id_level"),
|
|
1148
|
+
("id_label", "@id_label"),
|
|
1149
|
+
("id_ion", "@id_ion"),
|
|
1150
|
+
("id_class", "@id_class"),
|
|
1151
|
+
],
|
|
1152
|
+
)
|
|
1134
1153
|
feature_points_2 = hv.Points(
|
|
1135
1154
|
feat_df,
|
|
1136
1155
|
kdims=["rt", "mz"],
|
|
@@ -1157,15 +1176,17 @@ def plot_2d_oracle(
|
|
|
1157
1176
|
feat_df = feats.copy()
|
|
1158
1177
|
feat_df = feat_df[(feat_df["ms2_scans"].isnull()) & (feat_df["id_level"] == 1)]
|
|
1159
1178
|
if len(feat_df) > 0:
|
|
1160
|
-
oracle_hover_3 = HoverTool(
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1179
|
+
oracle_hover_3 = HoverTool(
|
|
1180
|
+
tooltips=[
|
|
1181
|
+
("rt", "@rt"),
|
|
1182
|
+
("m/z", "@mz{0.0000}"),
|
|
1183
|
+
("feature_uid", "@feature_uid"),
|
|
1184
|
+
("id_level", "@id_level"),
|
|
1185
|
+
("id_label", "@id_label"),
|
|
1186
|
+
("id_ion", "@id_ion"),
|
|
1187
|
+
("id_class", "@id_class"),
|
|
1188
|
+
],
|
|
1189
|
+
)
|
|
1169
1190
|
feature_points_3 = hv.Points(
|
|
1170
1191
|
feat_df,
|
|
1171
1192
|
kdims=["rt", "mz"],
|
|
@@ -1192,12 +1213,14 @@ def plot_2d_oracle(
|
|
|
1192
1213
|
feat_df = feats.copy()
|
|
1193
1214
|
feat_df = feat_df[(feat_df["ms2_scans"].notnull()) & (feat_df["id_level"] < 1)]
|
|
1194
1215
|
if len(feat_df) > 0:
|
|
1195
|
-
oracle_hover_4 = HoverTool(
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1216
|
+
oracle_hover_4 = HoverTool(
|
|
1217
|
+
tooltips=[
|
|
1218
|
+
("rt", "@rt"),
|
|
1219
|
+
("m/z", "@mz{0.0000}"),
|
|
1220
|
+
("feature_uid", "@feature_uid"),
|
|
1221
|
+
("inty", "@inty"),
|
|
1222
|
+
],
|
|
1223
|
+
)
|
|
1201
1224
|
feature_points_4 = hv.Points(
|
|
1202
1225
|
feat_df,
|
|
1203
1226
|
kdims=["rt", "mz"],
|
|
@@ -1216,12 +1239,14 @@ def plot_2d_oracle(
|
|
|
1216
1239
|
feat_df = feats.copy()
|
|
1217
1240
|
feat_df = feat_df[(feat_df["ms2_scans"].isnull()) & (feat_df["id_level"] < 1)]
|
|
1218
1241
|
if len(feat_df) > 0:
|
|
1219
|
-
oracle_hover_5 = HoverTool(
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1242
|
+
oracle_hover_5 = HoverTool(
|
|
1243
|
+
tooltips=[
|
|
1244
|
+
("rt", "@rt"),
|
|
1245
|
+
("m/z", "@mz{0.0000}"),
|
|
1246
|
+
("feature_uid", "@feature_uid"),
|
|
1247
|
+
("inty", "@inty"),
|
|
1248
|
+
],
|
|
1249
|
+
)
|
|
1225
1250
|
feature_points_5 = hv.Points(
|
|
1226
1251
|
feat_df,
|
|
1227
1252
|
kdims=["rt", "mz"],
|
masster/sample/processing.py
CHANGED
|
@@ -519,6 +519,10 @@ def find_features(self, **kwargs):
|
|
|
519
519
|
low-quality peaks), lower values make it more permissive. Typical tuning range: ~3 (relaxed) to >10
|
|
520
520
|
(stringent). Default: 10.0.
|
|
521
521
|
|
|
522
|
+
- isotope_filtering_model (str):
|
|
523
|
+
Isotope filtering model ('metabolites (2% RMS)', 'metabolites (5% RMS)', 'peptides', 'none').
|
|
524
|
+
Default: 'metabolites (5% RMS)'.
|
|
525
|
+
|
|
522
526
|
Tuning recommendation: first set ``chrom_fwhm`` to match your LC peak shape, then set ``noise`` to a baseline
|
|
523
527
|
intensity filter for your data, and finally adjust ``chrom_peak_snr`` to reach the desired balance between
|
|
524
528
|
sensitivity and specificity.
|
|
@@ -556,24 +560,25 @@ def find_features(self, **kwargs):
|
|
|
556
560
|
self.logger.warning(f"Unknown parameter {key} ignored")
|
|
557
561
|
|
|
558
562
|
# Set global parameters
|
|
559
|
-
if hasattr(params,
|
|
563
|
+
if hasattr(params, "threads") and params.threads is not None:
|
|
560
564
|
try:
|
|
561
565
|
# Try setting via OpenMP environment variable first (newer approach)
|
|
562
566
|
import os
|
|
563
|
-
|
|
567
|
+
|
|
568
|
+
os.environ["OMP_NUM_THREADS"] = str(params.threads)
|
|
564
569
|
self.logger.debug(f"Set thread count to {params.threads} via OMP_NUM_THREADS")
|
|
565
570
|
except Exception:
|
|
566
571
|
self.logger.warning(f"Could not set thread count to {params.threads} - using default")
|
|
567
|
-
|
|
572
|
+
|
|
568
573
|
# Set debug mode if enabled
|
|
569
|
-
if hasattr(params,
|
|
574
|
+
if hasattr(params, "debug") and params.debug:
|
|
570
575
|
self.logger.debug("Debug mode enabled")
|
|
571
|
-
elif hasattr(params,
|
|
576
|
+
elif hasattr(params, "no_progress") and params.no_progress:
|
|
572
577
|
self.logger.debug("No progress mode enabled")
|
|
573
|
-
|
|
578
|
+
|
|
574
579
|
self.logger.info("Starting feature detection...")
|
|
575
580
|
self.logger.debug(
|
|
576
|
-
f"Parameters: chrom_fwhm={params.get('chrom_fwhm')}, noise={params.get('noise')}, tol_ppm={params.get('tol_ppm')}",
|
|
581
|
+
f"Parameters: chrom_fwhm={params.get('chrom_fwhm')}, noise={params.get('noise')}, tol_ppm={params.get('tol_ppm')}, isotope_filtering_model={params.get('isotope_filtering_model')}",
|
|
577
582
|
)
|
|
578
583
|
|
|
579
584
|
exp = oms.MSExperiment()
|
|
@@ -602,7 +607,8 @@ def find_features(self, **kwargs):
|
|
|
602
607
|
# Apply MTD parameters
|
|
603
608
|
mtd_par.setValue("mass_error_ppm", float(params.get("tol_ppm")))
|
|
604
609
|
mtd_par.setValue("noise_threshold_int", float(params.get("noise")))
|
|
605
|
-
mtd_par.setValue(
|
|
610
|
+
mtd_par.setValue(
|
|
611
|
+
"min_trace_length",
|
|
606
612
|
float(params.get("min_trace_length_multiplier")) * float(params.get("chrom_fwhm_min")),
|
|
607
613
|
)
|
|
608
614
|
mtd_par.setValue(
|
|
@@ -610,7 +616,7 @@ def find_features(self, **kwargs):
|
|
|
610
616
|
int(params.get("trace_termination_outliers")),
|
|
611
617
|
)
|
|
612
618
|
mtd_par.setValue("chrom_peak_snr", float(params.get("chrom_peak_snr")))
|
|
613
|
-
|
|
619
|
+
|
|
614
620
|
# Additional MTD parameters
|
|
615
621
|
mtd_par.setValue("min_sample_rate", float(params.get("min_sample_rate")))
|
|
616
622
|
mtd_par.setValue("min_trace_length", float(params.get("min_trace_length")))
|
|
@@ -636,10 +642,10 @@ def find_features(self, **kwargs):
|
|
|
636
642
|
epd_par.setValue("masstrace_snr_filtering", "true")
|
|
637
643
|
if params.get("mz_scoring_13C"):
|
|
638
644
|
epd_par.setValue("mz_scoring_13C", "true")
|
|
639
|
-
|
|
645
|
+
|
|
640
646
|
# Additional EPD parameters
|
|
641
647
|
epd_par.setValue("enabled", "true" if params.get("enabled") else "false")
|
|
642
|
-
|
|
648
|
+
|
|
643
649
|
epd.setParameters(epd_par)
|
|
644
650
|
epd.detectPeaks(mass_traces, mass_traces_deconvol)
|
|
645
651
|
|
|
@@ -675,7 +681,7 @@ def find_features(self, **kwargs):
|
|
|
675
681
|
ffm_par.setValue("local_mz_range", float(params.get("local_mz_range")))
|
|
676
682
|
ffm_par.setValue("charge_lower_bound", int(params.get("charge_lower_bound")))
|
|
677
683
|
ffm_par.setValue("charge_upper_bound", int(params.get("charge_upper_bound")))
|
|
678
|
-
|
|
684
|
+
ffm_par.setValue("isotope_filtering_model", params.get("isotope_filtering_model"))
|
|
679
685
|
|
|
680
686
|
ffm.setParameters(ffm_par)
|
|
681
687
|
|