masster 0.3.9__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 +90 -94
- masster/sample/defaults/sample_def.py +15 -0
- masster/sample/h5.py +2 -2
- masster/sample/helpers.py +137 -136
- masster/sample/lib.py +11 -11
- masster/sample/load.py +13 -9
- masster/sample/plot.py +167 -60
- masster/sample/processing.py +150 -153
- 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 +197 -123
- masster/study/save.py +7 -7
- masster/study/study.py +97 -88
- masster/study/study5_schema.json +82 -82
- {masster-0.3.9.dist-info → masster-0.3.11.dist-info}/METADATA +1 -1
- {masster-0.3.9.dist-info → masster-0.3.11.dist-info}/RECORD +34 -32
- {masster-0.3.9.dist-info → masster-0.3.11.dist-info}/WHEEL +0 -0
- {masster-0.3.9.dist-info → masster-0.3.11.dist-info}/entry_points.txt +0 -0
- {masster-0.3.9.dist-info → masster-0.3.11.dist-info}/licenses/LICENSE +0 -0
masster/sample/lib.py
CHANGED
|
@@ -421,14 +421,14 @@ def save_lib_mgf(
|
|
|
421
421
|
# trim spectrum 2 Da lower and 10 Da higher than precursor m/z
|
|
422
422
|
spec = spec.mz_trim(mz_min=row["mz"] - 2.0, mz_max=row["mz"] + 10.0)
|
|
423
423
|
|
|
424
|
-
|
|
424
|
+
file_basename: str = os.path.basename(self.file_path)
|
|
425
425
|
mslevel = 1 if spec.ms_level is None else spec.ms_level
|
|
426
426
|
activation = None
|
|
427
427
|
energy = None
|
|
428
428
|
kineticenergy = None
|
|
429
429
|
if mslevel > 1:
|
|
430
|
-
if "CID" in
|
|
431
|
-
if "EAD" in
|
|
430
|
+
if "CID" in file_basename.upper() or "ZTS" in file_basename.upper():
|
|
431
|
+
if "EAD" in file_basename.upper():
|
|
432
432
|
activation = "CID-EAD"
|
|
433
433
|
# search ([0-9]*KE) in filename.upper() using regex
|
|
434
434
|
match = re.search(r"(\d+)KE", str(filename.upper()))
|
|
@@ -440,14 +440,14 @@ def save_lib_mgf(
|
|
|
440
440
|
kineticenergy = int(match.group(1))
|
|
441
441
|
else:
|
|
442
442
|
activation = "CID"
|
|
443
|
-
elif "EAD" in
|
|
443
|
+
elif "EAD" in file_basename.upper():
|
|
444
444
|
activation = "EAD"
|
|
445
445
|
# search ([0-9]*KE) in filename.upper() using regex
|
|
446
|
-
match = re.search(r"(\d+)KE",
|
|
446
|
+
match = re.search(r"(\d+)KE", file_basename.upper())
|
|
447
447
|
if match:
|
|
448
448
|
kineticenergy = int(match.group(1))
|
|
449
449
|
else:
|
|
450
|
-
match = re.search(r"(\d+)EV",
|
|
450
|
+
match = re.search(r"(\d+)EV", file_basename.upper())
|
|
451
451
|
if match:
|
|
452
452
|
kineticenergy = int(match.group(1))
|
|
453
453
|
energy = spec.energy if hasattr(spec, "energy") else None
|
|
@@ -515,14 +515,14 @@ def save_lib_mgf(
|
|
|
515
515
|
kineticenergy = int(match.group(1))
|
|
516
516
|
else:
|
|
517
517
|
activation = "CID"
|
|
518
|
-
elif "EAD" in
|
|
518
|
+
elif "EAD" in file_basename.upper():
|
|
519
519
|
activation = "EAD"
|
|
520
|
-
# search ([0-9]*KE) in
|
|
521
|
-
match = re.search(r"(\d+)KE",
|
|
520
|
+
# search ([0-9]*KE) in file_basename.upper() using regex
|
|
521
|
+
match = re.search(r"(\d+)KE", file_basename.upper())
|
|
522
522
|
if match:
|
|
523
523
|
kineticenergy = int(match.group(1))
|
|
524
524
|
else:
|
|
525
|
-
match = re.search(r"(\d+)EV",
|
|
525
|
+
match = re.search(r"(\d+)EV", file_basename.upper())
|
|
526
526
|
if match:
|
|
527
527
|
kineticenergy = int(match.group(1))
|
|
528
528
|
energy = spec.energy if hasattr(spec, "energy") else None
|
|
@@ -541,7 +541,7 @@ def save_lib_mgf(
|
|
|
541
541
|
"ACTIVATION": activation,
|
|
542
542
|
"COLLISIONENERGY": energy,
|
|
543
543
|
"KINETICENERGY": kineticenergy,
|
|
544
|
-
"FILENAME":
|
|
544
|
+
"FILENAME": file_basename,
|
|
545
545
|
"SCANS": ms1_scan_uid,
|
|
546
546
|
"FID": row["feature_uid"],
|
|
547
547
|
"MSLEVEL": 1 if spec.ms_level is None else spec.ms_level,
|
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,6 +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
|
|
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
|
+
)
|
|
522
535
|
feature_points_1 = hv.Points(
|
|
523
536
|
features_df,
|
|
524
537
|
kdims=["rt", "mz"],
|
|
@@ -536,11 +549,21 @@ def plot_2d(
|
|
|
536
549
|
color=color_1,
|
|
537
550
|
marker=marker_type,
|
|
538
551
|
size=size_1,
|
|
539
|
-
tools=[
|
|
552
|
+
tools=[feature_hover_1],
|
|
540
553
|
hooks=hooks,
|
|
541
554
|
)
|
|
542
555
|
# find features without MS2 data
|
|
543
556
|
features_df = feats[feats["ms2_scans"].isnull()]
|
|
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
|
+
)
|
|
544
567
|
feature_points_2 = hv.Points(
|
|
545
568
|
features_df,
|
|
546
569
|
kdims=["rt", "mz"],
|
|
@@ -557,7 +580,7 @@ def plot_2d(
|
|
|
557
580
|
color="red",
|
|
558
581
|
marker=marker_type,
|
|
559
582
|
size=size_2,
|
|
560
|
-
tools=[
|
|
583
|
+
tools=[feature_hover_2],
|
|
561
584
|
hooks=hooks,
|
|
562
585
|
)
|
|
563
586
|
|
|
@@ -567,6 +590,18 @@ def plot_2d(
|
|
|
567
590
|
# Convert to pandas for plotting compatibility
|
|
568
591
|
if hasattr(features_df, "to_pandas"):
|
|
569
592
|
features_df = features_df.to_pandas()
|
|
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
|
+
)
|
|
570
605
|
feature_points_iso = hv.Points(
|
|
571
606
|
features_df,
|
|
572
607
|
kdims=["rt", "mz"],
|
|
@@ -585,7 +620,7 @@ def plot_2d(
|
|
|
585
620
|
color="violet",
|
|
586
621
|
marker=marker_type,
|
|
587
622
|
size=size_1,
|
|
588
|
-
tools=[
|
|
623
|
+
tools=[feature_hover_iso],
|
|
589
624
|
hooks=hooks,
|
|
590
625
|
)
|
|
591
626
|
if show_ms2:
|
|
@@ -597,6 +632,15 @@ def plot_2d(
|
|
|
597
632
|
if len(ms2_orphan) > 0:
|
|
598
633
|
# pandalize
|
|
599
634
|
ms2 = ms2_orphan.to_pandas()
|
|
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
|
+
)
|
|
600
644
|
feature_points_3 = hv.Points(
|
|
601
645
|
ms2,
|
|
602
646
|
kdims=["rt", "prec_mz"],
|
|
@@ -606,7 +650,7 @@ def plot_2d(
|
|
|
606
650
|
color=color_2,
|
|
607
651
|
marker="x",
|
|
608
652
|
size=size_2,
|
|
609
|
-
tools=[
|
|
653
|
+
tools=[ms2_hover_3],
|
|
610
654
|
)
|
|
611
655
|
|
|
612
656
|
ms2_linked = self.scans_df.filter(pl.col("ms_level") == 2).filter(
|
|
@@ -615,6 +659,15 @@ def plot_2d(
|
|
|
615
659
|
if len(ms2_linked) > 0:
|
|
616
660
|
# pandalize
|
|
617
661
|
ms2 = ms2_linked.to_pandas()
|
|
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
|
+
)
|
|
618
671
|
feature_points_4 = hv.Points(
|
|
619
672
|
ms2,
|
|
620
673
|
kdims=["rt", "prec_mz"],
|
|
@@ -624,7 +677,7 @@ def plot_2d(
|
|
|
624
677
|
color=color_1,
|
|
625
678
|
marker="x",
|
|
626
679
|
size=size_2,
|
|
627
|
-
tools=[
|
|
680
|
+
tools=[ms2_hover_4],
|
|
628
681
|
)
|
|
629
682
|
|
|
630
683
|
overlay = raster
|
|
@@ -648,17 +701,17 @@ def plot_2d(
|
|
|
648
701
|
# For slider functionality, we need to work with the feature points directly
|
|
649
702
|
# and not nest DynamicMaps. We'll create the slider using param and panel.
|
|
650
703
|
import param
|
|
651
|
-
import panel as
|
|
652
|
-
|
|
704
|
+
import panel as on
|
|
705
|
+
|
|
653
706
|
class MarkerSizeController(param.Parameterized):
|
|
654
707
|
size_slider = param.Number(default=markersize, bounds=(1, 20), step=0.5)
|
|
655
|
-
|
|
708
|
+
|
|
656
709
|
controller = MarkerSizeController()
|
|
657
|
-
|
|
710
|
+
|
|
658
711
|
# Create a function that generates just the feature overlays with different sizes
|
|
659
712
|
def create_feature_overlay(size_val):
|
|
660
713
|
feature_overlay = None
|
|
661
|
-
|
|
714
|
+
|
|
662
715
|
if feature_points_4 is not None:
|
|
663
716
|
updated_points_4 = feature_points_4.opts(size=size_val)
|
|
664
717
|
feature_overlay = updated_points_4 if feature_overlay is None else feature_overlay * updated_points_4
|
|
@@ -673,22 +726,24 @@ def plot_2d(
|
|
|
673
726
|
feature_overlay = updated_points_2 if feature_overlay is None else feature_overlay * updated_points_2
|
|
674
727
|
if feature_points_iso is not None:
|
|
675
728
|
updated_points_iso = feature_points_iso.opts(size=size_val)
|
|
676
|
-
feature_overlay =
|
|
677
|
-
|
|
729
|
+
feature_overlay = (
|
|
730
|
+
updated_points_iso if feature_overlay is None else feature_overlay * updated_points_iso
|
|
731
|
+
)
|
|
732
|
+
|
|
678
733
|
# Combine with the static raster background
|
|
679
734
|
if feature_overlay is not None:
|
|
680
735
|
combined_overlay = raster * feature_overlay
|
|
681
736
|
else:
|
|
682
737
|
combined_overlay = raster
|
|
683
|
-
|
|
738
|
+
|
|
684
739
|
if title is not None:
|
|
685
740
|
combined_overlay = combined_overlay.opts(title=title)
|
|
686
|
-
|
|
741
|
+
|
|
687
742
|
return combined_overlay
|
|
688
|
-
|
|
743
|
+
|
|
689
744
|
# Create a horizontal control widget on top of the plot
|
|
690
745
|
# Create the slider widget with explicit visibility
|
|
691
|
-
size_slider =
|
|
746
|
+
size_slider = on.widgets.FloatSlider(
|
|
692
747
|
name="Marker Size",
|
|
693
748
|
start=1.0,
|
|
694
749
|
end=20.0,
|
|
@@ -697,19 +752,19 @@ def plot_2d(
|
|
|
697
752
|
width=300,
|
|
698
753
|
height=40,
|
|
699
754
|
margin=(5, 5),
|
|
700
|
-
show_value=True
|
|
755
|
+
show_value=True,
|
|
701
756
|
)
|
|
702
|
-
|
|
757
|
+
|
|
703
758
|
# Create the slider widget row with clear styling
|
|
704
|
-
slider_widget =
|
|
705
|
-
|
|
759
|
+
slider_widget = on.Row(
|
|
760
|
+
on.pane.HTML("<b>Marker Size Control:</b>", width=150, height=40, margin=(5, 10)),
|
|
706
761
|
size_slider,
|
|
707
762
|
height=60,
|
|
708
|
-
margin=10
|
|
763
|
+
margin=10,
|
|
709
764
|
)
|
|
710
|
-
|
|
765
|
+
|
|
711
766
|
# Create slider widget
|
|
712
|
-
size_slider =
|
|
767
|
+
size_slider = on.widgets.FloatSlider(
|
|
713
768
|
name="Marker Size",
|
|
714
769
|
start=1.0,
|
|
715
770
|
end=20.0,
|
|
@@ -718,18 +773,18 @@ def plot_2d(
|
|
|
718
773
|
width=300,
|
|
719
774
|
height=40,
|
|
720
775
|
margin=(5, 5),
|
|
721
|
-
show_value=True
|
|
776
|
+
show_value=True,
|
|
722
777
|
)
|
|
723
|
-
|
|
724
|
-
slider_widget =
|
|
725
|
-
|
|
778
|
+
|
|
779
|
+
slider_widget = on.Row(
|
|
780
|
+
on.pane.HTML("<b>Marker Size:</b>", width=100, height=40, margin=(5, 10)),
|
|
726
781
|
size_slider,
|
|
727
782
|
height=60,
|
|
728
|
-
margin=10
|
|
783
|
+
margin=10,
|
|
729
784
|
)
|
|
730
|
-
|
|
785
|
+
|
|
731
786
|
# Simple reactive plot - slider mode doesn't use dynamic rasterization
|
|
732
|
-
@
|
|
787
|
+
@on.depends(size_slider.param.value)
|
|
733
788
|
def reactive_plot(size_val):
|
|
734
789
|
overlay = create_feature_overlay(float(size_val))
|
|
735
790
|
# Apply static rasterization for slider mode
|
|
@@ -739,19 +794,19 @@ def plot_2d(
|
|
|
739
794
|
aggregator=ds.count(),
|
|
740
795
|
width=raster_max_px,
|
|
741
796
|
height=raster_max_px,
|
|
742
|
-
dynamic=False # Static raster for slider mode
|
|
797
|
+
dynamic=False, # Static raster for slider mode
|
|
743
798
|
).opts(
|
|
744
|
-
cnorm=
|
|
745
|
-
tools=[
|
|
799
|
+
cnorm="eq_hist",
|
|
800
|
+
tools=["hover"],
|
|
746
801
|
width=width,
|
|
747
|
-
height=height
|
|
802
|
+
height=height,
|
|
748
803
|
)
|
|
749
804
|
else:
|
|
750
805
|
return overlay
|
|
751
|
-
|
|
806
|
+
|
|
752
807
|
# Create layout
|
|
753
|
-
layout =
|
|
754
|
-
|
|
808
|
+
layout = on.Column(slider_widget, reactive_plot, sizing_mode="stretch_width")
|
|
809
|
+
|
|
755
810
|
return layout
|
|
756
811
|
else:
|
|
757
812
|
# Create a panel layout without slider
|
|
@@ -1041,6 +1096,20 @@ def plot_2d_oracle(
|
|
|
1041
1096
|
feat_df = feats.copy()
|
|
1042
1097
|
feat_df = feat_df[feat_df["id_level"] == 2]
|
|
1043
1098
|
|
|
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
|
+
)
|
|
1044
1113
|
feature_points_1 = hv.Points(
|
|
1045
1114
|
feat_df,
|
|
1046
1115
|
kdims=["rt", "mz"],
|
|
@@ -1062,7 +1131,7 @@ def plot_2d_oracle(
|
|
|
1062
1131
|
marker="circle",
|
|
1063
1132
|
size=markersize,
|
|
1064
1133
|
fill_alpha=1.0,
|
|
1065
|
-
tools=[
|
|
1134
|
+
tools=[oracle_hover_1],
|
|
1066
1135
|
)
|
|
1067
1136
|
|
|
1068
1137
|
# feature_points_2 are all features that have ms2_scans not null and id_level ==1
|
|
@@ -1070,6 +1139,17 @@ def plot_2d_oracle(
|
|
|
1070
1139
|
feat_df = feats.copy()
|
|
1071
1140
|
feat_df = feat_df[(feat_df["ms2_scans"].notnull()) & (feat_df["id_level"] == 1)]
|
|
1072
1141
|
if len(feat_df) > 0:
|
|
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
|
+
)
|
|
1073
1153
|
feature_points_2 = hv.Points(
|
|
1074
1154
|
feat_df,
|
|
1075
1155
|
kdims=["rt", "mz"],
|
|
@@ -1088,7 +1168,7 @@ def plot_2d_oracle(
|
|
|
1088
1168
|
marker="circle",
|
|
1089
1169
|
size=markersize,
|
|
1090
1170
|
fill_alpha=0.0,
|
|
1091
|
-
tools=[
|
|
1171
|
+
tools=[oracle_hover_2],
|
|
1092
1172
|
)
|
|
1093
1173
|
|
|
1094
1174
|
# feature_points_3 are all features that have ms2_scans null and id_level ==1
|
|
@@ -1096,6 +1176,17 @@ def plot_2d_oracle(
|
|
|
1096
1176
|
feat_df = feats.copy()
|
|
1097
1177
|
feat_df = feat_df[(feat_df["ms2_scans"].isnull()) & (feat_df["id_level"] == 1)]
|
|
1098
1178
|
if len(feat_df) > 0:
|
|
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
|
+
)
|
|
1099
1190
|
feature_points_3 = hv.Points(
|
|
1100
1191
|
feat_df,
|
|
1101
1192
|
kdims=["rt", "mz"],
|
|
@@ -1114,7 +1205,7 @@ def plot_2d_oracle(
|
|
|
1114
1205
|
marker="diamond",
|
|
1115
1206
|
size=markersize,
|
|
1116
1207
|
fill_alpha=0.0,
|
|
1117
|
-
tools=[
|
|
1208
|
+
tools=[oracle_hover_3],
|
|
1118
1209
|
)
|
|
1119
1210
|
|
|
1120
1211
|
# feature_points_4 are all features that have ms2_scans null and id_level ==0
|
|
@@ -1122,6 +1213,14 @@ def plot_2d_oracle(
|
|
|
1122
1213
|
feat_df = feats.copy()
|
|
1123
1214
|
feat_df = feat_df[(feat_df["ms2_scans"].notnull()) & (feat_df["id_level"] < 1)]
|
|
1124
1215
|
if len(feat_df) > 0:
|
|
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
|
+
)
|
|
1125
1224
|
feature_points_4 = hv.Points(
|
|
1126
1225
|
feat_df,
|
|
1127
1226
|
kdims=["rt", "mz"],
|
|
@@ -1132,14 +1231,22 @@ def plot_2d_oracle(
|
|
|
1132
1231
|
marker="circle",
|
|
1133
1232
|
size=markersize,
|
|
1134
1233
|
fill_alpha=0.0,
|
|
1135
|
-
tools=[
|
|
1234
|
+
tools=[oracle_hover_4],
|
|
1136
1235
|
)
|
|
1137
1236
|
|
|
1138
|
-
#
|
|
1237
|
+
# feature_points_5 are all features that have ms2_scans null and id_level ==0
|
|
1139
1238
|
feature_points_5 = None
|
|
1140
1239
|
feat_df = feats.copy()
|
|
1141
1240
|
feat_df = feat_df[(feat_df["ms2_scans"].isnull()) & (feat_df["id_level"] < 1)]
|
|
1142
1241
|
if len(feat_df) > 0:
|
|
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
|
+
)
|
|
1143
1250
|
feature_points_5 = hv.Points(
|
|
1144
1251
|
feat_df,
|
|
1145
1252
|
kdims=["rt", "mz"],
|
|
@@ -1150,7 +1257,7 @@ def plot_2d_oracle(
|
|
|
1150
1257
|
marker="diamond",
|
|
1151
1258
|
fill_alpha=0.0,
|
|
1152
1259
|
size=markersize,
|
|
1153
|
-
tools=[
|
|
1260
|
+
tools=[oracle_hover_5],
|
|
1154
1261
|
)
|
|
1155
1262
|
|
|
1156
1263
|
overlay = raster
|