openms-insight 0.1.10__py3-none-any.whl → 0.1.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.
@@ -238,9 +238,10 @@ def downsample_2d_simple(
238
238
  data: Union[pl.LazyFrame, pl.DataFrame],
239
239
  max_points: int = 20000,
240
240
  intensity_column: str = "intensity",
241
+ descending: bool = True,
241
242
  ) -> pl.LazyFrame:
242
243
  """
243
- Simple downsampling by keeping highest-intensity points.
244
+ Simple downsampling by keeping top-priority points.
244
245
 
245
246
  A simpler alternative to downsample_2d that doesn't require scipy.
246
247
  Less spatially aware but still preserves important peaks.
@@ -249,6 +250,7 @@ def downsample_2d_simple(
249
250
  data: Input data as Polars LazyFrame or DataFrame
250
251
  max_points: Maximum number of points to keep
251
252
  intensity_column: Name of intensity column for ranking
253
+ descending: If True (default), keep highest values. If False, keep lowest.
252
254
 
253
255
  Returns:
254
256
  Downsampled data as Polars LazyFrame
@@ -256,7 +258,7 @@ def downsample_2d_simple(
256
258
  if isinstance(data, pl.DataFrame):
257
259
  data = data.lazy()
258
260
 
259
- return data.sort(intensity_column, descending=True).head(max_points)
261
+ return data.sort(intensity_column, descending=descending).head(max_points)
260
262
 
261
263
 
262
264
  def downsample_2d_streaming(
@@ -269,6 +271,7 @@ def downsample_2d_streaming(
269
271
  y_bins: int = 50,
270
272
  x_range: Optional[tuple] = None,
271
273
  y_range: Optional[tuple] = None,
274
+ descending: bool = True,
272
275
  ) -> pl.LazyFrame:
273
276
  """
274
277
  Streaming 2D downsampling using pure Polars operations.
@@ -287,6 +290,8 @@ def downsample_2d_streaming(
287
290
  y_bins: Number of bins along y-axis
288
291
  x_range: Optional (min, max) tuple for x-axis. If None, computed from data.
289
292
  y_range: Optional (min, max) tuple for y-axis. If None, computed from data.
293
+ descending: If True (default), keep highest intensity per bin.
294
+ If False, keep lowest intensity per bin.
290
295
 
291
296
  Returns:
292
297
  Downsampled data as Polars LazyFrame (fully lazy, no collection)
@@ -319,7 +324,7 @@ def downsample_2d_streaming(
319
324
 
320
325
  result = (
321
326
  data.with_columns([x_bin_expr, y_bin_expr])
322
- .sort(intensity_column, descending=True)
327
+ .sort(intensity_column, descending=descending)
323
328
  .group_by(["_x_bin", "_y_bin"])
324
329
  .head(points_per_bin)
325
330
  .drop(["_x_bin", "_y_bin"])
@@ -349,7 +354,7 @@ def downsample_2d_streaming(
349
354
  .alias("_y_bin"),
350
355
  ]
351
356
  )
352
- .sort(intensity_column, descending=True)
357
+ .sort(intensity_column, descending=descending)
353
358
  .group_by(["_x_bin", "_y_bin"])
354
359
  .head(points_per_bin)
355
360
  .drop(["_x_bin", "_y_bin"])
@@ -271,7 +271,16 @@ def _prepare_vue_data_cached(
271
271
  data_hash = _hash_data(vue_data)
272
272
  return vue_data, data_hash
273
273
  else:
274
- # No dynamic annotations - return cached as-is
274
+ # No dynamic annotations - ensure _plotConfig is present
275
+ # When annotations are cleared, Vue needs _plotConfig with null columns
276
+ # to stop showing stale annotations (Vue merge only updates keys present)
277
+ if hasattr(component, "_build_plot_config"):
278
+ vue_data = dict(cached_data)
279
+ vue_data["_plotConfig"] = component._build_plot_config(
280
+ getattr(component, "_highlight_column", None),
281
+ getattr(component, "_annotation_column", None),
282
+ )
283
+ return vue_data, cached_hash
275
284
  return cached_data, cached_hash
276
285
 
277
286
  # Cache miss - compute data
@@ -405,9 +414,10 @@ def _validate_interactivity_selections(
405
414
 
406
415
  data = data.filter(pl.col(column) == selected_value)
407
416
 
408
- # Validate each interactivity selection against filtered data
417
+ # Collect all checks to validate
409
418
  state_changed = False
410
419
  schema = data.collect_schema()
420
+ checks_to_validate: list[tuple[str, str, Any]] = []
411
421
 
412
422
  for identifier, column in interactivity.items():
413
423
  selected_value = state.get(identifier)
@@ -423,19 +433,30 @@ def _validate_interactivity_selections(
423
433
  if isinstance(selected_value, float) and selected_value.is_integer():
424
434
  selected_value = int(selected_value)
425
435
 
426
- # Check if value exists in filtered data (efficient: only fetch 1 row)
427
- exists = (
428
- data.filter(pl.col(column) == selected_value).head(1).collect().height > 0
429
- )
436
+ checks_to_validate.append((identifier, column, selected_value))
437
+
438
+ # Early return if nothing to validate
439
+ if not checks_to_validate:
440
+ return False
441
+
442
+ # Build single query with all existence checks
443
+ existence_exprs = [
444
+ (pl.col(column) == value).any().alias(identifier)
445
+ for identifier, column, value in checks_to_validate
446
+ ]
447
+
448
+ # Execute ONE query instead of N
449
+ results = data.select(existence_exprs).collect().row(0, named=True)
430
450
 
431
- if not exists:
432
- # Selection is invalid - clear it
451
+ # Process results and clear invalid selections
452
+ for identifier, _column, value in checks_to_validate:
453
+ if not results[identifier]:
433
454
  state_manager.set_selection(identifier, None)
434
455
  state_changed = True
435
456
  if _DEBUG_STATE_SYNC:
436
457
  _logger.warning(
437
458
  f"[Bridge:{component._cache_id}] Cleared invalid selection: "
438
- f"{identifier}={selected_value} not in filtered data"
459
+ f"{identifier}={value} not in filtered data"
439
460
  )
440
461
 
441
462
  return state_changed
@@ -764,6 +785,7 @@ def render_component(
764
785
  if st.session_state.get(ann_hash_key) is not None:
765
786
  annotations_changed = True
766
787
  st.session_state[ann_hash_key] = None
788
+ _store_component_annotations(key, None)
767
789
 
768
790
  if annotations_changed:
769
791
  state_changed = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openms-insight
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: Interactive visualization components for mass spectrometry data in Streamlit
5
5
  Project-URL: Homepage, https://github.com/t0mdavid-m/OpenMS-Insight
6
6
  Project-URL: Documentation, https://github.com/t0mdavid-m/OpenMS-Insight#readme
@@ -243,7 +243,9 @@ Heatmap(
243
243
  - `min_points`: Target size for downsampling (default: 20000)
244
244
  - `x_bins`, `y_bins`: Grid resolution for spatial binning
245
245
  - `colorscale`: Plotly colorscale name (default: 'Portland')
246
+ - `reversescale`: Invert colorscale direction (default: False)
246
247
  - `log_scale`: Use log10 color mapping (default: True). Set to False for linear.
248
+ - `low_values_on_top`: Prioritize low values during downsampling and display them on top (default: False). Use for scores where lower = better (e.g., e-values, PEP, q-values).
247
249
  - `intensity_label`: Custom colorbar label (default: 'Intensity')
248
250
 
249
251
  **Linear scale example:**
@@ -260,6 +262,24 @@ Heatmap(
260
262
  )
261
263
  ```
262
264
 
265
+ **Low values on top (PSM scores):**
266
+ For identification results where lower scores indicate better matches (e.g., e-values, PEP, q-values), use `low_values_on_top=True` to preserve low-scoring points during downsampling and display them on top of high-scoring points:
267
+
268
+ ```python
269
+ Heatmap(
270
+ cache_id="psm_evalue",
271
+ data_path="psm_data.parquet",
272
+ x_column='rt',
273
+ y_column='mz',
274
+ intensity_column='e_value',
275
+ log_scale=True, # Log scale for e-values
276
+ low_values_on_top=True, # Keep/show low e-values (best hits)
277
+ reversescale=True, # Bright color = low value = best
278
+ intensity_label='E-value',
279
+ colorscale='Portland',
280
+ )
281
+ ```
282
+
263
283
  **Categorical mode:**
264
284
  Use `category_column` for discrete coloring by category instead of continuous intensity colorscale:
265
285
 
@@ -1,9 +1,9 @@
1
- openms_insight/__init__.py,sha256=b4H9k0fPVZ6jCiJ4QVSwzlUQnyElF6ZwAlDI8fdPMJE,1111
1
+ openms_insight/__init__.py,sha256=tmst1YCzXuVLQAs7Hzit5idMPmJsAqn3djpV8fVduYU,1112
2
2
  openms_insight/components/__init__.py,sha256=Lcg-D0FILta-YVgMJBlWMKLKC5G5kXOqdy9hBOENABw,233
3
- openms_insight/components/heatmap.py,sha256=psrdW4gNzZR1jAIox9YS9EHaZaTRrDHFR0t2_0APU9Y,47214
3
+ openms_insight/components/heatmap.py,sha256=Yek06-TCDformcR42VeUCu0d7WDtRQPCpNQ5kOnfv5w,48372
4
4
  openms_insight/components/lineplot.py,sha256=I-JPvDzCr3Nu8Boc1V4D8QQ1bHgTqvM6CbeoIe7zJ-s,30896
5
5
  openms_insight/components/sequenceview.py,sha256=ufvtzuU9zaAybsT3pqfqIR0ZHCl7dKJ-jk3kwJALr54,31241
6
- openms_insight/components/table.py,sha256=0LiJetxECoGLZqhkUhDXp1_92FYgGeajm4zDTWjfc1U,42146
6
+ openms_insight/components/table.py,sha256=gmgWlsd5GXV2NRkYoDmhi4asaS0pzwoW0C4yf5jI_cI,42740
7
7
  openms_insight/components/volcanoplot.py,sha256=F-cmYxJMKXVK-aYJpifp8be7nB8hkQd2kLi9DrBElD8,15155
8
8
  openms_insight/core/__init__.py,sha256=EPjKX_FFQRgO8mWHs59I-o0BiuzEMzEU1Pfu9YOfLC4,338
9
9
  openms_insight/core/base.py,sha256=h0OaubHLky8mk7Yfy3HTIimsz-DfuNRgLfotJu3pZVw,20517
@@ -12,19 +12,19 @@ openms_insight/core/registry.py,sha256=Hak80Jqhx0qa4gbd1YolNZnM6xBrS8I4U_X7zC0bQ
12
12
  openms_insight/core/state.py,sha256=CMToxxNyGnqxMccwOcn7FwABNTzjjTsgsMrJCZOZc2o,12438
13
13
  openms_insight/core/subprocess_preprocess.py,sha256=m9FbAAFy9Do1Exlh-m4Wo-LDwv6yHlEI4klz5OVwemc,3133
14
14
  openms_insight/preprocessing/__init__.py,sha256=xoGdhNVrX8Ty3ywmyaCcWAO3a6QlKceO1xxsy1C8ZTI,596
15
- openms_insight/preprocessing/compression.py,sha256=T4YbX9PUlfTfPit_kpuLZn8hYpqLYu3xtTme_CG2ymc,12241
15
+ openms_insight/preprocessing/compression.py,sha256=jHcZqjcFCKb4rRns4PY2DglQecbO0cSThB523bUvUtM,12519
16
16
  openms_insight/preprocessing/filtering.py,sha256=fkmaIXfR5hfjyWfaMYqaeybMHaZjvUZYaKCqvxPOWMQ,14152
17
17
  openms_insight/preprocessing/scatter.py,sha256=2ifCNTUKHEW9UVpv4z9c5GaLnz5zw9o1119IenzAe9s,4703
18
18
  openms_insight/rendering/__init__.py,sha256=ApHvKeh87yY4GTIEai-tCeIXpNbwOXWlmcmIwMMRZYc,198
19
- openms_insight/rendering/bridge.py,sha256=yl8QmeGnoaK2ZM4kf-XQW47o4DZ5S2lnkL0Iu7C5CjE,30331
19
+ openms_insight/rendering/bridge.py,sha256=xFM74gbyzQHFmKt61-hk2ouYZ7KAG7HHosvwmKNBY_M,31311
20
20
  openms_insight/js-component/dist/index.html,sha256=LSJ3B_YmGUrCCdZ1UaZO2p6Wqsih6nTH62Z_0uZxpD8,430
21
- openms_insight/js-component/dist/assets/index.css,sha256=wFvo7FbG132LL7uw0slXfrL_oSQ8u2RKL1DW9hD9-kk,884264
22
- openms_insight/js-component/dist/assets/index.js,sha256=aqGc3g7XLTRr7ptEgoA3XDu5oMS47yxxjUBXgansIo0,6091480
21
+ openms_insight/js-component/dist/assets/index.css,sha256=s0J7lg0ben8EwwtgSI5tDqNPmPlTI4xOy0XoKsum46Y,884264
22
+ openms_insight/js-component/dist/assets/index.js,sha256=TjpKz5QuvUcdM5-iXP4cHh49m0U4z618yJMHRpS0gEc,6092354
23
23
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.eot,sha256=CxgxBNL8XyYZbnc8d72vLgVQn9QlnS0V7O3Kebh-hPk,1307880
24
24
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.ttf,sha256=YeirpaTpgf4iz3yOi82-oAR251xiw38Bv37jM2HWhCg,1307660
25
25
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.woff,sha256=pZKKDVwvYk5G-Y2bFcL2AEU3f3xZTdeKF1kTLqO0Y-s,587984
26
26
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.woff2,sha256=Zi_vqPL4qVwYWI0hd0eJwQfGTnccvmWmmvRikcQxGvw,403216
27
- openms_insight-0.1.10.dist-info/METADATA,sha256=diBvV5CHNzaJ8RsS4b24ywYnnFFSM_TMkYkLuEpAUnc,16788
28
- openms_insight-0.1.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
29
- openms_insight-0.1.10.dist-info/licenses/LICENSE,sha256=INFF4rOMmpah7Oi14hLqu7NTOsx56KRRNChAAUcfh2E,1823
30
- openms_insight-0.1.10.dist-info/RECORD,,
27
+ openms_insight-0.1.11.dist-info/METADATA,sha256=J4jpnQDVfM73lukRzb5JLGZjqeUt-lzbfdtsnMIm8x8,17708
28
+ openms_insight-0.1.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
29
+ openms_insight-0.1.11.dist-info/licenses/LICENSE,sha256=INFF4rOMmpah7Oi14hLqu7NTOsx56KRRNChAAUcfh2E,1823
30
+ openms_insight-0.1.11.dist-info/RECORD,,