masster 0.4.4__py3-none-any.whl → 0.4.5__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.

Files changed (39) hide show
  1. masster/__init__.py +8 -8
  2. masster/chromatogram.py +1 -1
  3. masster/data/libs/urine.csv +3 -3
  4. masster/logger.py +11 -11
  5. masster/sample/__init__.py +1 -1
  6. masster/sample/adducts.py +338 -264
  7. masster/sample/defaults/find_adducts_def.py +21 -8
  8. masster/sample/h5.py +561 -282
  9. masster/sample/helpers.py +131 -75
  10. masster/sample/lib.py +4 -4
  11. masster/sample/load.py +31 -17
  12. masster/sample/parameters.py +1 -1
  13. masster/sample/plot.py +7 -7
  14. masster/sample/processing.py +117 -87
  15. masster/sample/sample.py +103 -90
  16. masster/sample/sample5_schema.json +44 -44
  17. masster/sample/save.py +35 -12
  18. masster/spectrum.py +1 -1
  19. masster/study/__init__.py +1 -1
  20. masster/study/defaults/align_def.py +5 -1
  21. masster/study/defaults/identify_def.py +3 -1
  22. masster/study/defaults/study_def.py +58 -25
  23. masster/study/export.py +360 -210
  24. masster/study/h5.py +560 -158
  25. masster/study/helpers.py +496 -203
  26. masster/study/helpers_optimized.py +1 -1
  27. masster/study/id.py +538 -349
  28. masster/study/load.py +233 -143
  29. masster/study/plot.py +71 -71
  30. masster/study/processing.py +456 -254
  31. masster/study/save.py +15 -5
  32. masster/study/study.py +213 -131
  33. masster/study/study5_schema.json +149 -149
  34. {masster-0.4.4.dist-info → masster-0.4.5.dist-info}/METADATA +3 -1
  35. {masster-0.4.4.dist-info → masster-0.4.5.dist-info}/RECORD +39 -39
  36. {masster-0.4.4.dist-info → masster-0.4.5.dist-info}/WHEEL +0 -0
  37. {masster-0.4.4.dist-info → masster-0.4.5.dist-info}/entry_points.txt +0 -0
  38. {masster-0.4.4.dist-info → masster-0.4.5.dist-info}/licenses/LICENSE +0 -0
  39. {masster-0.4.4.dist-info → masster-0.4.5.dist-info}/top_level.txt +0 -0
