masster 0.3.9__tar.gz → 0.3.10__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.

Files changed (80) hide show
  1. {masster-0.3.9 → masster-0.3.10}/PKG-INFO +1 -1
  2. {masster-0.3.9 → masster-0.3.10}/pyproject.toml +1 -1
  3. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/defaults/find_features_def.py +82 -96
  4. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/defaults/sample_def.py +15 -0
  5. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/lib.py +11 -11
  6. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/plot.py +93 -11
  7. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/processing.py +137 -146
  8. {masster-0.3.9 → masster-0.3.10}/src/masster/study/processing.py +39 -48
  9. {masster-0.3.9 → masster-0.3.10}/uv.lock +1 -1
  10. {masster-0.3.9 → masster-0.3.10}/.github/workflows/publish.yml +0 -0
  11. {masster-0.3.9 → masster-0.3.10}/.github/workflows/security.yml +0 -0
  12. {masster-0.3.9 → masster-0.3.10}/.github/workflows/test.yml +0 -0
  13. {masster-0.3.9 → masster-0.3.10}/.gitignore +0 -0
  14. {masster-0.3.9 → masster-0.3.10}/.pre-commit-config.yaml +0 -0
  15. {masster-0.3.9 → masster-0.3.10}/LICENSE +0 -0
  16. {masster-0.3.9 → masster-0.3.10}/Makefile +0 -0
  17. {masster-0.3.9 → masster-0.3.10}/README.md +0 -0
  18. {masster-0.3.9 → masster-0.3.10}/TESTING.md +0 -0
  19. {masster-0.3.9 → masster-0.3.10}/demo/example_batch_process.py +0 -0
  20. {masster-0.3.9 → masster-0.3.10}/demo/example_sample_process.py +0 -0
  21. {masster-0.3.9 → masster-0.3.10}/src/masster/__init__.py +0 -0
  22. {masster-0.3.9 → masster-0.3.10}/src/masster/_version.py +0 -0
  23. {masster-0.3.9 → masster-0.3.10}/src/masster/chromatogram.py +0 -0
  24. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.featureXML +0 -0
  25. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.mzML +0 -0
  26. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.sample5 +0 -0
  27. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.timeseries.data +0 -0
  28. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff +0 -0
  29. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff.scan +0 -0
  30. {masster-0.3.9 → masster-0.3.10}/src/masster/data/examples/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff2 +0 -0
  31. {masster-0.3.9 → masster-0.3.10}/src/masster/logger.py +0 -0
  32. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/__init__.py +0 -0
  33. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/defaults/__init__.py +0 -0
  34. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/defaults/find_adducts_def.py +0 -0
  35. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/defaults/find_ms2_def.py +0 -0
  36. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/defaults/get_spectrum_def.py +0 -0
  37. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/h5.py +0 -0
  38. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/helpers.py +0 -0
  39. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/load.py +0 -0
  40. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/parameters.py +0 -0
  41. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/quant.py +0 -0
  42. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/sample.py +0 -0
  43. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/sample5_schema.json +0 -0
  44. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/save.py +0 -0
  45. {masster-0.3.9 → masster-0.3.10}/src/masster/sample/sciex.py +0 -0
  46. {masster-0.3.9 → masster-0.3.10}/src/masster/spectrum.py +0 -0
  47. {masster-0.3.9 → masster-0.3.10}/src/masster/study/__init__.py +0 -0
  48. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/__init__.py +0 -0
  49. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/align_def.py +0 -0
  50. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/export_def.py +0 -0
  51. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/fill_chrom_def.py +0 -0
  52. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/fill_def.py +0 -0
  53. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/find_consensus_def.py +0 -0
  54. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/find_ms2_def.py +0 -0
  55. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/integrate_chrom_def.py +0 -0
  56. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/integrate_def.py +0 -0
  57. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/merge_def.py +0 -0
  58. {masster-0.3.9 → masster-0.3.10}/src/masster/study/defaults/study_def.py +0 -0
  59. {masster-0.3.9 → masster-0.3.10}/src/masster/study/export.py +0 -0
  60. {masster-0.3.9 → masster-0.3.10}/src/masster/study/h5.py +0 -0
  61. {masster-0.3.9 → masster-0.3.10}/src/masster/study/helpers.py +0 -0
  62. {masster-0.3.9 → masster-0.3.10}/src/masster/study/helpers_optimized.py +0 -0
  63. {masster-0.3.9 → masster-0.3.10}/src/masster/study/load.py +0 -0
  64. {masster-0.3.9 → masster-0.3.10}/src/masster/study/parameters.py +0 -0
  65. {masster-0.3.9 → masster-0.3.10}/src/masster/study/plot.py +0 -0
  66. {masster-0.3.9 → masster-0.3.10}/src/masster/study/save.py +0 -0
  67. {masster-0.3.9 → masster-0.3.10}/src/masster/study/study.py +0 -0
  68. {masster-0.3.9 → masster-0.3.10}/src/masster/study/study5_schema.json +0 -0
  69. {masster-0.3.9 → masster-0.3.10}/tests/conftest.py +0 -0
  70. {masster-0.3.9 → masster-0.3.10}/tests/test_chromatogram.py +0 -0
  71. {masster-0.3.9 → masster-0.3.10}/tests/test_defaults.py +0 -0
  72. {masster-0.3.9 → masster-0.3.10}/tests/test_imports.py +0 -0
  73. {masster-0.3.9 → masster-0.3.10}/tests/test_integration.py +0 -0
  74. {masster-0.3.9 → masster-0.3.10}/tests/test_logger.py +0 -0
  75. {masster-0.3.9 → masster-0.3.10}/tests/test_parameters.py +0 -0
  76. {masster-0.3.9 → masster-0.3.10}/tests/test_sample.py +0 -0
  77. {masster-0.3.9 → masster-0.3.10}/tests/test_spectrum.py +0 -0
  78. {masster-0.3.9 → masster-0.3.10}/tests/test_study.py +0 -0
  79. {masster-0.3.9 → masster-0.3.10}/tests/test_version.py +0 -0
  80. {masster-0.3.9 → masster-0.3.10}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: masster
