masster 0.3.1__tar.gz → 0.3.3__tar.gz
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-0.3.1 → masster-0.3.3}/PKG-INFO +1 -1
- {masster-0.3.1 → masster-0.3.3}/pyproject.toml +1 -1
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/plot.py +207 -15
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/sample.py +1 -1
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/plot.py +83 -15
- {masster-0.3.1 → masster-0.3.3}/uv.lock +1 -1
- {masster-0.3.1 → masster-0.3.3}/.github/workflows/publish.yml +0 -0
- {masster-0.3.1 → masster-0.3.3}/.github/workflows/security.yml +0 -0
- {masster-0.3.1 → masster-0.3.3}/.github/workflows/test.yml +0 -0
- {masster-0.3.1 → masster-0.3.3}/.gitignore +0 -0
- {masster-0.3.1 → masster-0.3.3}/.pre-commit-config.yaml +0 -0
- {masster-0.3.1 → masster-0.3.3}/LICENSE +0 -0
- {masster-0.3.1 → masster-0.3.3}/Makefile +0 -0
- {masster-0.3.1 → masster-0.3.3}/README.md +0 -0
- {masster-0.3.1 → masster-0.3.3}/TESTING.md +0 -0
- {masster-0.3.1 → masster-0.3.3}/demo/example_batch_process.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/demo/example_sample_process.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/__init__.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/_version.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/chromatogram.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.featureXML +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.mzML +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.sample5 +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.timeseries.data +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff.scan +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff2 +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/logger.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/__init__.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/defaults/__init__.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/defaults/find_adducts_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/defaults/find_features_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/defaults/find_ms2_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/defaults/get_spectrum_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/defaults/sample_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/h5.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/helpers.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/lib.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/load.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/parameters.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/processing.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/quant.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/sample5_schema.json +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/save.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/sample/sciex.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/spectrum.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/__init__.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/__init__.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/align_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/export_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/fill_chrom_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/fill_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/find_consensus_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/find_ms2_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/integrate_chrom_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/integrate_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/merge_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/defaults/study_def.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/export.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/h5.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/helpers.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/helpers_optimized.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/load.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/parameters.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/processing.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/save.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/study.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/src/masster/study/study5_schema.json +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/conftest.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_chromatogram.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_defaults.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_imports.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_integration.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_logger.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_parameters.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_sample.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_spectrum.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_study.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tests/test_version.py +0 -0
- {masster-0.3.1 → masster-0.3.3}/tox.ini +0 -0
|
@@ -283,9 +283,12 @@ def plot_2d(
|
|
|
283
283
|
cmap=None,
|
|
284
284
|
marker="circle",
|
|
285
285
|
markersize=10,
|
|
286
|
+
size="dynamic",
|
|
286
287
|
raster_dynamic=True,
|
|
287
288
|
raster_max_px=8,
|
|
288
289
|
raster_threshold=0.8,
|
|
290
|
+
height=600,
|
|
291
|
+
width=800,
|
|
289
292
|
mz_range=None,
|
|
290
293
|
rt_range=None,
|
|
291
294
|
):
|
|
@@ -318,6 +321,11 @@ def plot_2d(
|
|
|
318
321
|
Marker type to use for feature and MS2 points.
|
|
319
322
|
markersize (int, default 10):
|
|
320
323
|
Base size of the markers used for plotting points.
|
|
324
|
+
size (str, default 'dynamic'):
|
|
325
|
+
Controls marker sizing behavior. Options: 'dynamic', 'static', or 'slider'.
|
|
326
|
+
- 'dynamic': Uses coordinate-based sizing that scales with zoom level (markers get larger when zooming in)
|
|
327
|
+
- 'static': Uses screen-based sizing that remains constant regardless of zoom level
|
|
328
|
+
- 'slider': Provides an interactive slider to dynamically adjust marker size
|
|
321
329
|
raster_dynamic (bool, default True):
|
|
322
330
|
Whether to use dynamic rasterization for the background point cloud.
|
|
323
331
|
raster_max_px (int, default 8):
|
|
@@ -357,9 +365,9 @@ def plot_2d(
|
|
|
357
365
|
# keep only rt, mz, and inty
|
|
358
366
|
spectradf = spectradf.select(["rt", "mz", "inty"])
|
|
359
367
|
if mz_range is not None:
|
|
360
|
-
spectradf = spectradf
|
|
368
|
+
spectradf = spectradf.filter((pl.col("mz") >= mz_range[0]) & (pl.col("mz") <= mz_range[1]))
|
|
361
369
|
if rt_range is not None:
|
|
362
|
-
spectradf = spectradf
|
|
370
|
+
spectradf = spectradf.filter((pl.col("rt") >= rt_range[0]) & (pl.col("rt") <= rt_range[1]))
|
|
363
371
|
maxrt = spectradf["rt"].max()
|
|
364
372
|
minrt = spectradf["rt"].min()
|
|
365
373
|
maxmz = spectradf["mz"].max()
|
|
@@ -384,19 +392,81 @@ def plot_2d(
|
|
|
384
392
|
tools=["hover"],
|
|
385
393
|
)
|
|
386
394
|
|
|
387
|
-
|
|
395
|
+
# Configure marker and size behavior based on size parameter
|
|
396
|
+
use_dynamic_sizing = size.lower() in ["dyn", "dynamic"]
|
|
397
|
+
use_slider_sizing = size.lower() == "slider"
|
|
398
|
+
|
|
399
|
+
def dynamic_sizing_hook(plot, element):
|
|
400
|
+
"""Hook to convert size-based markers to radius-based for dynamic behavior"""
|
|
401
|
+
try:
|
|
402
|
+
if use_dynamic_sizing and hasattr(plot, 'state') and hasattr(plot.state, 'renderers'):
|
|
403
|
+
from bokeh.models import Circle
|
|
404
|
+
for renderer in plot.state.renderers:
|
|
405
|
+
if hasattr(renderer, 'glyph'):
|
|
406
|
+
glyph = renderer.glyph
|
|
407
|
+
# Check if it's a circle/scatter glyph that we can convert
|
|
408
|
+
if hasattr(glyph, 'size') and marker_type == "circle":
|
|
409
|
+
# Create a new Circle glyph with radius instead of size
|
|
410
|
+
new_glyph = Circle(
|
|
411
|
+
x=glyph.x,
|
|
412
|
+
y=glyph.y,
|
|
413
|
+
radius=base_radius,
|
|
414
|
+
fill_color=glyph.fill_color,
|
|
415
|
+
line_color=glyph.line_color,
|
|
416
|
+
fill_alpha=glyph.fill_alpha,
|
|
417
|
+
line_alpha=glyph.line_alpha,
|
|
418
|
+
)
|
|
419
|
+
renderer.glyph = new_glyph
|
|
420
|
+
except Exception:
|
|
421
|
+
# Silently fail and use regular sizing if hook doesn't work
|
|
422
|
+
pass
|
|
423
|
+
|
|
424
|
+
if use_dynamic_sizing:
|
|
425
|
+
# Dynamic sizing: use coordinate-based sizing that scales with zoom
|
|
426
|
+
marker_type = "circle"
|
|
427
|
+
# Calculate radius based on data range for coordinate-based sizing
|
|
428
|
+
rtrange = maxrt - minrt
|
|
429
|
+
mzrange = maxmz - minmz
|
|
430
|
+
# Use a fraction of the smaller dimension for radius
|
|
431
|
+
base_radius = min(rtrange, mzrange) * 0.0005 * markersize
|
|
432
|
+
size_1 = markersize # Use regular size initially, hook will convert to radius
|
|
433
|
+
size_2 = markersize
|
|
434
|
+
hooks = [dynamic_sizing_hook]
|
|
435
|
+
elif use_slider_sizing:
|
|
436
|
+
# Slider sizing: create an interactive slider for marker size
|
|
437
|
+
marker_type = marker # Use the original marker parameter
|
|
438
|
+
size_1 = markersize # Use markersize initially, will be updated by slider
|
|
439
|
+
size_2 = markersize
|
|
440
|
+
base_radius = None # Not used in slider mode
|
|
441
|
+
hooks = []
|
|
442
|
+
else:
|
|
443
|
+
# Static sizing: use pixel-based sizing that stays fixed
|
|
444
|
+
marker_type = marker # Use the original marker parameter
|
|
445
|
+
size_1 = markersize
|
|
446
|
+
size_2 = markersize
|
|
447
|
+
base_radius = None # Not used in static mode
|
|
448
|
+
hooks = []
|
|
449
|
+
|
|
388
450
|
color_1 = "forestgreen"
|
|
389
|
-
size_2 = 1 * markersize
|
|
390
451
|
color_2 = "darkorange"
|
|
391
452
|
if filename is not None:
|
|
392
453
|
dyn = False
|
|
393
454
|
if not filename.endswith(".html"):
|
|
394
|
-
|
|
455
|
+
if use_dynamic_sizing:
|
|
456
|
+
# For exported files, use smaller coordinate-based size
|
|
457
|
+
size_1 = 2
|
|
458
|
+
size_2 = 2
|
|
459
|
+
else:
|
|
460
|
+
size_1 = 2
|
|
461
|
+
size_2 = 2
|
|
395
462
|
color_1 = "forestgreen"
|
|
396
|
-
size_2 = 2
|
|
397
463
|
color_2 = "darkorange"
|
|
398
464
|
raster_dynamic = False
|
|
399
465
|
|
|
466
|
+
# For slider functionality, disable raster dynamic to avoid DynamicMap nesting
|
|
467
|
+
if use_slider_sizing:
|
|
468
|
+
raster_dynamic = False
|
|
469
|
+
|
|
400
470
|
dyn = raster_dynamic
|
|
401
471
|
raster = hd.rasterize(
|
|
402
472
|
points,
|
|
@@ -408,8 +478,8 @@ def plot_2d(
|
|
|
408
478
|
cmap=process_cmap(cmap, provider="bokeh"), # blues
|
|
409
479
|
tools=["hover"],
|
|
410
480
|
hooks=[new_bounds_hook],
|
|
411
|
-
width=
|
|
412
|
-
height=
|
|
481
|
+
width=width,
|
|
482
|
+
height=height,
|
|
413
483
|
cnorm="log",
|
|
414
484
|
xlabel="Retention time (s)",
|
|
415
485
|
ylabel="m/z",
|
|
@@ -448,6 +518,7 @@ def plot_2d(
|
|
|
448
518
|
feats = feats[feats["iso"] == 0]
|
|
449
519
|
# find features with ms2_scans not None and iso==0
|
|
450
520
|
features_df = feats[feats["ms2_scans"].notnull()]
|
|
521
|
+
# Create feature points with proper sizing method
|
|
451
522
|
feature_points_1 = hv.Points(
|
|
452
523
|
features_df,
|
|
453
524
|
kdims=["rt", "mz"],
|
|
@@ -463,9 +534,10 @@ def plot_2d(
|
|
|
463
534
|
label="Features with MS2 data",
|
|
464
535
|
).options(
|
|
465
536
|
color=color_1,
|
|
466
|
-
marker=
|
|
537
|
+
marker=marker_type,
|
|
467
538
|
size=size_1,
|
|
468
539
|
tools=["hover"],
|
|
540
|
+
hooks=hooks,
|
|
469
541
|
)
|
|
470
542
|
# find features without MS2 data
|
|
471
543
|
features_df = feats[feats["ms2_scans"].isnull()]
|
|
@@ -483,9 +555,10 @@ def plot_2d(
|
|
|
483
555
|
label="Features without MS2 data",
|
|
484
556
|
).options(
|
|
485
557
|
color="red",
|
|
558
|
+
marker=marker_type,
|
|
486
559
|
size=size_2,
|
|
487
|
-
marker=marker,
|
|
488
560
|
tools=["hover"],
|
|
561
|
+
hooks=hooks,
|
|
489
562
|
)
|
|
490
563
|
|
|
491
564
|
if show_isotopes:
|
|
@@ -510,9 +583,10 @@ def plot_2d(
|
|
|
510
583
|
label="Isotopes",
|
|
511
584
|
).options(
|
|
512
585
|
color="violet",
|
|
513
|
-
marker=
|
|
586
|
+
marker=marker_type,
|
|
514
587
|
size=size_1,
|
|
515
588
|
tools=["hover"],
|
|
589
|
+
hooks=hooks,
|
|
516
590
|
)
|
|
517
591
|
if show_ms2:
|
|
518
592
|
# find all self.scans_df with mslevel 2 that are not linked to a feature
|
|
@@ -569,8 +643,119 @@ def plot_2d(
|
|
|
569
643
|
if title is not None:
|
|
570
644
|
overlay = overlay.opts(title=title)
|
|
571
645
|
|
|
572
|
-
#
|
|
573
|
-
|
|
646
|
+
# Handle slider functionality
|
|
647
|
+
if use_slider_sizing:
|
|
648
|
+
# For slider functionality, we need to work with the feature points directly
|
|
649
|
+
# and not nest DynamicMaps. We'll create the slider using param and panel.
|
|
650
|
+
import param
|
|
651
|
+
import panel as pn
|
|
652
|
+
|
|
653
|
+
class MarkerSizeController(param.Parameterized):
|
|
654
|
+
size_slider = param.Number(default=markersize, bounds=(1, 20), step=0.5)
|
|
655
|
+
|
|
656
|
+
controller = MarkerSizeController()
|
|
657
|
+
|
|
658
|
+
# Create a function that generates just the feature overlays with different sizes
|
|
659
|
+
def create_feature_overlay(size_val):
|
|
660
|
+
feature_overlay = None
|
|
661
|
+
|
|
662
|
+
if feature_points_4 is not None:
|
|
663
|
+
updated_points_4 = feature_points_4.opts(size=size_val)
|
|
664
|
+
feature_overlay = updated_points_4 if feature_overlay is None else feature_overlay * updated_points_4
|
|
665
|
+
if feature_points_3 is not None:
|
|
666
|
+
updated_points_3 = feature_points_3.opts(size=size_val)
|
|
667
|
+
feature_overlay = updated_points_3 if feature_overlay is None else feature_overlay * updated_points_3
|
|
668
|
+
if feature_points_1 is not None:
|
|
669
|
+
updated_points_1 = feature_points_1.opts(size=size_val)
|
|
670
|
+
feature_overlay = updated_points_1 if feature_overlay is None else feature_overlay * updated_points_1
|
|
671
|
+
if not show_only_features_with_ms2 and feature_points_2 is not None:
|
|
672
|
+
updated_points_2 = feature_points_2.opts(size=size_val)
|
|
673
|
+
feature_overlay = updated_points_2 if feature_overlay is None else feature_overlay * updated_points_2
|
|
674
|
+
if feature_points_iso is not None:
|
|
675
|
+
updated_points_iso = feature_points_iso.opts(size=size_val)
|
|
676
|
+
feature_overlay = updated_points_iso if feature_overlay is None else feature_overlay * updated_points_iso
|
|
677
|
+
|
|
678
|
+
# Combine with the static raster background
|
|
679
|
+
if feature_overlay is not None:
|
|
680
|
+
combined_overlay = raster * feature_overlay
|
|
681
|
+
else:
|
|
682
|
+
combined_overlay = raster
|
|
683
|
+
|
|
684
|
+
if title is not None:
|
|
685
|
+
combined_overlay = combined_overlay.opts(title=title)
|
|
686
|
+
|
|
687
|
+
return combined_overlay
|
|
688
|
+
|
|
689
|
+
# Create a horizontal control widget on top of the plot
|
|
690
|
+
# Create the slider widget with explicit visibility
|
|
691
|
+
size_slider = pn.widgets.FloatSlider(
|
|
692
|
+
name="Marker Size",
|
|
693
|
+
start=1.0,
|
|
694
|
+
end=20.0,
|
|
695
|
+
step=0.5,
|
|
696
|
+
value=markersize,
|
|
697
|
+
width=300,
|
|
698
|
+
height=40,
|
|
699
|
+
margin=(5, 5),
|
|
700
|
+
show_value=True
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
# Create the slider widget row with clear styling
|
|
704
|
+
slider_widget = pn.Row(
|
|
705
|
+
pn.pane.HTML("<b>Marker Size Control:</b>", width=150, height=40, margin=(5, 10)),
|
|
706
|
+
size_slider,
|
|
707
|
+
height=60,
|
|
708
|
+
margin=10
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
# Create slider widget
|
|
712
|
+
size_slider = pn.widgets.FloatSlider(
|
|
713
|
+
name="Marker Size",
|
|
714
|
+
start=1.0,
|
|
715
|
+
end=20.0,
|
|
716
|
+
step=0.5,
|
|
717
|
+
value=markersize,
|
|
718
|
+
width=300,
|
|
719
|
+
height=40,
|
|
720
|
+
margin=(5, 5),
|
|
721
|
+
show_value=True
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
slider_widget = pn.Row(
|
|
725
|
+
pn.pane.HTML("<b>Marker Size:</b>", width=100, height=40, margin=(5, 10)),
|
|
726
|
+
size_slider,
|
|
727
|
+
height=60,
|
|
728
|
+
margin=10
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
# Simple reactive plot - slider mode doesn't use dynamic rasterization
|
|
732
|
+
@pn.depends(size_slider.param.value)
|
|
733
|
+
def reactive_plot(size_val):
|
|
734
|
+
overlay = create_feature_overlay(float(size_val))
|
|
735
|
+
# Apply static rasterization for slider mode
|
|
736
|
+
if raster_dynamic:
|
|
737
|
+
return hd.rasterize(
|
|
738
|
+
overlay,
|
|
739
|
+
aggregator=ds.count(),
|
|
740
|
+
width=raster_max_px,
|
|
741
|
+
height=raster_max_px,
|
|
742
|
+
dynamic=False # Static raster for slider mode
|
|
743
|
+
).opts(
|
|
744
|
+
cnorm='eq_hist',
|
|
745
|
+
tools=['hover'],
|
|
746
|
+
width=width,
|
|
747
|
+
height=height
|
|
748
|
+
)
|
|
749
|
+
else:
|
|
750
|
+
return overlay
|
|
751
|
+
|
|
752
|
+
# Create layout
|
|
753
|
+
layout = pn.Column(slider_widget, reactive_plot, sizing_mode='stretch_width')
|
|
754
|
+
|
|
755
|
+
return layout
|
|
756
|
+
else:
|
|
757
|
+
# Create a panel layout without slider
|
|
758
|
+
layout = panel.Column(overlay)
|
|
574
759
|
|
|
575
760
|
if filename is not None:
|
|
576
761
|
# if filename includes .html, save the panel layout to an HTML file
|
|
@@ -578,10 +763,17 @@ def plot_2d(
|
|
|
578
763
|
layout.save(filename, embed=True)
|
|
579
764
|
else:
|
|
580
765
|
# save the panel layout as a png
|
|
581
|
-
|
|
766
|
+
if use_slider_sizing:
|
|
767
|
+
# For slider plots, save the current state of the param_plot
|
|
768
|
+
hv.save(create_feature_overlay(markersize), filename, fmt="png")
|
|
769
|
+
else:
|
|
770
|
+
hv.save(overlay, filename, fmt="png")
|
|
582
771
|
else:
|
|
583
772
|
# Check if we're in a notebook environment and display appropriately
|
|
584
|
-
|
|
773
|
+
if use_slider_sizing:
|
|
774
|
+
return _display_plot(layout, layout)
|
|
775
|
+
else:
|
|
776
|
+
return _display_plot(overlay, layout)
|
|
585
777
|
|
|
586
778
|
|
|
587
779
|
def plot_2d_oracle(
|
|
@@ -287,7 +287,7 @@ class Sample:
|
|
|
287
287
|
"""
|
|
288
288
|
# Reset logger configuration flags to allow proper reconfiguration after reload
|
|
289
289
|
try:
|
|
290
|
-
import masster.
|
|
290
|
+
import masster.logger as logger_module
|
|
291
291
|
|
|
292
292
|
if hasattr(logger_module, "_SAMPLE_LOGGER_CONFIGURED"):
|
|
293
293
|
logger_module._SAMPLE_LOGGER_CONFIGURED = False
|
|
@@ -157,13 +157,43 @@ def plot_consensus_2d(
|
|
|
157
157
|
colorby="number_samples",
|
|
158
158
|
sizeby="inty_mean",
|
|
159
159
|
markersize=6,
|
|
160
|
+
size="dynamic",
|
|
160
161
|
alpha=0.7,
|
|
161
162
|
cmap=None,
|
|
163
|
+
width=900,
|
|
164
|
+
height=900,
|
|
165
|
+
mz_range=None,
|
|
166
|
+
rt_range=None
|
|
162
167
|
):
|
|
168
|
+
"""
|
|
169
|
+
Plot consensus features in a 2D scatter plot with retention time vs m/z.
|
|
170
|
+
|
|
171
|
+
Parameters:
|
|
172
|
+
filename (str, optional): Path to save the plot
|
|
173
|
+
colorby (str): Column name to use for color mapping (default: "number_samples")
|
|
174
|
+
sizeby (str): Column name to use for size mapping (default: "inty_mean")
|
|
175
|
+
markersize (int): Base marker size (default: 6)
|
|
176
|
+
size (str): Controls whether points scale with zoom. Options:
|
|
177
|
+
'dynamic' - points use circle() and scale with zoom
|
|
178
|
+
'static' - points use scatter() and maintain fixed pixel size
|
|
179
|
+
alpha (float): Transparency level (default: 0.7)
|
|
180
|
+
cmap (str, optional): Color map name
|
|
181
|
+
width (int): Plot width in pixels (default: 900)
|
|
182
|
+
height (int): Plot height in pixels (default: 900)
|
|
183
|
+
mz_range (tuple, optional): m/z range for filtering consensus features (min_mz, max_mz)
|
|
184
|
+
rt_range (tuple, optional): Retention time range for filtering consensus features (min_rt, max_rt)
|
|
185
|
+
"""
|
|
163
186
|
if self.consensus_df is None:
|
|
164
187
|
self.logger.error("No consensus map found.")
|
|
165
188
|
return
|
|
166
189
|
data = self.consensus_df.clone()
|
|
190
|
+
|
|
191
|
+
# Filter by mz_range and rt_range if provided
|
|
192
|
+
if mz_range is not None:
|
|
193
|
+
data = data.filter((pl.col("mz") >= mz_range[0]) & (pl.col("mz") <= mz_range[1]))
|
|
194
|
+
if rt_range is not None:
|
|
195
|
+
data = data.filter((pl.col("rt") >= rt_range[0]) & (pl.col("rt") <= rt_range[1]))
|
|
196
|
+
|
|
167
197
|
if colorby not in data.columns:
|
|
168
198
|
self.logger.error(f"Column {colorby} not found in consensus_df.")
|
|
169
199
|
return
|
|
@@ -238,21 +268,33 @@ def plot_consensus_2d(
|
|
|
238
268
|
)
|
|
239
269
|
# scatter plot rt vs mz
|
|
240
270
|
p = bp.figure(
|
|
241
|
-
width=
|
|
242
|
-
height=
|
|
271
|
+
width=width,
|
|
272
|
+
height=height,
|
|
243
273
|
title="Consensus map",
|
|
244
274
|
)
|
|
245
275
|
p.xaxis.axis_label = "Retention Time (min)"
|
|
246
276
|
p.yaxis.axis_label = "m/z"
|
|
247
|
-
scatter_renderer =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
277
|
+
scatter_renderer: Any = None
|
|
278
|
+
if size.lower() in ["dyn", "dynamic"]:
|
|
279
|
+
scatter_renderer = p.circle(
|
|
280
|
+
x="rt",
|
|
281
|
+
y="mz",
|
|
282
|
+
radius=markersize / 10,
|
|
283
|
+
fill_color={"field": colorby, "transform": color_mapper},
|
|
284
|
+
line_color=None,
|
|
285
|
+
alpha=alpha,
|
|
286
|
+
source=source,
|
|
287
|
+
)
|
|
288
|
+
else:
|
|
289
|
+
scatter_renderer = p.scatter(
|
|
290
|
+
x="rt",
|
|
291
|
+
y="mz",
|
|
292
|
+
size="markersize",
|
|
293
|
+
fill_color={"field": colorby, "transform": color_mapper},
|
|
294
|
+
line_color=None,
|
|
295
|
+
alpha=alpha,
|
|
296
|
+
source=source,
|
|
297
|
+
)
|
|
256
298
|
# add hover tool
|
|
257
299
|
hover = HoverTool(
|
|
258
300
|
tooltips=[
|
|
@@ -292,16 +334,36 @@ def plot_samples_2d(
|
|
|
292
334
|
samples=None,
|
|
293
335
|
filename=None,
|
|
294
336
|
markersize=2,
|
|
295
|
-
size="
|
|
337
|
+
size="dynamic",
|
|
296
338
|
alpha_max=0.8,
|
|
297
339
|
alpha="inty",
|
|
298
340
|
cmap="Turbo256",
|
|
299
|
-
max_features=50000,
|
|
341
|
+
max_features=50000,
|
|
342
|
+
width=900,
|
|
343
|
+
height=900,
|
|
344
|
+
mz_range=None,
|
|
345
|
+
rt_range=None
|
|
300
346
|
):
|
|
301
347
|
"""
|
|
302
348
|
Plot all feature maps for sample_uid in parameter uids in an overlaid scatter plot.
|
|
303
349
|
Each sample is a different color. Alpha scales with intensity.
|
|
304
350
|
OPTIMIZED VERSION: Uses vectorized operations and batch processing.
|
|
351
|
+
|
|
352
|
+
Parameters:
|
|
353
|
+
samples: Sample UIDs to plot
|
|
354
|
+
filename (str, optional): Path to save the plot
|
|
355
|
+
markersize (int): Base marker size (default: 2)
|
|
356
|
+
size (str): Controls whether points scale with zoom. Options:
|
|
357
|
+
'dynamic' or 'dyn' - points use circle() and scale with zoom
|
|
358
|
+
'const', 'static' or other - points use scatter() and maintain fixed pixel size
|
|
359
|
+
alpha_max (float): Maximum transparency level (default: 0.8)
|
|
360
|
+
alpha (str): Column name to use for alpha mapping (default: "inty")
|
|
361
|
+
cmap (str): Color map name (default: "Turbo256")
|
|
362
|
+
max_features (int): Maximum number of features to plot (default: 50000)
|
|
363
|
+
width (int): Plot width in pixels (default: 900)
|
|
364
|
+
height (int): Plot height in pixels (default: 900)
|
|
365
|
+
mz_range (tuple, optional): m/z range for filtering features (min_mz, max_mz)
|
|
366
|
+
rt_range (tuple, optional): Retention time range for filtering features (min_rt, max_rt)
|
|
305
367
|
"""
|
|
306
368
|
|
|
307
369
|
sample_uids = self._get_sample_uids(samples)
|
|
@@ -314,8 +376,8 @@ def plot_samples_2d(
|
|
|
314
376
|
color_map = {uid: colors[i * (256 // max(1, len(sample_uids)))] for i, uid in enumerate(sample_uids)}
|
|
315
377
|
|
|
316
378
|
p = figure(
|
|
317
|
-
width=
|
|
318
|
-
height=
|
|
379
|
+
width=width,
|
|
380
|
+
height=height,
|
|
319
381
|
title="Sample Features",
|
|
320
382
|
)
|
|
321
383
|
p.xaxis.axis_label = "Retention Time (RT)"
|
|
@@ -323,6 +385,12 @@ def plot_samples_2d(
|
|
|
323
385
|
|
|
324
386
|
# OPTIMIZATION 1: Batch filter all features for selected samples at once
|
|
325
387
|
features_batch = self.features_df.filter(pl.col("sample_uid").is_in(sample_uids))
|
|
388
|
+
|
|
389
|
+
# Filter by mz_range and rt_range if provided
|
|
390
|
+
if mz_range is not None:
|
|
391
|
+
features_batch = features_batch.filter((pl.col("mz") >= mz_range[0]) & (pl.col("mz") <= mz_range[1]))
|
|
392
|
+
if rt_range is not None:
|
|
393
|
+
features_batch = features_batch.filter((pl.col("rt") >= rt_range[0]) & (pl.col("rt") <= rt_range[1]))
|
|
326
394
|
|
|
327
395
|
if features_batch.is_empty():
|
|
328
396
|
self.logger.error("No features found for the selected samples.")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|