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.
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/PKG-INFO +1 -1
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/__init__.py +1 -1
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/_curves.py +1 -1
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/curves.py +2 -2
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/fitting.py +44 -20
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/peak_analysis.py +42 -6
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/smoothing.py +4 -2
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/pyproject.toml +2 -2
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/LICENSE +0 -0
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/README.md +0 -0
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/_baseline.py +0 -0
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/_smoothing.py +0 -0
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/baseline.py +0 -0
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/normalization.py +0 -0
- {funcnodes_span-0.3.0 → funcnodes_span-0.3.1}/funcnodes_span/peaks.py +0 -0
|
@@ -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="
|
|
20
|
-
description="
|
|
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
|
-
|
|
110
|
-
|
|
115
|
+
x = np.asarray(x)
|
|
116
|
+
y = np.asarray(y)
|
|
111
117
|
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
230
|
-
|
|
232
|
+
x = np.asarray(x)
|
|
233
|
+
y = np.asarray(y)
|
|
231
234
|
|
|
232
|
-
|
|
233
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|