3
- Version: 0.3.9
3
+ Version: 0.3.10
4
4
  Summary: Mass spectrometry data analysis package
5
5
  Project-URL: homepage, https://github.com/zamboni-lab/masster
6
6
  Project-URL: repository, https://github.com/zamboni-lab/masster
@@ -1,7 +1,7 @@
1
1
 
2
2
  [project]
3
3
  name = "masster"
4
- version = "0.3.9"
4
+ version = "0.3.10"
5
5
  description = "Mass spectrometry data analysis package"
6
6
  authors = [
7
7
  { name = "Zamboni Lab" }
@@ -17,102 +17,100 @@ from typing import Any
17
17
 
18
18
  @dataclass
19
19
  class find_features_defaults:
20
+ """Configuration defaults for the feature-finding pipeline.
21
+
22
+ This dataclass centralizes parameters used by the `find_features()` routine
23
+ (mass-trace detection, elution-peak detection and feature assembly). The
24
+ purpose of this docstring is to explain the role and impact of the main
25
+ parameters users commonly tune.
26
+
27
+ Main parameters (what they mean, units and guidance):
28
+
29
+ - chrom_fwhm (float, seconds):
30
+ Expected chromatographic peak full-width at half-maximum (FWHM) in
31
+ seconds. This value informs the peak detection algorithms about the
32
+ typical temporal width of chromatographic peaks. It is used for
33
+ smoothing, window sizes when searching for local maxima and when
34
+ calculating RT-based tolerances. Use a value that matches your LC
35
+ method: smaller values for sharp, fast chromatography and larger values
36
+ for broader peaks. Default: 1.0 s.
37
+
38
+ - noise (float, intensity units):
39
+ Intensity threshold used to filter out low-intensity signals before
40
+ mass-trace and peak detection. Points with intensity below this
41
+ threshold are treated as background and typically ignored. Raising
42
+ `noise` reduces false positives from background fluctuations but may
43
+ remove low-abundance true peaks; lowering it increases sensitivity at
44
+ the cost of more noise. Default: 200.0 (instrument-dependent).
45
+
46
+ - chrom_peak_snr (float, unitless):
47
+ Minimum signal-to-noise ratio required to accept a detected
48
+ chromatographic peak. SNR is typically computed as peak height
49
+ (or crest intensity) divided by an estimate of local noise. A higher
50
+ `chrom_peak_snr` makes detection stricter (fewer false positives),
51
+ while a lower value makes detection more permissive (more low-SNR
52
+ peaks accepted). Typical values range from ~3 (relaxed) to >10
53
+ (stringent). Default: 10.0.
54
+
55
+ Use these three parameters together to balance sensitivity and
56
+ specificity for your dataset: tune `chrom_fwhm` to match chromatographic
57
+ peak shapes, set `noise` to a conservative background level for your
58
+ instrument, then adjust `chrom_peak_snr` to control how aggressively
59
+ peaks are accepted or rejected.
60
+
61
+ The class also contains many other configuration options (mass tolerances,
62
+ isotope handling, post-processing and reporting flags). See individual
63
+ parameter metadata (`_param_metadata`) for allowed ranges and types.
20
64
  """
21
- Parameters for mass spectrometry feature detection using OpenMS algorithms.
22
-
23
- This class consolidates all parameters used in the find_features() method including
24
- mass trace detection (MTD), elution peak detection (EPD), and feature finding (FFM).
25
- It provides type checking, validation, and comprehensive parameter descriptions.
26
-
27
- Mass Trace Detection (MTD) Parameters:
28
- tol_ppm: Mass error tolerance in parts-per-million for mass trace detection.
29
- noise: Noise threshold intensity to filter out low-intensity signals.
30
- min_trace_length_multiplier: Multiplier for minimum trace length (multiplied by chrom_fwhm_min).
31
- trace_termination_outliers: Number of outliers allowed before terminating a trace.
32
-
33
- Elution Peak Detection (EPD) Parameters:
34
- chrom_fwhm: Full width at half maximum for chromatographic peak shape.
35
- chrom_fwhm_min: Minimum FWHM for chromatographic peak detection.
36
- chrom_peak_snr: Signal-to-noise ratio required for chromatographic peaks.
37
- masstrace_snr_filtering: Whether to apply SNR filtering to mass traces.
38
- mz_scoring_13C: Whether to enable scoring of 13C isotopic patterns.
39
- width_filtering: Width filtering method for mass traces.
40
-
41
- Feature Finding (FFM) Parameters:
42
- remove_single_traces: Whether to remove mass traces without satellite isotopic traces.
43
- report_convex_hulls: Whether to report convex hulls for features.
44
- report_summed_ints: Whether to report summed intensities.
45
- report_chromatograms: Whether to report chromatograms.
46
-
47
- Post-processing Parameters:
48
- deisotope: Whether to perform deisotoping of detected features.
49
- deisotope_mz_tol: m/z tolerance for deisotoping.
50
- deisotope_rt_tol_factor: RT tolerance factor for deisotoping (multiplied by chrom_fwhm_min/4).
51
- eic_mz_tol: m/z tolerance for EIC extraction.
52
- eic_rt_tol: RT tolerance for EIC extraction.
53
-
54
- Available Methods:
55
- - validate(param_name, value): Validate a single parameter value
56
- - validate_all(): Validate all parameters at once
57
- - to_dict(): Convert parameters to dictionary
58
- - set_from_dict(param_dict, validate=True): Update multiple parameters from dict
59
- - set(param_name, value, validate=True): Set parameter value with validation
60
- - get(param_name): Get parameter value
61
- - get_description(param_name): Get parameter description
62
- - get_info(param_name): Get full parameter metadata
63
- - list_parameters(): Get list of all parameter names
64
- """
65
-
65
+
66
+ # Main params
67
+ noise: float = 200.0
68
+ chrom_fwhm: float = 1.0
69
+ chrom_peak_snr: float = 10.0
70
+
66
71
  # Mass Trace Detection parameters
67
72
  tol_ppm: float = 30.0
68
- noise: float = 200.0
69
- min_trace_length_multiplier: float = 1.0
70
- trace_termination_outliers: int = 2
73
+ reestimate_mt_sd: bool = True
74
+ quant_method: str = "area"
75
+ trace_termination_criterion: str = "outlier"
76
+ trace_termination_outliers: int = 5
77
+ min_sample_rate: float = 0.5
78
+
79
+ min_trace_length: float = 0.5
80
+ min_trace_length_multiplier: float = 0.2
81
+ max_trace_length: float = -1.0
71
82
 
72
83
  # Elution Peak Detection parameters
73
- chrom_fwhm: float = 1.0
74
- chrom_fwhm_min: float = 0.5
75
- chrom_peak_snr: float = 10.0
76
- masstrace_snr_filtering: bool = False
77
- mz_scoring_13C: bool = False
84
+ enabled: bool = True
85
+ chrom_fwhm_min: float = 0.2
86
+ chrom_fwhm_max: float = 60.0
78
87
  width_filtering: str = "fixed"
88
+ masstrace_snr_filtering: bool = False
79
89
 
80
90
  # Feature Finding parameters
91
+ local_rt_range: float = 1.0
92
+ local_mz_range: float = 5.0
93
+ charge_lower_bound: int = 0
94
+ charge_upper_bound: int = 5
95
+
96
+ report_smoothed_intensities: bool = False
81
97
  remove_single_traces: bool = False
82
98
  report_convex_hulls: bool = True
83
99
  report_summed_ints: bool = False
84
100
  report_chromatograms: bool = True
101
+ mz_scoring_13C: bool = False
102
+
103
+ threads: int = 1
104
+ no_progress: bool = False
105
+ debug: bool = False
85
106
 
86
107
  # Post-processing parameters
87
108
  deisotope: bool = True
88
109
  deisotope_mz_tol: float = 0.02
89
- deisotope_rt_tol_factor: float = 0.25 # Will be multiplied by chrom_fwhm_min/4
90
- eic_mz_tol: float = 0.01
91
- eic_rt_tol: float = 10.0
110
+ deisotope_rt_tol_factor: float = 0.5 # Will be multiplied by chrom_fwhm
92
111
 
93
- # Additional OpenMS FeatureFinderMetabo parameters
94
- threads: int = 1
95
- no_progress: bool = False
96
- debug: bool = False
97
- min_sample_rate: float = 0.5
98
- min_trace_length: int = 5
99
- min_fwhm: float = 1.0
100
- max_fwhm: float = 60.0
101
-
102
- # Additional Mass Trace Detection parameters
103
- trace_termination_criterion: str = "outlier"
104
- reestimate_mt_sd: bool = True
105
- quant_method: str = "area"
106
-
107
- # Additional Elution Peak Detection parameters
108
- enabled: bool = True
109
-
110
- # Additional Feature Finding parameters
111
- local_rt_range: float = 10.0
112
- local_mz_range: float = 6.5
113
- charge_lower_bound: int = 1
114
- charge_upper_bound: int = 3
115
- report_smoothed_intensities: bool = False
112
+ # chrom extraction parameters
113
+
116
114
 
117
115
  # Parameter metadata for validation and description
118
116
  _param_metadata: dict[str, dict[str, Any]] = field(
@@ -132,8 +130,8 @@ class find_features_defaults:
132
130
  "min_trace_length_multiplier": {
133
131
  "dtype": float,
134
132
  "description": "Multiplier for minimum trace length calculation (multiplied by chrom_fwhm_min)",
135
- "min_value": 1.0,
136
- "max_value": 10.0,
133
+ "min_value": 0.1,
134
+ "max_value": 2.0,
137
135
  },
138
136
  "trace_termination_outliers": {
139
137
  "dtype": int,
@@ -204,18 +202,6 @@ class find_features_defaults:
204
202
  "min_value": 0.1,
205
203
  "max_value": 2.0,
206
204
  },
207
- "eic_mz_tol": {
208
- "dtype": float,
209
- "description": "m/z tolerance for EIC extraction (Da)",
210
- "min_value": 0.001,
211
- "max_value": 0.1,
212
- },
213
- "eic_rt_tol": {
214
- "dtype": float,
215
- "description": "RT tolerance for EIC extraction (seconds)",
216
- "min_value": 1.0,
217
- "max_value": 60.0,
218
- },
219
205
  "threads": {
220
206
  "dtype": int,
221
207
  "description": "Number of threads to use for parallel processing",
@@ -242,13 +228,13 @@ class find_features_defaults:
242
228
  "min_value": 2,
243
229
  "max_value": 100,
244
230
  },
245
- "min_fwhm": {
231
+ ''' "min_fwhm": {
246
232
  "dtype": float,
247
233
  "description": "Minimum full width at half maximum for peaks (seconds)",
248
234
  "min_value": 0.1,
249
235
  "max_value": 10.0,
250
- },
251
- "max_fwhm": {
236
+ },'''
237
+ "chrom_fwhm_max": {
252
238
  "dtype": float,
253
239
  "description": "Maximum full width at half maximum for peaks (seconds)",
254
240
  "min_value": 1.0,
@@ -53,6 +53,9 @@ class sample_defaults:
53
53
  centroid_prominence: int = -1
54
54
  max_points_per_spectrum: int = 50000
55
55
  dia_window: float | None = None
56
+
57
+ eic_mz_tol: float = 0.01
58
+ eic_rt_tol: float = 10.0
56
59
 
57
60
  _param_metadata: dict[str, dict[str, Any]] = field(
58
61
  default_factory=lambda: {
@@ -163,6 +166,18 @@ class sample_defaults:
163
166
  "default": None,
164
167
  "min_value": 0.0,
165
168
  },
169
+ "eic_mz_tol": {
170
+ "dtype": float,
171
+ "description": "m/z tolerance for EIC extraction (Da)",
172
+ "min_value": 0.001,
173
+ "max_value": 1.0,
174
+ },
175
+ "eic_rt_tol": {
176
+ "dtype": float,
177
+ "description": "RT tolerance for EIC extraction (seconds)",
178
+ "min_value": 0.2,
179
+ "max_value": 60.0,
180
+ },
166
181
  },
167
182
  repr=False,
168
183
  )
@@ -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
- filename: str = os.path.basename(self.file_path)
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 filename.upper() or "ZTS" in filename.upper():
431
- if "EAD" in filename.upper():
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 filename.upper():
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", filename.upper())
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", filename.upper())
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 filename.upper():
518
+ elif "EAD" in file_basename.upper():
519
519
  activation = "EAD"
520
- # search ([0-9]*KE) in filename.upper() using regex
521
- match = re.search(r"(\d+)KE", filename.upper())
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", filename.upper())
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": 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,
@@ -519,6 +519,14 @@ def plot_2d(
519
519
  # find features with ms2_scans not None and iso==0
520
520
  features_df = feats[feats["ms2_scans"].notnull()]
521
521
  # Create feature points with proper sizing method
522
+ feature_hover_1 = HoverTool(tooltips=[
523
+ ("rt", "@rt"),
524
+ ("m/z", "@mz{0.0000}"),
525
+ ("feature_uid", "@feature_uid"),
526
+ ("inty", "@inty"),
527
+ ("quality", "@quality"),
528
+ ("rt_delta", "@rt_delta"),
529
+ ])
522
530
  feature_points_1 = hv.Points(
523
531
  features_df,
524
532
  kdims=["rt", "mz"],
@@ -536,11 +544,19 @@ def plot_2d(
536
544
  color=color_1,
537
545
  marker=marker_type,
538
546
  size=size_1,
539
- tools=["hover"],
547
+ tools=[feature_hover_1],
540
548
  hooks=hooks,
541
549
  )
542
550
  # find features without MS2 data
543
551
  features_df = feats[feats["ms2_scans"].isnull()]
552
+ feature_hover_2 = HoverTool(tooltips=[
553
+ ("rt", "@rt"),
554
+ ("m/z", "@mz{0.0000}"),
555
+ ("feature_uid", "@feature_uid"),
556
+ ("inty", "@inty"),
557
+ ("quality", "@quality"),
558
+ ("rt_delta", "@rt_delta"),
559
+ ])
544
560
  feature_points_2 = hv.Points(
545
561
  features_df,
546
562
  kdims=["rt", "mz"],
@@ -557,7 +573,7 @@ def plot_2d(
557
573
  color="red",
558
574
  marker=marker_type,
559
575
  size=size_2,
560
- tools=["hover"],
576
+ tools=[feature_hover_2],
561
577
  hooks=hooks,
562
578
  )
563
579
 
@@ -567,6 +583,16 @@ def plot_2d(
567
583
  # Convert to pandas for plotting compatibility
568
584
  if hasattr(features_df, "to_pandas"):
569
585
  features_df = features_df.to_pandas()
586
+ feature_hover_iso = HoverTool(tooltips=[
587
+ ("rt", "@rt"),
588
+ ("m/z", "@mz{0.0000}"),
589
+ ("feature_uid", "@feature_uid"),
590
+ ("inty", "@inty"),
591
+ ("quality", "@quality"),
592
+ ("rt_delta", "@rt_delta"),
593
+ ("iso", "@iso"),
594
+ ("iso_of", "@iso_of"),
595
+ ])
570
596
  feature_points_iso = hv.Points(
571
597
  features_df,
572
598
  kdims=["rt", "mz"],
@@ -585,7 +611,7 @@ def plot_2d(
585
611
  color="violet",
586
612
  marker=marker_type,
587
613
  size=size_1,
588
- tools=["hover"],
614
+ tools=[feature_hover_iso],
589
615
  hooks=hooks,
590
616
  )
591
617
  if show_ms2:
@@ -597,6 +623,13 @@ def plot_2d(
597
623
  if len(ms2_orphan) > 0:
598
624
  # pandalize
599
625
  ms2 = ms2_orphan.to_pandas()
626
+ ms2_hover_3 = HoverTool(tooltips=[
627
+ ("rt", "@rt"),
628
+ ("prec_mz", "@prec_mz{0.0000}"),
629
+ ("index", "@index"),
630
+ ("inty_tot", "@inty_tot"),
631
+ ("bl", "@bl"),
632
+ ])
600
633
  feature_points_3 = hv.Points(
601
634
  ms2,
602
635
  kdims=["rt", "prec_mz"],
@@ -606,7 +639,7 @@ def plot_2d(
606
639
  color=color_2,
607
640
  marker="x",
608
641
  size=size_2,
609
- tools=["hover"],
642
+ tools=[ms2_hover_3],
610
643
  )
611
644
 
612
645
  ms2_linked = self.scans_df.filter(pl.col("ms_level") == 2).filter(
@@ -615,6 +648,13 @@ def plot_2d(
615
648
  if len(ms2_linked) > 0:
616
649
  # pandalize
617
650
  ms2 = ms2_linked.to_pandas()
651
+ ms2_hover_4 = HoverTool(tooltips=[
652
+ ("rt", "@rt"),
653
+ ("prec_mz", "@prec_mz{0.0000}"),
654
+ ("index", "@index"),
655
+ ("inty_tot", "@inty_tot"),
656
+ ("bl", "@bl"),
657
+ ])
618
658
  feature_points_4 = hv.Points(
619
659
  ms2,
620
660
  kdims=["rt", "prec_mz"],
@@ -624,7 +664,7 @@ def plot_2d(
624
664
  color=color_1,
625
665
  marker="x",
626
666
  size=size_2,
627
- tools=["hover"],
667
+ tools=[ms2_hover_4],
628
668
  )
629
669
 
630
670
  overlay = raster
@@ -1041,6 +1081,18 @@ def plot_2d_oracle(
1041
1081
  feat_df = feats.copy()
1042
1082
  feat_df = feat_df[feat_df["id_level"] == 2]
1043
1083
 
1084
+ oracle_hover_1 = HoverTool(tooltips=[
1085
+ ("rt", "@rt"),
1086
+ ("m/z", "@mz{0.0000}"),
1087
+ ("feature_uid", "@feature_uid"),
1088
+ ("id_level", "@id_level"),
1089
+ ("id_class", "@id_class"),
1090
+ ("id_label", "@id_label"),
1091
+ ("id_ion", "@id_ion"),
1092
+ ("id_evidence", "@id_evidence"),
1093
+ ("score", "@score"),
1094
+ ("score2", "@score2"),
1095
+ ])
1044
1096
  feature_points_1 = hv.Points(
1045
1097
  feat_df,
1046
1098
  kdims=["rt", "mz"],
@@ -1062,7 +1114,7 @@ def plot_2d_oracle(
1062
1114
  marker="circle",
1063
1115
  size=markersize,
1064
1116
  fill_alpha=1.0,
1065
- tools=["hover"],
1117
+ tools=[oracle_hover_1],
1066
1118
  )
1067
1119
 
1068
1120
  # feature_points_2 are all features that have ms2_scans not null and id_level ==1
@@ -1070,6 +1122,15 @@ def plot_2d_oracle(
1070
1122
  feat_df = feats.copy()
1071
1123
  feat_df = feat_df[(feat_df["ms2_scans"].notnull()) & (feat_df["id_level"] == 1)]
1072
1124
  if len(feat_df) > 0:
1125
+ oracle_hover_2 = HoverTool(tooltips=[
1126
+ ("rt", "@rt"),
1127
+ ("m/z", "@mz{0.0000}"),
1128
+ ("feature_uid", "@feature_uid"),
1129
+ ("id_level", "@id_level"),
1130
+ ("id_label", "@id_label"),
1131
+ ("id_ion", "@id_ion"),
1132
+ ("id_class", "@id_class"),
1133
+ ])
1073
1134
  feature_points_2 = hv.Points(
1074
1135
  feat_df,
1075
1136
  kdims=["rt", "mz"],
@@ -1088,7 +1149,7 @@ def plot_2d_oracle(
1088
1149
  marker="circle",
1089
1150
  size=markersize,
1090
1151
  fill_alpha=0.0,
1091
- tools=["hover"],
1152
+ tools=[oracle_hover_2],
1092
1153
  )
1093
1154
 
1094
1155
  # feature_points_3 are all features that have ms2_scans null and id_level ==1
@@ -1096,6 +1157,15 @@ def plot_2d_oracle(
1096
1157
  feat_df = feats.copy()
1097
1158
  feat_df = feat_df[(feat_df["ms2_scans"].isnull()) & (feat_df["id_level"] == 1)]
1098
1159
  if len(feat_df) > 0:
1160
+ oracle_hover_3 = HoverTool(tooltips=[
1161
+ ("rt", "@rt"),
1162
+ ("m/z", "@mz{0.0000}"),
1163
+ ("feature_uid", "@feature_uid"),
1164
+ ("id_level", "@id_level"),
1165
+ ("id_label", "@id_label"),
1166
+ ("id_ion", "@id_ion"),
1167
+ ("id_class", "@id_class"),
1168
+ ])
1099
1169
  feature_points_3 = hv.Points(
1100
1170
  feat_df,
1101
1171
  kdims=["rt", "mz"],
@@ -1114,7 +1184,7 @@ def plot_2d_oracle(
1114
1184
  marker="diamond",
1115
1185
  size=markersize,
1116
1186
  fill_alpha=0.0,
1117
- tools=["hover"],
1187
+ tools=[oracle_hover_3],
1118
1188
  )
1119
1189
 
1120
1190
  # feature_points_4 are all features that have ms2_scans null and id_level ==0
@@ -1122,6 +1192,12 @@ def plot_2d_oracle(
1122
1192
  feat_df = feats.copy()
1123
1193
  feat_df = feat_df[(feat_df["ms2_scans"].notnull()) & (feat_df["id_level"] < 1)]
1124
1194
  if len(feat_df) > 0:
1195
+ oracle_hover_4 = HoverTool(tooltips=[
1196
+ ("rt", "@rt"),
1197
+ ("m/z", "@mz{0.0000}"),
1198
+ ("feature_uid", "@feature_uid"),
1199
+ ("inty", "@inty"),
1200
+ ])
1125
1201
  feature_points_4 = hv.Points(
1126
1202
  feat_df,
1127
1203
  kdims=["rt", "mz"],
@@ -1132,14 +1208,20 @@ def plot_2d_oracle(
1132
1208
  marker="circle",
1133
1209
  size=markersize,
1134
1210
  fill_alpha=0.0,
1135
- tools=["hover"],
1211
+ tools=[oracle_hover_4],
1136
1212
  )
1137
1213
 
1138
- # feature_points_4 are all features that have ms2_scans null and id_level ==0
1214
+ # feature_points_5 are all features that have ms2_scans null and id_level ==0
1139
1215
  feature_points_5 = None
1140
1216
  feat_df = feats.copy()
1141
1217
  feat_df = feat_df[(feat_df["ms2_scans"].isnull()) & (feat_df["id_level"] < 1)]
1142
1218
  if len(feat_df) > 0:
1219
+ oracle_hover_5 = HoverTool(tooltips=[
1220
+ ("rt", "@rt"),
1221
+ ("m/z", "@mz{0.0000}"),
1222
+ ("feature_uid", "@feature_uid"),
1223
+ ("inty", "@inty"),
1224
+ ])
1143
1225
  feature_points_5 = hv.Points(
1144
1226
  feat_df,
1145
1227
  kdims=["rt", "mz"],
@@ -1150,7 +1232,7 @@ def plot_2d_oracle(
1150
1232
  marker="diamond",
1151
1233
  fill_alpha=0.0,
1152
1234
  size=markersize,
1153
- tools=["hover"],
1235
+ tools=[oracle_hover_5],
1154
1236
  )
1155
1237
 
1156
1238
  overlay = raster