funcnodes-span 0.2.0__tar.gz → 0.2.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.2.0 → funcnodes_span-0.2.1}/PKG-INFO +2 -2
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/funcnodes_span/__init__.py +1 -1
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/funcnodes_span/baseline.py +23 -55
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/funcnodes_span/normalization.py +10 -12
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/funcnodes_span/peak_analysis.py +50 -38
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/funcnodes_span/smoothing.py +57 -52
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/pyproject.toml +2 -2
- {funcnodes_span-0.2.0 → funcnodes_span-0.2.1}/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: funcnodes-span
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary:
|
|
5
5
|
Home-page: https://linkdlab.de/
|
|
6
6
|
Author: Kourosh Rezaei
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Requires-Dist: Numba
|
|
13
13
|
Requires-Dist: funcnodes
|
|
14
|
-
Requires-Dist: funcnodes-core (>=0.1.
|
|
14
|
+
Requires-Dist: funcnodes-core (>=0.1.4)
|
|
15
15
|
Requires-Dist: funcnodes_numpy
|
|
16
16
|
Requires-Dist: funcnodes_pandas
|
|
17
17
|
Requires-Dist: funcnodes_plotly
|
|
@@ -5,7 +5,7 @@ from .smoothing import SMOOTH_NODE_SHELF as SMOOTH
|
|
|
5
5
|
from .peak_analysis import PEAKS_NODE_SHELF as PEAK
|
|
6
6
|
from .baseline import BASELINE_NODE_SHELF as BASELINE
|
|
7
7
|
|
|
8
|
-
__version__ = "0.2.
|
|
8
|
+
__version__ = "0.2.1"
|
|
9
9
|
|
|
10
10
|
NODE_SHELF = fn.Shelf(
|
|
11
11
|
name="Spectral Analysis",
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
+
import funcnodes as fn
|
|
1
2
|
from funcnodes import NodeDecorator, Shelf
|
|
2
3
|
from exposedfunctionality import controlled_wrapper
|
|
3
4
|
import numpy as np
|
|
4
|
-
from enum import Enum
|
|
5
5
|
from typing import Optional, Tuple, Union, List
|
|
6
6
|
import pybaselines
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class CostFunction(
|
|
9
|
+
class CostFunction(fn.DataEnum):
|
|
10
10
|
asymmetric_indec = "asymmetric_indec"
|
|
11
11
|
asymmetric_truncated_quadratic = "asymmetric_truncated_quadratic"
|
|
12
12
|
asymmetric_huber = "asymmetric_huber"
|
|
13
13
|
|
|
14
|
-
@classmethod
|
|
15
|
-
def default(cls):
|
|
16
|
-
return cls.asymmetric_indec.value
|
|
17
|
-
|
|
18
14
|
|
|
19
15
|
@NodeDecorator(
|
|
20
16
|
"pybaselines.polynomial.goldindec",
|
|
@@ -33,7 +29,7 @@ def _goldindec(
|
|
|
33
29
|
tol: float = 0.001,
|
|
34
30
|
max_iter: int = 250,
|
|
35
31
|
weights: Optional[np.ndarray] = None,
|
|
36
|
-
cost_function: CostFunction = CostFunction.
|
|
32
|
+
cost_function: CostFunction = CostFunction.asymmetric_indec,
|
|
37
33
|
peak_ratio: float = 0.5,
|
|
38
34
|
alpha_factor: float = 0.99,
|
|
39
35
|
tol_2: float = 0.001,
|
|
@@ -41,8 +37,7 @@ def _goldindec(
|
|
|
41
37
|
max_iter_2: int = 100,
|
|
42
38
|
return_coef: bool = False,
|
|
43
39
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
44
|
-
|
|
45
|
-
cost_function = cost_function.value
|
|
40
|
+
cost_function = CostFunction.v(cost_function)
|
|
46
41
|
baseline, params = pybaselines.polynomial.goldindec(
|
|
47
42
|
data,
|
|
48
43
|
x_data=x_data,
|
|
@@ -186,7 +181,7 @@ def _modpoly(
|
|
|
186
181
|
return baseline_corrected, baseline, params
|
|
187
182
|
|
|
188
183
|
|
|
189
|
-
class PenalizedPolyCostFunction(
|
|
184
|
+
class PenalizedPolyCostFunction(fn.DataEnum):
|
|
190
185
|
asymmetric_truncated_quadratic = "asymmetric_truncated_quadratic"
|
|
191
186
|
symmetric_truncated_quadratic = "symmetric_truncated_quadratic"
|
|
192
187
|
asymmetric_huber = "asymmetric_huber"
|
|
@@ -194,10 +189,6 @@ class PenalizedPolyCostFunction(Enum):
|
|
|
194
189
|
asymmetric_indec = "asymmetric_indec"
|
|
195
190
|
symmetric_indec = "symmetric_indec"
|
|
196
191
|
|
|
197
|
-
@classmethod
|
|
198
|
-
def default(cls):
|
|
199
|
-
return cls.asymmetric_truncated_quadratic.value
|
|
200
|
-
|
|
201
192
|
|
|
202
193
|
@NodeDecorator(
|
|
203
194
|
"pybaselines.polynomial.penalized_poly",
|
|
@@ -218,13 +209,12 @@ def _penalized_poly(
|
|
|
218
209
|
max_iter: int = 250,
|
|
219
210
|
tol: float = 1e-3,
|
|
220
211
|
weights: Optional[np.ndarray] = None,
|
|
221
|
-
cost_function: PenalizedPolyCostFunction = PenalizedPolyCostFunction.
|
|
212
|
+
cost_function: PenalizedPolyCostFunction = PenalizedPolyCostFunction.asymmetric_truncated_quadratic,
|
|
222
213
|
threshold: Optional[float] = None,
|
|
223
214
|
alpha_factor: float = 0.99,
|
|
224
215
|
return_coef: bool = False,
|
|
225
216
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
226
|
-
|
|
227
|
-
cost_function = cost_function.value
|
|
217
|
+
cost_function = PenalizedPolyCostFunction.v(cost_function)
|
|
228
218
|
baseline, params = pybaselines.polynomial.penalized_poly(
|
|
229
219
|
data,
|
|
230
220
|
x_data=x_data,
|
|
@@ -1540,15 +1530,11 @@ def _noise_median(
|
|
|
1540
1530
|
return baseline_corrected, baseline, params
|
|
1541
1531
|
|
|
1542
1532
|
|
|
1543
|
-
class Side(
|
|
1533
|
+
class Side(fn.DataEnum):
|
|
1544
1534
|
both = "both"
|
|
1545
1535
|
left = "left"
|
|
1546
1536
|
right = "right"
|
|
1547
1537
|
|
|
1548
|
-
@classmethod
|
|
1549
|
-
def default(cls):
|
|
1550
|
-
return cls.both.value
|
|
1551
|
-
|
|
1552
1538
|
|
|
1553
1539
|
@NodeDecorator(
|
|
1554
1540
|
"pybaselines.smooth.ria",
|
|
@@ -1566,13 +1552,12 @@ def _ria(
|
|
|
1566
1552
|
half_window: Optional[int] = None,
|
|
1567
1553
|
max_iter: int = 500,
|
|
1568
1554
|
tol: float = 0.01,
|
|
1569
|
-
side: Side = Side.
|
|
1555
|
+
side: Side = Side.both,
|
|
1570
1556
|
width_scale: float = 0.1,
|
|
1571
1557
|
height_scale: float = 1.0,
|
|
1572
1558
|
sigma_scale: float = 1.0 / 12.0,
|
|
1573
1559
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1574
|
-
|
|
1575
|
-
side = side.value
|
|
1560
|
+
side = Side.v(side)
|
|
1576
1561
|
baseline, params = pybaselines.smooth.ria(
|
|
1577
1562
|
data,
|
|
1578
1563
|
x_data=x_data,
|
|
@@ -1936,14 +1921,10 @@ CLASSIFICATION_NODE_SHELF = Shelf(
|
|
|
1936
1921
|
)
|
|
1937
1922
|
|
|
1938
1923
|
|
|
1939
|
-
class Method(
|
|
1924
|
+
class Method(fn.DataEnum):
|
|
1940
1925
|
modpoly = "modpoly"
|
|
1941
1926
|
imodpoly = "imodpoly"
|
|
1942
1927
|
|
|
1943
|
-
@classmethod
|
|
1944
|
-
def default(cls):
|
|
1945
|
-
return cls.modpoly.value
|
|
1946
|
-
|
|
1947
1928
|
|
|
1948
1929
|
@NodeDecorator(
|
|
1949
1930
|
"pybaselines.optimizers.adaptive_minmax",
|
|
@@ -1961,14 +1942,13 @@ def _adaptive_minmax(
|
|
|
1961
1942
|
data: np.ndarray,
|
|
1962
1943
|
x_data: Optional[np.ndarray] = None,
|
|
1963
1944
|
poly_order: Optional[Union[int, List[int]]] = None,
|
|
1964
|
-
method: Method = Method.
|
|
1945
|
+
method: Method = Method.modpoly,
|
|
1965
1946
|
constrained_fraction: Union[float, List[float]] = 0.01,
|
|
1966
1947
|
constrained_weight: Union[float, List[float]] = 100000.0,
|
|
1967
1948
|
estimation_poly_order: int = 2,
|
|
1968
1949
|
weights: Optional[np.ndarray] = None,
|
|
1969
1950
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1970
|
-
|
|
1971
|
-
method = method.value
|
|
1951
|
+
method = Method.v(method)
|
|
1972
1952
|
baseline, params = pybaselines.optimizers.adaptive_minmax(
|
|
1973
1953
|
data,
|
|
1974
1954
|
x_data=x_data,
|
|
@@ -1983,7 +1963,7 @@ def _adaptive_minmax(
|
|
|
1983
1963
|
return baseline_corrected, baseline, params
|
|
1984
1964
|
|
|
1985
1965
|
|
|
1986
|
-
class MethodColab(
|
|
1966
|
+
class MethodColab(fn.DataEnum):
|
|
1987
1967
|
airpls = "airpls"
|
|
1988
1968
|
arpls = "arpls"
|
|
1989
1969
|
asls = "asls"
|
|
@@ -1994,10 +1974,6 @@ class MethodColab(Enum):
|
|
|
1994
1974
|
iasls = "iasls"
|
|
1995
1975
|
psalsa = "psalsa"
|
|
1996
1976
|
|
|
1997
|
-
@classmethod
|
|
1998
|
-
def default(cls):
|
|
1999
|
-
return cls.asls.value
|
|
2000
|
-
|
|
2001
1977
|
|
|
2002
1978
|
@NodeDecorator(
|
|
2003
1979
|
"pybaselines.optimizers.collab_pls",
|
|
@@ -2015,10 +1991,9 @@ def _collab_pls(
|
|
|
2015
1991
|
data: np.ndarray,
|
|
2016
1992
|
x_data: Optional[np.ndarray] = None,
|
|
2017
1993
|
average_dataset: bool = True,
|
|
2018
|
-
method: MethodColab = MethodColab.
|
|
1994
|
+
method: MethodColab = MethodColab.asls,
|
|
2019
1995
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
2020
|
-
|
|
2021
|
-
method = method.value
|
|
1996
|
+
method = MethodColab.v(method)
|
|
2022
1997
|
baseline, params = pybaselines.optimizers.collab_pls(
|
|
2023
1998
|
data,
|
|
2024
1999
|
x_data=x_data,
|
|
@@ -2029,7 +2004,7 @@ def _collab_pls(
|
|
|
2029
2004
|
return baseline_corrected, baseline, params
|
|
2030
2005
|
|
|
2031
2006
|
|
|
2032
|
-
class MethodAll(
|
|
2007
|
+
class MethodAll(fn.DataEnum):
|
|
2033
2008
|
goldindec = "goldindec"
|
|
2034
2009
|
imodpoly = "imodpoly"
|
|
2035
2010
|
loess = "loess"
|
|
@@ -2076,10 +2051,6 @@ class MethodAll(Enum):
|
|
|
2076
2051
|
rubberband = "rubberband"
|
|
2077
2052
|
std_distribution = "std_distribution"
|
|
2078
2053
|
|
|
2079
|
-
@classmethod
|
|
2080
|
-
def default(cls):
|
|
2081
|
-
return cls.asls.value
|
|
2082
|
-
|
|
2083
2054
|
|
|
2084
2055
|
@NodeDecorator(
|
|
2085
2056
|
"pybaselines.optimizers.custom_bc",
|
|
@@ -2097,10 +2068,9 @@ def _custom_bc(
|
|
|
2097
2068
|
sampling: Union[int, np.ndarray] = 1,
|
|
2098
2069
|
lam: Optional[float] = None,
|
|
2099
2070
|
diff_order: int = 2,
|
|
2100
|
-
method: MethodAll = MethodAll.
|
|
2071
|
+
method: MethodAll = MethodAll.asls,
|
|
2101
2072
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
2102
|
-
|
|
2103
|
-
method = method.value
|
|
2073
|
+
method = MethodAll.v(method)
|
|
2104
2074
|
baseline, params = pybaselines.optimizers.custom_bc(
|
|
2105
2075
|
data,
|
|
2106
2076
|
x_data=x_data,
|
|
@@ -2128,19 +2098,17 @@ def _custom_bc(
|
|
|
2128
2098
|
def _optimize_extended_range(
|
|
2129
2099
|
data: np.ndarray,
|
|
2130
2100
|
x_data: Optional[np.ndarray] = None,
|
|
2131
|
-
side: Side = Side.
|
|
2101
|
+
side: Side = Side.both,
|
|
2132
2102
|
width_scale: float = 0.1,
|
|
2133
2103
|
height_scale: float = 1.0,
|
|
2134
2104
|
sigma_scale: float = 1.0 / 12.0,
|
|
2135
2105
|
min_value: float = 2.0,
|
|
2136
2106
|
max_value: float = 8.0,
|
|
2137
2107
|
step: int = 1,
|
|
2138
|
-
method: MethodAll = MethodAll.
|
|
2108
|
+
method: MethodAll = MethodAll.asls,
|
|
2139
2109
|
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
if isinstance(side, Side):
|
|
2143
|
-
side = side.value
|
|
2110
|
+
method = MethodAll.v(method)
|
|
2111
|
+
side = Side.v(side)
|
|
2144
2112
|
baseline, params = pybaselines.optimizers.optimize_extended_range(
|
|
2145
2113
|
data,
|
|
2146
2114
|
x_data=x_data,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from funcnodes import NodeDecorator, Shelf
|
|
2
2
|
import numpy as np
|
|
3
|
-
from enum import Enum
|
|
4
3
|
|
|
4
|
+
import funcnodes as fn
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
class NormMode(fn.DataEnum):
|
|
7
8
|
ZERO_ONE = "zero_one"
|
|
8
9
|
MINUS_ONE_ONE = "minus_one_one"
|
|
9
10
|
SUM_ABS = "sum_abs"
|
|
@@ -12,14 +13,9 @@ class NormMode(Enum):
|
|
|
12
13
|
MEAN_STD = "mean_std"
|
|
13
14
|
MAX = "max"
|
|
14
15
|
|
|
15
|
-
@classmethod
|
|
16
|
-
def default(cls) -> "NormMode":
|
|
17
|
-
"""Returns the default normalization mode."""
|
|
18
|
-
return cls.ZERO_ONE.value
|
|
19
|
-
|
|
20
16
|
|
|
21
17
|
@NodeDecorator(id="span.basics.norm", name="Normalization node")
|
|
22
|
-
def _norm(array: np.ndarray, mode: NormMode = NormMode.
|
|
18
|
+
def _norm(array: np.ndarray, mode: NormMode = NormMode.ZERO_ONE) -> np.ndarray:
|
|
23
19
|
# """
|
|
24
20
|
# Apply different normalizations to the array.
|
|
25
21
|
|
|
@@ -33,21 +29,23 @@ def _norm(array: np.ndarray, mode: NormMode = NormMode.default()) -> np.ndarray:
|
|
|
33
29
|
# Raises:
|
|
34
30
|
# ValueError: If an unsupported normalization mode is provided.
|
|
35
31
|
# """
|
|
36
|
-
|
|
37
|
-
mode = mode.value
|
|
32
|
+
mode = NormMode.v(mode)
|
|
38
33
|
normalization_methods = {
|
|
39
34
|
NormMode.ZERO_ONE.value: lambda x: (x - np.amin(x)) / (np.amax(x) - np.amin(x)),
|
|
40
|
-
NormMode.MINUS_ONE_ONE.value: lambda x: 2
|
|
35
|
+
NormMode.MINUS_ONE_ONE.value: lambda x: 2
|
|
36
|
+
* ((x - np.amin(x)) / (np.amax(x) - np.amin(x)))
|
|
37
|
+
- 1,
|
|
41
38
|
NormMode.SUM_ABS.value: lambda x: x / np.abs(x).sum(),
|
|
42
39
|
NormMode.SUM.value: lambda x: x / x.sum(),
|
|
43
40
|
NormMode.EUCLIDEAN.value: lambda x: x / np.sqrt((x**2).sum()),
|
|
44
41
|
NormMode.MEAN_STD.value: lambda x: (x - x.mean()) / x.std(),
|
|
45
|
-
NormMode.MAX.value: lambda x: x / x.max()
|
|
42
|
+
NormMode.MAX.value: lambda x: x / x.max(),
|
|
46
43
|
}
|
|
47
44
|
if mode not in normalization_methods.keys():
|
|
48
45
|
raise ValueError(f"Unsupported normalization mode: {mode}")
|
|
49
46
|
return normalization_methods[mode](array)
|
|
50
47
|
|
|
48
|
+
|
|
51
49
|
NORM_NODE_SHELF = Shelf(
|
|
52
50
|
nodes=[_norm],
|
|
53
51
|
subshelves=[],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from funcnodes import NodeDecorator, Shelf
|
|
2
|
+
import funcnodes as fn
|
|
2
3
|
import numpy as np
|
|
3
|
-
from enum import Enum
|
|
4
4
|
from exposedfunctionality import controlled_wrapper
|
|
5
|
-
from typing import Optional, List, Tuple
|
|
5
|
+
from typing import Optional, List, Tuple, Dict
|
|
6
6
|
from scipy.signal import find_peaks
|
|
7
7
|
from scipy.stats import norm
|
|
8
8
|
from scipy import signal, interpolate
|
|
@@ -15,6 +15,14 @@ import re
|
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
@dataclass
|
|
19
|
+
class FittinInfo:
|
|
20
|
+
model_name: str
|
|
21
|
+
best_values: dict
|
|
22
|
+
data: np.ndarray
|
|
23
|
+
userkws: dict
|
|
24
|
+
|
|
25
|
+
|
|
18
26
|
@dataclass
|
|
19
27
|
class PeakProperties:
|
|
20
28
|
id: str
|
|
@@ -35,8 +43,8 @@ class PeakProperties:
|
|
|
35
43
|
width: float
|
|
36
44
|
_is_fitted: bool = False
|
|
37
45
|
_is_force_fitted: bool = False
|
|
38
|
-
fitting_data: Optional[
|
|
39
|
-
fitting_info: Optional[
|
|
46
|
+
fitting_data: Optional[Dict[str, np.ndarray]] = None
|
|
47
|
+
fitting_info: Optional[FittinInfo] = None
|
|
40
48
|
|
|
41
49
|
|
|
42
50
|
def compute_peak_properties(
|
|
@@ -46,25 +54,27 @@ def compute_peak_properties(
|
|
|
46
54
|
peak_nr: int,
|
|
47
55
|
is_fitted: bool = False,
|
|
48
56
|
is_force_fitted: bool = False,
|
|
49
|
-
fitting_data: Optional[
|
|
50
|
-
fitting_info: Optional[
|
|
57
|
+
fitting_data: Optional[Dict[str, np.ndarray]] = None,
|
|
58
|
+
fitting_info: Optional[FittinInfo] = None,
|
|
51
59
|
) -> PeakProperties:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
"""
|
|
61
|
+
Compute various properties of a given peak.
|
|
62
|
+
|
|
63
|
+
Parameters:
|
|
64
|
+
- x_array: np.ndarray - The array of x-values (e.g., time or wavelength).
|
|
65
|
+
- y_array: np.ndarray - The array of y-values (e.g., intensity).
|
|
66
|
+
- peak_indices: List[int] - A list containing the start index, peak index, and end index of the peak.
|
|
67
|
+
- peak_nr: int - The identifier number of the peak.
|
|
68
|
+
- is_fitted: bool = False - A flag indicating whether the peak is fitted or not.
|
|
69
|
+
- is_force_fitted: bool = False - A flag indicating whether the peak is forced fitted or not.
|
|
70
|
+
- fitting_data: Optional[Dict[str, np.ndarray]] = None - A dictionary containing the fitting
|
|
71
|
+
data if the peak is fitted.
|
|
72
|
+
- fitting_info: Optional[FittinInfo] = None - A dictionary containing the fitting information
|
|
73
|
+
if the peak is fitted.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
- peak_properties: PeakProperties - A dictionary containing various properties of the peak.
|
|
77
|
+
"""
|
|
68
78
|
|
|
69
79
|
i_index, index, f_index = peak_indices
|
|
70
80
|
|
|
@@ -198,7 +208,7 @@ def peak_finder(
|
|
|
198
208
|
wlen: Optional[int] = None,
|
|
199
209
|
rel_height: float = 0.5,
|
|
200
210
|
plateau_size: Optional[int] = None,
|
|
201
|
-
) ->
|
|
211
|
+
) -> List[PeakProperties]:
|
|
202
212
|
peak_lst = []
|
|
203
213
|
x_array = np.array(x_array, dtype=float)
|
|
204
214
|
y_array = np.array(y_array, dtype=float)
|
|
@@ -331,7 +341,7 @@ def interpolation_1d(
|
|
|
331
341
|
return x_interpolated, y_interpolated
|
|
332
342
|
|
|
333
343
|
|
|
334
|
-
class FittingModel(
|
|
344
|
+
class FittingModel(fn.DataEnum):
|
|
335
345
|
ComplexConstant = "Complex Constant"
|
|
336
346
|
Gaussian = "Gaussian"
|
|
337
347
|
Gaussian2D = "Gaussian-2D"
|
|
@@ -356,12 +366,8 @@ class FittingModel(Enum):
|
|
|
356
366
|
Rectangle = "Rectangle"
|
|
357
367
|
Expression = "Expression"
|
|
358
368
|
|
|
359
|
-
@classmethod
|
|
360
|
-
def default(cls):
|
|
361
|
-
return cls.Gaussian.value
|
|
362
369
|
|
|
363
|
-
|
|
364
|
-
class BaselineModel(Enum):
|
|
370
|
+
class BaselineModel(fn.DataEnum):
|
|
365
371
|
# Polynomial = "Polynomial"
|
|
366
372
|
Linear = "Linear"
|
|
367
373
|
Spline = "Spline"
|
|
@@ -374,13 +380,13 @@ class BaselineModel(Enum):
|
|
|
374
380
|
return cls.Exponential.value
|
|
375
381
|
|
|
376
382
|
|
|
377
|
-
@NodeDecorator(id="span.basics.fit", name="Fit 1D")
|
|
383
|
+
@NodeDecorator(id="span.basics.fit", name="Fit 1D", separate_process=True)
|
|
378
384
|
def fit_1D(
|
|
379
385
|
x_array: np.ndarray,
|
|
380
386
|
y_array: np.ndarray,
|
|
381
387
|
basic_peaks: List[PeakProperties],
|
|
382
|
-
main_model: FittingModel = FittingModel.
|
|
383
|
-
baseline_model: BaselineModel = BaselineModel.
|
|
388
|
+
main_model: FittingModel = FittingModel.Gaussian,
|
|
389
|
+
baseline_model: BaselineModel = BaselineModel.Exponential,
|
|
384
390
|
) -> List[PeakProperties]:
|
|
385
391
|
# """
|
|
386
392
|
# Fit a 1D model to the given data.
|
|
@@ -405,10 +411,9 @@ def fit_1D(
|
|
|
405
411
|
x_array = np.array(x_array)
|
|
406
412
|
y_array = np.array(y_array)
|
|
407
413
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
baseline_model = baseline_model.value
|
|
414
|
+
main_model = FittingModel.v(main_model)
|
|
415
|
+
|
|
416
|
+
baseline_model = BaselineModel.v(baseline_model)
|
|
412
417
|
peaks = copy.deepcopy(basic_peaks)
|
|
413
418
|
y = y_array
|
|
414
419
|
x = x_array
|
|
@@ -473,8 +478,15 @@ def fit_1D(
|
|
|
473
478
|
|
|
474
479
|
out = f.fit(y, pars, x=x)
|
|
475
480
|
com = out.eval_components(x=x)
|
|
476
|
-
info_dict = out.__dict__
|
|
477
|
-
info_dict["model_name"] = main_model
|
|
481
|
+
# info_dict = out.__dict__
|
|
482
|
+
# info_dict["model_name"] = main_model
|
|
483
|
+
|
|
484
|
+
info_dict = FittinInfo(
|
|
485
|
+
model_name=main_model,
|
|
486
|
+
best_values=out.best_values,
|
|
487
|
+
data=out.data,
|
|
488
|
+
userkws=out.userkws,
|
|
489
|
+
)
|
|
478
490
|
|
|
479
491
|
peak_properties_list = []
|
|
480
492
|
|
|
@@ -1,28 +1,70 @@
|
|
|
1
|
+
from typing import Dict, Callable
|
|
1
2
|
from funcnodes import NodeDecorator, Shelf
|
|
2
3
|
import numpy as np
|
|
3
4
|
import pandas as pd
|
|
4
|
-
from enum import Enum
|
|
5
5
|
from scipy.signal import savgol_filter, medfilt
|
|
6
6
|
from scipy.ndimage import gaussian_filter1d
|
|
7
7
|
import warnings
|
|
8
|
+
import funcnodes as fn
|
|
8
9
|
|
|
9
10
|
warnings.filterwarnings("ignore")
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
class SmoothMode(fn.DataEnum):
|
|
12
14
|
SAVITZKY_GOLAY = "savgol"
|
|
13
15
|
GAUSSIAN = "gaussian"
|
|
14
16
|
MOVING_AVERAGE = "ma"
|
|
15
17
|
EXPONENTIAL_MOVING_AVERAGE = "ema"
|
|
16
18
|
MEDIAN = "median"
|
|
17
|
-
|
|
18
|
-
@classmethod
|
|
19
|
-
def default(cls) -> 'SmoothMode':
|
|
20
|
-
"""Returns the default smoothing mode."""
|
|
21
|
-
return cls.SAVITZKY_GOLAY.value
|
|
22
19
|
|
|
23
|
-
@NodeDecorator("span.basics.smooth", name="Smoothing")
|
|
24
20
|
|
|
25
|
-
def
|
|
21
|
+
def smooth_savgol(x: np.ndarray, window: int) -> np.ndarray:
|
|
22
|
+
return savgol_filter(x, window, 2)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def smooth_gaussian(x: np.ndarray, window: int) -> np.ndarray:
|
|
26
|
+
return gaussian_filter1d(x, window)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def smooth_ma(x: np.ndarray, window: int) -> np.ndarray:
|
|
30
|
+
if x.ndim > 1:
|
|
31
|
+
n, m = x.shape
|
|
32
|
+
result = np.zeros((n, m))
|
|
33
|
+
for i in range(n):
|
|
34
|
+
result[i, :] = np.convolve(x[i, :], np.ones(window) / window, mode="same")
|
|
35
|
+
return result
|
|
36
|
+
else:
|
|
37
|
+
return np.convolve(x, np.ones(window) / window, mode="same")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def smooth_ema(x: np.ndarray, window: int) -> np.ndarray:
|
|
41
|
+
if x.ndim > 1:
|
|
42
|
+
n, m = x.shape
|
|
43
|
+
result = np.zeros((n, m))
|
|
44
|
+
for i in range(n):
|
|
45
|
+
result[i, :] = pd.Series(x[i, :]).ewm(span=window).mean().values
|
|
46
|
+
return result
|
|
47
|
+
else:
|
|
48
|
+
return pd.Series(x).ewm(span=window).mean().values
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def smooth_median(x: np.ndarray, window: int) -> np.ndarray:
|
|
52
|
+
return medfilt(x, window)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_SMOOTHING_MAPPER: Dict[str, Callable[[np.ndarray, int], np.ndarray]] = {
|
|
56
|
+
SmoothMode.SAVITZKY_GOLAY.value: smooth_savgol,
|
|
57
|
+
SmoothMode.GAUSSIAN.value: smooth_gaussian,
|
|
58
|
+
SmoothMode.MOVING_AVERAGE.value: smooth_ma,
|
|
59
|
+
SmoothMode.EXPONENTIAL_MOVING_AVERAGE.value: smooth_ema,
|
|
60
|
+
SmoothMode.MEDIAN.value: smooth_median,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@NodeDecorator("span.basics.smooth", name="Smoothing")
|
|
65
|
+
def _smooth(
|
|
66
|
+
array: np.ndarray, mode: SmoothMode = SmoothMode.SAVITZKY_GOLAY, window: int = 5
|
|
67
|
+
) -> np.ndarray:
|
|
26
68
|
# """
|
|
27
69
|
# Apply different smoothing techniques to the input array.
|
|
28
70
|
|
|
@@ -37,49 +79,12 @@ def _smooth(array: np.ndarray, mode: SmoothMode = SmoothMode.default(), window:
|
|
|
37
79
|
# Raises:
|
|
38
80
|
# ValueError: If an unsupported smoothing mode is provided.
|
|
39
81
|
# """
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return savgol_filter(x, window, 2)
|
|
44
|
-
|
|
45
|
-
def smooth_gaussian(x: np.ndarray) -> np.ndarray:
|
|
46
|
-
return gaussian_filter1d(x, window)
|
|
47
|
-
|
|
48
|
-
def smooth_ma(x: np.ndarray) -> np.ndarray:
|
|
49
|
-
if x.ndim > 1:
|
|
50
|
-
n, m = x.shape
|
|
51
|
-
result = np.zeros((n, m))
|
|
52
|
-
for i in range(n):
|
|
53
|
-
result[i, :] = np.convolve(x[i, :], np.ones(window) / window, mode="same")
|
|
54
|
-
return result
|
|
55
|
-
else:
|
|
56
|
-
return np.convolve(x, np.ones(window) / window, mode="same")
|
|
57
|
-
|
|
58
|
-
def smooth_ema(x: np.ndarray) -> np.ndarray:
|
|
59
|
-
if x.ndim > 1:
|
|
60
|
-
n, m = x.shape
|
|
61
|
-
result = np.zeros((n, m))
|
|
62
|
-
for i in range(n):
|
|
63
|
-
result[i, :] = pd.Series(x[i, :]).ewm(span=window).mean().values
|
|
64
|
-
return result
|
|
65
|
-
else:
|
|
66
|
-
return pd.Series(x).ewm(span=window).mean().values
|
|
67
|
-
|
|
68
|
-
def smooth_median(x: np.ndarray) -> np.ndarray:
|
|
69
|
-
return medfilt(x, window)
|
|
70
|
-
|
|
71
|
-
smoothing_methods = {
|
|
72
|
-
SmoothMode.SAVITZKY_GOLAY.value: smooth_savgol,
|
|
73
|
-
SmoothMode.GAUSSIAN.value: smooth_gaussian,
|
|
74
|
-
SmoothMode.MOVING_AVERAGE.value: smooth_ma,
|
|
75
|
-
SmoothMode.EXPONENTIAL_MOVING_AVERAGE.value: smooth_ema,
|
|
76
|
-
SmoothMode.MEDIAN.value: smooth_median
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if mode not in smoothing_methods.keys():
|
|
82
|
+
mode = SmoothMode.v(mode)
|
|
83
|
+
|
|
84
|
+
if mode not in _SMOOTHING_MAPPER.keys():
|
|
80
85
|
raise ValueError(f"Unsupported smoothing mode: {mode}")
|
|
81
|
-
|
|
82
|
-
return
|
|
86
|
+
|
|
87
|
+
return _SMOOTHING_MAPPER[mode](array, window)
|
|
83
88
|
|
|
84
89
|
|
|
85
90
|
# @NodeDecorator("span.basics.smooth.savgol", name="Savgol")
|
|
@@ -187,4 +192,4 @@ SMOOTH_NODE_SHELF = Shelf(
|
|
|
187
192
|
subshelves=[],
|
|
188
193
|
name="Smoothing",
|
|
189
194
|
description="Smoothing of the spectra",
|
|
190
|
-
)
|
|
195
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "funcnodes-span"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["Kourosh Rezaei <kouroshrezaei90@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -15,7 +15,7 @@ funcnodes_numpy = "*"
|
|
|
15
15
|
funcnodes_pandas = "*"
|
|
16
16
|
funcnodes_plotly = "*"
|
|
17
17
|
|
|
18
|
-
funcnodes-core = ">=0.1.
|
|
18
|
+
funcnodes-core = ">=0.1.4"
|
|
19
19
|
pybaselines = "*"
|
|
20
20
|
Numba = "*"
|
|
21
21
|
pentapy = "*"
|
|
File without changes
|