masster/study/plot.py CHANGED
@@ -26,17 +26,17 @@ def _isolated_save_plot(plot_object, filename, abs_filename, logger, plot_title=
26
26
  # Use isolated file saving that doesn't affect global output state
27
27
  from bokeh.resources import Resources
28
28
  from bokeh.embed import file_html
29
-
29
+
30
30
  # Create HTML content without affecting global state
31
31
  resources = Resources(mode='cdn')
32
32
  html = file_html(plot_object, resources, title=plot_title)
33
-
33
+
34
34
  # Write directly to file
35
35
  with open(filename, 'w', encoding='utf-8') as f:
36
36
  f.write(html)
37
-
37
+
38
38
  logger.info(f"Plot saved to: {abs_filename}")
39
-
39
+
40
40
  elif filename.endswith(".png"):
41
41
  try:
42
42
  from bokeh.io.export import export_png
@@ -47,13 +47,13 @@ def _isolated_save_plot(plot_object, filename, abs_filename, logger, plot_title=
47
47
  html_filename = filename.replace('.png', '.html')
48
48
  from bokeh.resources import Resources
49
49
  from bokeh.embed import file_html
50
-
50
+
51
51
  resources = Resources(mode='cdn')
52
52
  html = file_html(plot_object, resources, title=plot_title)
53
-
53
+
54
54
  with open(html_filename, 'w', encoding='utf-8') as f:
55
55
  f.write(html)
56
-
56
+
57
57
  logger.warning(f"PNG export not available, saved as HTML instead: {html_filename}")
58
58
  elif filename.endswith(".pdf"):
59
59
  # Try to save as PDF, fall back to HTML if not available
@@ -66,25 +66,25 @@ def _isolated_save_plot(plot_object, filename, abs_filename, logger, plot_title=
66
66
  html_filename = filename.replace('.pdf', '.html')
67
67
  from bokeh.resources import Resources
68
68
  from bokeh.embed import file_html
69
-
69
+
70
70
  resources = Resources(mode='cdn')
71
71
  html = file_html(plot_object, resources, title=plot_title)
72
-
72
+
73
73
  with open(html_filename, 'w', encoding='utf-8') as f:
74
74
  f.write(html)
75
-
75
+
76
76
  logger.warning(f"PDF export not available, saved as HTML instead: {html_filename}")
77
77
  else:
78
78
  # Default to HTML for unknown extensions using isolated approach
79
79
  from bokeh.resources import Resources
80
80
  from bokeh.embed import file_html
81
-
81
+
82
82
  resources = Resources(mode='cdn')
83
83
  html = file_html(plot_object, resources, title=plot_title)
84
-
84
+
85
85
  with open(filename, 'w', encoding='utf-8') as f:
86
86
  f.write(html)
87
-
87
+
88
88
  logger.info(f"Plot saved to: {abs_filename}")
89
89
 
90
90
 
@@ -97,28 +97,28 @@ def _isolated_show_notebook(plot_object):
97
97
  import holoviews as hv
98
98
  import warnings
99
99
  import logging
100
-
100
+
101
101
  # Suppress both warnings and logging messages for the specific Bokeh callback warnings
102
102
  # that occur when Panel components with Python callbacks are converted to standalone Bokeh
103
103
  bokeh_logger = logging.getLogger('bokeh.embed.util')
104
104
  original_level = bokeh_logger.level
105
105
  bokeh_logger.setLevel(logging.ERROR) # Suppress WARNING level messages
106
-
106
+
107
107
  with warnings.catch_warnings():
108
108
  warnings.filterwarnings("ignore", message=".*standalone HTML/JS output.*", category=UserWarning)
109
109
  warnings.filterwarnings("ignore", message=".*real Python callbacks.*", category=UserWarning)
110
-
110
+
111
111
  try:
112
112
  # First clear all output state
113
113
  reset_output()
114
-
114
+
115
115
  # Set notebook mode
116
116
  output_notebook(hide_banner=True)
117
-
118
- # Reset Holoviews to notebook mode
117
+
118
+ # Reset Holoviews to notebook mode
119
119
  hv.extension('bokeh', logo=False)
120
120
  hv.output(backend='bokeh', mode='jupyter')
121
-
121
+
122
122
  # Show in notebook
123
123
  show(plot_object)
124
124
  finally:
@@ -129,16 +129,16 @@ def _isolated_show_notebook(plot_object):
129
129
  def _isolated_save_panel_plot(panel_obj, filename, abs_filename, logger, plot_title):
130
130
  """
131
131
  Save a Panel plot using isolated approach that doesn't affect global Bokeh state.
132
-
132
+
133
133
  Args:
134
134
  panel_obj: Panel object to save
135
- filename: Target filename
135
+ filename: Target filename
136
136
  abs_filename: Absolute path for logging
137
137
  logger: Logger instance
138
138
  plot_title: Title for logging
139
139
  """
140
140
  import os # Import os for path operations
141
-
141
+
142
142
  if filename.endswith(".html"):
143
143
  # Panel save method should be isolated but let's be sure
144
144
  try:
@@ -147,7 +147,7 @@ def _isolated_save_panel_plot(panel_obj, filename, abs_filename, logger, plot_ti
147
147
  logger.info(f"{plot_title} saved to: {abs_filename}")
148
148
  except Exception as e:
149
149
  logger.error(f"Failed to save {plot_title}: {e}")
150
-
150
+
151
151
  elif filename.endswith(".png"):
152
152
  try:
153
153
  from panel.io.save import save_png
@@ -164,7 +164,7 @@ def _isolated_save_panel_plot(panel_obj, filename, abs_filename, logger, plot_ti
164
164
  logger.warning(f"PNG export not available, saved as HTML instead: {abs_html_filename}")
165
165
  except Exception as e:
166
166
  logger.error(f"Failed to save {plot_title} as HTML fallback: {e}")
167
-
167
+
168
168
  elif filename.endswith(".pdf"):
169
169
  # Try to save as PDF, fall back to HTML if not available
170
170
  try:
@@ -193,29 +193,29 @@ def _isolated_save_panel_plot(panel_obj, filename, abs_filename, logger, plot_ti
193
193
  def _isolated_show_panel_notebook(panel_obj):
194
194
  """
195
195
  Show a Panel plot in notebook with state isolation to prevent browser opening.
196
-
196
+
197
197
  Args:
198
198
  panel_obj: Panel object to display
199
199
  """
200
200
  # Reset Bokeh state completely to prevent browser opening if output_file was called before
201
201
  from bokeh.io import reset_output, output_notebook
202
202
  import holoviews as hv
203
-
203
+
204
204
  # First clear all output state
205
205
  reset_output()
206
-
206
+
207
207
  # Set notebook mode
208
208
  output_notebook(hide_banner=True)
209
-
210
- # Reset Holoviews to notebook mode
209
+
210
+ # Reset Holoviews to notebook mode
211
211
  hv.extension('bokeh', logo=False)
212
212
  hv.output(backend='bokeh', mode='jupyter')
213
-
214
- # For Panel objects in notebooks, use pn.extension and display inline
215
- import panel as pn
213
+
214
+ # For Panel objects in notebooks, use on.extension and display inline
215
+ import panel as on
216
216
  try:
217
217
  # Configure Panel for notebook display
218
- pn.extension('bokeh', inline=True, comms='vscode')
218
+ on.extension('bokeh', inline=True, comms='vscode')
219
219
  # Use IPython display to show inline instead of show()
220
220
  from IPython.display import display
221
221
  display(panel_obj)
@@ -278,25 +278,25 @@ def plot_alignment(
278
278
  # Create mapping from sample_uid to map_id and filter accordingly
279
279
  if hasattr(self, "samples_df") and self.samples_df is not None and not self.samples_df.is_empty():
280
280
  samples_info = self.samples_df.to_pandas()
281
-
281
+
282
282
  # Filter samples_info to only selected sample_uids and get their map_ids
283
283
  selected_samples = samples_info[samples_info["sample_uid"].isin(sample_uids)]
284
284
  if selected_samples.empty:
285
285
  self.logger.error("No matching samples found for the provided sample_uids.")
286
286
  return
287
-
287
+
288
288
  # Get the map_ids for selected samples
289
289
  selected_map_ids = selected_samples["map_id"].tolist()
290
-
290
+
291
291
  # Filter feature maps based on map_ids
292
292
  filtered_maps = []
293
293
  for map_id in selected_map_ids:
294
294
  if 0 <= map_id < len(fmaps):
295
295
  filtered_maps.append(fmaps[map_id])
296
-
296
+
297
297
  fmaps = filtered_maps
298
298
  samples_info = selected_samples.reset_index(drop=True)
299
-
299
+
300
300
  if not fmaps:
301
301
  self.logger.error("No feature maps found for the selected samples.")
302
302
  return
@@ -438,7 +438,7 @@ def plot_alignment(
438
438
 
439
439
  # Use Polars instead of pandas
440
440
  features_df = self.features_df
441
-
441
+
442
442
  # Filter by selected samples if specified
443
443
  if sample_uids is not None:
444
444
  features_df = features_df.filter(pl.col("sample_uid").is_in(sample_uids))
@@ -633,10 +633,10 @@ def plot_alignment(
633
633
  import os
634
634
  if not os.path.isabs(filename):
635
635
  filename = os.path.join(self.folder, filename)
636
-
636
+
637
637
  # Convert to absolute path for logging
638
638
  abs_filename = os.path.abspath(filename)
639
-
639
+
640
640
  # Use isolated file saving
641
641
  _isolated_save_plot(layout, filename, abs_filename, self.logger, "Alignment Plot")
642
642
  else:
@@ -864,10 +864,10 @@ def plot_consensus_2d(
864
864
  import os
865
865
  if not os.path.isabs(filename):
866
866
  filename = os.path.join(self.folder, filename)
867
-
867
+
868
868
  # Convert to absolute path for logging
869
869
  abs_filename = os.path.abspath(filename)
870
-
870
+
871
871
  # Use isolated file saving
872
872
  _isolated_save_plot(p, filename, abs_filename, self.logger, "Consensus 2D Plot")
873
873
  else:
@@ -1091,17 +1091,17 @@ def plot_samples_2d(
1091
1091
  # Only set legend properties if a legend was actually created to avoid Bokeh warnings
1092
1092
  if getattr(p, "legend", None) and len(p.legend) > 0:
1093
1093
  p.legend.visible = False
1094
-
1094
+
1095
1095
  # Apply consistent save/display behavior
1096
1096
  if filename is not None:
1097
1097
  # Convert relative paths to absolute paths using study folder as base
1098
1098
  import os
1099
1099
  if not os.path.isabs(filename):
1100
1100
  filename = os.path.join(self.folder, filename)
1101
-
1101
+
1102
1102
  # Convert to absolute path for logging
1103
1103
  abs_filename = os.path.abspath(filename)
1104
-
1104
+
1105
1105
  # Use isolated file saving
1106
1106
  _isolated_save_plot(p, filename, abs_filename, self.logger, "Samples 2D Plot")
1107
1107
  else:
@@ -1132,7 +1132,7 @@ def plot_bpc(
1132
1132
  from bokeh.plotting import figure, show, output_file
1133
1133
  from bokeh.models import ColumnDataSource, HoverTool
1134
1134
  from bokeh.io.export import export_png
1135
- from masster.study.helpers import get_bpc
1135
+ from master.study.helpers import get_bpc
1136
1136
 
1137
1137
  sample_uids = self._get_sample_uids(samples)
1138
1138
  if not sample_uids:
@@ -1271,10 +1271,10 @@ def plot_bpc(
1271
1271
  import os
1272
1272
  if not os.path.isabs(filename):
1273
1273
  filename = os.path.join(self.folder, filename)
1274
-
1274
+
1275
1275
  # Convert to absolute path for logging
1276
1276
  abs_filename = os.path.abspath(filename)
1277
-
1277
+
1278
1278
  # Use isolated file saving
1279
1279
  _isolated_save_plot(p, filename, abs_filename, self.logger, "BPC Plot")
1280
1280
  else:
@@ -1309,7 +1309,7 @@ def plot_eic(
1309
1309
  from bokeh.plotting import figure, show, output_file
1310
1310
  from bokeh.models import ColumnDataSource, HoverTool
1311
1311
  from bokeh.io.export import export_png
1312
- from masster.study.helpers import get_eic
1312
+ from master.study.helpers import get_eic
1313
1313
 
1314
1314
  # Use study's eic_mz_tol parameter as default if not provided
1315
1315
  if mz_tol is None:
@@ -1442,10 +1442,10 @@ def plot_eic(
1442
1442
  import os
1443
1443
  if not os.path.isabs(filename):
1444
1444
  filename = os.path.join(self.folder, filename)
1445
-
1445
+
1446
1446
  # Convert to absolute path for logging
1447
1447
  abs_filename = os.path.abspath(filename)
1448
-
1448
+
1449
1449
  # Use isolated file saving
1450
1450
  _isolated_save_plot(p, filename, abs_filename, self.logger, "EIC Plot")
1451
1451
  else:
@@ -1595,10 +1595,10 @@ def plot_rt_correction(
1595
1595
  import os
1596
1596
  if not os.path.isabs(filename):
1597
1597
  filename = os.path.join(self.folder, filename)
1598
-
1598
+
1599
1599
  # Convert to absolute path for logging
1600
1600
  abs_filename = os.path.abspath(filename)
1601
-
1601
+
1602
1602
  # Use isolated file saving
1603
1603
  _isolated_save_plot(p, filename, abs_filename, self.logger, "RT Correction Plot")
1604
1604
  else:
@@ -1692,19 +1692,19 @@ def plot_chrom(
1692
1692
  sorted_indices = np.argsort(rt)
1693
1693
  rt = rt[sorted_indices]
1694
1694
  inty = inty[sorted_indices]
1695
-
1695
+
1696
1696
  # Get sample uid for this sample name
1697
1697
  sample_uid = sample_name_to_uid.get(sample, None)
1698
1698
  sample_color = color_map.get(sample, "#1f77b4")
1699
-
1699
+
1700
1700
  # Create arrays with sample information for hover tooltips
1701
1701
  sample_names_array = [sample] * len(rt)
1702
1702
  sample_uids_array = [sample_uid] * len(rt)
1703
1703
  sample_colors_array = [sample_color] * len(rt)
1704
-
1704
+
1705
1705
  curve = hv.Curve(
1706
- (rt, inty, sample_names_array, sample_uids_array, sample_colors_array),
1707
- kdims=["RT"],
1706
+ (rt, inty, sample_names_array, sample_uids_array, sample_colors_array),
1707
+ kdims=["RT"],
1708
1708
  vdims=["inty", "sample_name", "sample_uid", "sample_color"]
1709
1709
  ).opts(
1710
1710
  color=color_map[sample],
@@ -1775,17 +1775,17 @@ def plot_chrom(
1775
1775
  # stack vertically.
1776
1776
  # Stack all plots vertically in a Panel column
1777
1777
  layout = panel.Column(*[panel.panel(plot) for plot in plots])
1778
-
1778
+
1779
1779
  # Apply consistent save/display behavior
1780
1780
  if filename is not None:
1781
1781
  # Convert relative paths to absolute paths using study folder as base
1782
1782
  import os
1783
1783
  if not os.path.isabs(filename):
1784
1784
  filename = os.path.join(self.folder, filename)
1785
-
1785
+
1786
1786
  # Convert to absolute path for logging
1787
1787
  abs_filename = os.path.abspath(filename)
1788
-
1788
+
1789
1789
  # Use isolated Panel saving
1790
1790
  _isolated_save_panel_plot(panel.panel(layout), filename, abs_filename, self.logger, "Chromatogram Plot")
1791
1791
  else:
@@ -2031,10 +2031,10 @@ def plot_consensus_stats(
2031
2031
  import os
2032
2032
  if not os.path.isabs(filename):
2033
2033
  filename = os.path.join(self.folder, filename)
2034
-
2034
+
2035
2035
  # Convert to absolute path for logging
2036
2036
  abs_filename = os.path.abspath(filename)
2037
-
2037
+
2038
2038
  # Use isolated file saving
2039
2039
  _isolated_save_plot(grid, filename, abs_filename, self.logger, "Consensus Stats Plot")
2040
2040
  else:
@@ -2097,7 +2097,7 @@ def plot_pca(
2097
2097
 
2098
2098
  # Extract only the sample columns (exclude consensus_uid column)
2099
2099
  sample_cols = [col for col in consensus_matrix.columns if col != "consensus_uid"]
2100
-
2100
+
2101
2101
  # Convert consensus matrix to numpy, excluding the consensus_uid column
2102
2102
  if hasattr(consensus_matrix, "select"):
2103
2103
  # Polars DataFrame
@@ -2295,10 +2295,10 @@ def plot_pca(
2295
2295
  import os
2296
2296
  if not os.path.isabs(filename):
2297
2297
  filename = os.path.join(self.folder, filename)
2298
-
2298
+
2299
2299
  # Convert to absolute path for logging
2300
2300
  abs_filename = os.path.abspath(filename)
2301
-
2301
+
2302
2302
  # Use isolated file saving
2303
2303
  _isolated_save_plot(p, filename, abs_filename, self.logger, "PCA Plot")
2304
2304
  else:
@@ -2325,7 +2325,7 @@ def plot_tic(
2325
2325
  from bokeh.plotting import figure, show, output_file
2326
2326
  from bokeh.models import ColumnDataSource, HoverTool
2327
2327
  from bokeh.io.export import export_png
2328
- from masster.study.helpers import get_tic
2328
+ from master.study.helpers import get_tic
2329
2329
 
2330
2330
  sample_uids = self._get_sample_uids(samples)
2331
2331
  if not sample_uids:
@@ -2449,10 +2449,10 @@ def plot_tic(
2449
2449
  import os
2450
2450
  if not os.path.isabs(filename):
2451
2451
  filename = os.path.join(self.folder, filename)
2452
-
2452
+
2453
2453
  # Convert to absolute path for logging
2454
2454
  abs_filename = os.path.abspath(filename)
2455
-
2455
+
2456
2456
  # Use isolated file saving
2457
2457
  _isolated_save_plot(p, filename, abs_filename, self.logger, "TIC Plot")
2458
2458
  else: