funcnodes-span 0.3.0__tar.gz → 0.3.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funcnodes-span
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Spectral Peak ANalysis (SPAN) for funcnodes
5
5
  License: MIT
6
6
  Author: Kourosh Rezaei
@@ -7,7 +7,7 @@ from .baseline import BASELINE_NODE_SHELF as BASELINE
7
7
  from funcnodes_lmfit import NODE_SHELF as LMFIT_NODE_SHELF
8
8
  from .curves import CURVES_NODE_SHELF
9
9
 
10
- __version__ = "0.3.0"
10
+ __version__ = "0.3.1"
11
11
 
12
12
  NODE_SHELF = fn.Shelf(
13
13
  name="Spectral Analysis",
@@ -47,7 +47,7 @@ def knee_point_detection(x, y) -> Tuple[int, float, float]:
47
47
 
48
48
 
49
49
  def estimate_noise(
50
- x, y, is_baseline_region: Optional[np.ndarray] = None, std_factor=3
50
+ x, y, is_baseline_region: Optional[np.ndarray] = None, std_factor: float = 3
51
51
  ) -> float:
52
52
  """
53
53
  Estimate the noise level in a signal.
@@ -16,6 +16,6 @@ estimate_noise_node = fn.NodeDecorator(
16
16
  CURVES_NODE_SHELF = fn.Shelf(
17
17
  nodes=[knee_point_detection_node, estimate_noise_node],
18
18
  subshelves=[],
19
- name="Smoothing",
20
- description="Smoothing of the spectra",
19
+ name="Curves",
20
+ description="Analysis of curves and spectra",
21
21
  )
@@ -5,6 +5,12 @@ from lmfit.model import ModelResult
5
5
  from lmfit.models import GaussianModel
6
6
  from .peaks import PeakProperties
7
7
  from funcnodes_lmfit.model import AUTOMODELMAP
8
+ import funcnodes as fn
9
+
10
+ AutoModelEnum = fn.DataEnum("AutoModelEnum", AUTOMODELMAP)
11
+ # class AutoModelEnum(fn.DataEnum):
12
+ # GaussianModel = AUTOMODELMAP["GaussianModel"]
13
+ # LorentzianModel = AUTOMODELMAP["LorentzianModel"]
8
14
 
9
15
 
10
16
  def group_signals(
@@ -106,11 +112,11 @@ def fit_local_peak(
106
112
  Model: The fitted model for the peak.
107
113
  """
108
114
 
109
- if isinstance(model_class, str):
110
- model_class = AUTOMODELMAP[model_class]
115
+ x = np.asarray(x)
116
+ y = np.asarray(y)
111
117
 
112
- if isinstance(incomplete_peak_model_class, str):
113
- incomplete_peak_model_class = AUTOMODELMAP[incomplete_peak_model_class]
118
+ model_class = AutoModelEnum.v(model_class)
119
+ incomplete_peak_model_class = AutoModelEnum.v(incomplete_peak_model_class)
114
120
 
115
121
  pf = f"p{peak.id}_"
116
122
  model = model_class(prefix=pf) # Initialize the model
@@ -129,11 +135,8 @@ def fit_local_peak(
129
135
  if peak.yfull is None:
130
136
  peak.yfull = y
131
137
 
132
- if isinstance(model_class, str):
133
- model_class = AUTOMODELMAP[model_class]
134
-
135
- if isinstance(incomplete_peak_model_class, str):
136
- incomplete_peak_model_class = AUTOMODELMAP[incomplete_peak_model_class]
138
+ model_class = AutoModelEnum.v(model_class)
139
+ incomplete_peak_model_class = AutoModelEnum.v(incomplete_peak_model_class)
137
140
 
138
141
  if np.abs(yf[-1] - yf[0]) > incomplete_threshold * (local_max - local_min):
139
142
  # Handle incomplete peak by extending the range and filling gaps
@@ -226,11 +229,11 @@ def fit_peak_group(
226
229
  Model: The fitted model for the group of peaks.
227
230
  """
228
231
 
229
- if isinstance(model_class, str):
230
- model_class = AUTOMODELMAP[model_class]
232
+ x = np.asarray(x)
233
+ y = np.asarray(y)
231
234
 
232
- if isinstance(incomplete_peak_model_class, str):
233
- incomplete_peak_model_class = AUTOMODELMAP[incomplete_peak_model_class]
235
+ model_class = AutoModelEnum.v(model_class)
236
+ incomplete_peak_model_class = AutoModelEnum.v(incomplete_peak_model_class)
234
237
 
235
238
  groupmodel = None
236
239
  most_left = min([p.i_index for p in peaks]) # Find the leftmost index in the group
@@ -278,12 +281,12 @@ def fit_peaks(
278
281
  peaks: List[PeakProperties],
279
282
  x: np.ndarray,
280
283
  y: np.ndarray,
281
- model_class: Type[Model] = GaussianModel,
284
+ model_class: AutoModelEnum = AutoModelEnum.GaussianModel,
282
285
  filter_negatives: bool = True,
283
286
  baseline_factor: float = 0.001,
284
287
  incomplete_threshold: float = 0.8,
285
288
  incomplete_x_extend: float = 2.0,
286
- incomplete_peak_model_class: Type[Model] = GaussianModel,
289
+ incomplete_peak_model_class: AutoModelEnum = AutoModelEnum.GaussianModel,
287
290
  iter_cb=None,
288
291
  ) -> Tuple[List[PeakProperties], Model, ModelResult]:
289
292
  """
@@ -337,11 +340,10 @@ def fit_peaks(
337
340
  Returns:
338
341
  Model: The fitted model for the global signal.
339
342
  """
340
- if isinstance(model_class, str):
341
- model_class = AUTOMODELMAP[model_class]
342
-
343
- if isinstance(incomplete_peak_model_class, str):
344
- incomplete_peak_model_class = AUTOMODELMAP[incomplete_peak_model_class]
343
+ model_class = AutoModelEnum.v(model_class)
344
+ incomplete_peak_model_class = AutoModelEnum.v(incomplete_peak_model_class)
345
+ x = np.asarray(x)
346
+ y = np.asarray(y)
345
347
 
346
348
  if len(peaks) == 0:
347
349
  raise ValueError("No peaks provided for fitting.")
@@ -381,3 +383,25 @@ def fit_peaks(
381
383
  peak.model = get_submodel_by_prefix(global_model, f"p{peak.id}_")
382
384
 
383
385
  return peaks, global_model, global_fit
386
+
387
+
388
+ def peaks_from_fitted(fitted_peaks: List[PeakProperties]) -> List[PeakProperties]:
389
+ peaks = []
390
+ from .peak_analysis import peak_finder # noaq Avoid circular import
391
+
392
+ for p in fitted_peaks:
393
+ model = p.model
394
+ mparams = model.make_params()
395
+ y = model.eval(x=p.xfull, params=mparams)
396
+ pf: List[PeakProperties] = peak_finder.o_func(y=y, x=p.xfull)[0]
397
+ if len(pf) != 1:
398
+ raise ValueError("Expected one peak")
399
+ pf = pf[0]
400
+ pf._id = f"fitted_{p.id}"
401
+ pf.model = model
402
+ p.add_serializable_property("model", str(model))
403
+ for k, v in mparams.items():
404
+ p.add_serializable_property(k.replace(model.prefix, ""), v.value)
405
+ peaks.append(pf)
406
+
407
+ return peaks
@@ -12,7 +12,13 @@ import plotly.graph_objs as go
12
12
  from .normalization import density_normalization
13
13
  from .peaks import PeakProperties
14
14
 
15
- from .fitting import fit_peaks, AUTOMODELMAP, fit_local_peak
15
+ from .fitting import fit_peaks, AUTOMODELMAP, fit_local_peak, peaks_from_fitted
16
+
17
+ # make enum from AUTOMODELMAP
18
+ AutoModelEnum = fn.DataEnum(
19
+ "AutoModelEnum",
20
+ AUTOMODELMAP,
21
+ )
16
22
 
17
23
 
18
24
  @NodeDecorator(
@@ -547,18 +553,47 @@ fit_peak_node = fn.NodeDecorator(
547
553
  id="span.peaks.fit_peak",
548
554
  name="Fit Peak",
549
555
  outputs=[{"name": "fitted_peak"}, {"name": "model"}, {"name": "fit_result"}],
550
- # separate_process=True,
556
+ separate_process=True,
551
557
  )(fit_local_peak)
552
558
 
559
+
553
560
  fit_peaks_node = fn.NodeDecorator(
554
561
  id="span.peaks.fit_peaks",
555
562
  name="Fit Peaks",
556
563
  outputs=[{"name": "fitted_peaks"}, {"name": "model"}, {"name": "fit_results"}],
557
- default_io_options={
558
- "modelname": {"value_options": {"options": list(AUTOMODELMAP.keys())}},
559
- },
560
- separate_process=True,
564
+ # separate_process=True,
561
565
  )(fit_peaks)
566
+ # @fn.controlled_wrapper(fit_peaks, wrapper_attribute="__fnwrapped__")
567
+ # def fit_peaks_node(
568
+ # peaks: List[PeakProperties],
569
+ # x: np.ndarray,
570
+ # y: np.ndarray,
571
+ # model_class: AutoModelEnum = AutoModelEnum.v("GaussianModel"),
572
+ # filter_negatives: bool = True,
573
+ # baseline_factor: float = 0.001,
574
+ # incomplete_threshold: float = 0.8,
575
+ # incomplete_x_extend: float = 2.0,
576
+ # incomplete_peak_model_class: AutoModelEnum = AutoModelEnum.v("GaussianModel"),
577
+ # ) -> Tuple[List[PeakProperties], Model, ModelResult]:
578
+ # return fit_peaks(
579
+ # peaks,
580
+ # x,
581
+ # y,
582
+ # model_class=AutoModelEnum.v(model_class),
583
+ # filter_negatives=filter_negatives,
584
+ # baseline_factor=baseline_factor,
585
+ # incomplete_threshold=incomplete_threshold,
586
+ # incomplete_x_extend=incomplete_x_extend,
587
+ # incomplete_peak_model_class=AutoModelEnum.v(incomplete_peak_model_class),
588
+ # )
589
+
590
+
591
+ peaks_from_fitted_node = fn.NodeDecorator(
592
+ id="span.peaks.peaks_from_fitted",
593
+ name="Peaks from fitted",
594
+ description="converts the fit data in peaks to regular peaks",
595
+ outputs=[{"name": "peaks"}],
596
+ )(peaks_from_fitted)
562
597
 
563
598
  PEAKS_NODE_SHELF = Shelf(
564
599
  nodes=[
@@ -568,6 +603,7 @@ PEAKS_NODE_SHELF = Shelf(
568
603
  plot_peaks,
569
604
  fit_peaks_node,
570
605
  fit_peak_node,
606
+ peaks_from_fitted_node,
571
607
  plot_fitted_peaks,
572
608
  plot_peak,
573
609
  ],
@@ -93,12 +93,14 @@ def _smooth(
93
93
 
94
94
  if mode not in _SMOOTHING_MAPPER.keys():
95
95
  raise ValueError(f"Unsupported smoothing mode: {mode}")
96
-
96
+ y = np.asarray(y)
97
97
  if x is not None:
98
+ x = np.asarray(x)
98
99
  med_xdiff = np.nanmedian(np.diff(x))
99
100
  window = window / med_xdiff
100
101
  window = int(window)
101
-
102
+ if window == 0:
103
+ return y.copy()
102
104
  return _SMOOTHING_MAPPER[mode](y, window)
103
105
 
104
106
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "funcnodes-span"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "Spectral Peak ANalysis (SPAN) for funcnodes"
5
5
  authors = ["Kourosh Rezaei <kouroshrezaei90@gmail.com>"]
6
6
  readme = "README.md"
@@ -28,7 +28,7 @@ funcnodes-lmfit = "*"
28
28
  [tool.poetry.group.dev.dependencies]
29
29
  pytest = "*"
30
30
  pre-commit = "*"
31
- funcnodes-module = "*"
31
+ funcnodes-module = "^0.1.19"
32
32
  pooch = "*"
33
33
 
34
34
 
File without changes
File without changes