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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funcnodes-span
3
- Version: 0.2.0
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.3)
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.0"
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(Enum):
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.default(),
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
- if isinstance(cost_function, CostFunction):
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(Enum):
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.default(),
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
- if isinstance(cost_function, PenalizedPolyCostFunction):
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(Enum):
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.default(),
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
- if isinstance(side, Side):
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(Enum):
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.default(),
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
- if isinstance(method, Method):
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(Enum):
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.default(),
1994
+ method: MethodColab = MethodColab.asls,
2019
1995
  ) -> Tuple[np.ndarray, np.ndarray, dict]:
2020
- if isinstance(method, MethodColab):
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(Enum):
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.default(),
2071
+ method: MethodAll = MethodAll.asls,
2101
2072
  ) -> Tuple[np.ndarray, np.ndarray, dict]:
2102
- if isinstance(method, MethodAll):
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.default(),
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.default(),
2108
+ method: MethodAll = MethodAll.asls,
2139
2109
  ) -> Tuple[np.ndarray, np.ndarray, dict]:
2140
- if isinstance(method, MethodAll):
2141
- method = method.value
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
- class NormMode(Enum):
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.default()) -> np.ndarray:
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
- if isinstance(mode, NormMode):
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 * ((x - np.amin(x)) / (np.amax(x) - np.amin(x))) - 1,
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[dict] = None
39
- fitting_info: Optional[dict] = None
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[dict] = None,
50
- fitting_info: Optional[dict] = None,
57
+ fitting_data: Optional[Dict[str, np.ndarray]] = None,
58
+ fitting_info: Optional[FittinInfo] = None,
51
59
  ) -> PeakProperties:
52
- # """
53
- # Compute various properties of a given peak.
54
-
55
- # Parameters:
56
- # - x_array: np.ndarray - The array of x-values (e.g., time or wavelength).
57
- # - y_array: np.ndarray - The array of y-values (e.g., intensity).
58
- # - peak_indices: List[int] - A list containing the start index, peak index, and end index of the peak.
59
- # - peak_nr: int - The identifier number of the peak.
60
- # - is_fitted: bool = False - A flag indicating whether the peak is fitted or not.
61
- # - is_force_fitted: bool = False - A flag indicating whether the peak is forced fitted or not.
62
- # - fitting_data: Optional[dict] = None - A dictionary containing the fitting data if the peak is fitted.
63
- # - fitting_info: Optional[dict] = None - A dictionary containing the fitting information if the peak is fitted.
64
-
65
- # Returns:
66
- # - peak_properties: PeakProperties - A dictionary containing various properties of the peak.
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
- ) -> dict:
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(Enum):
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.default(),
383
- baseline_model: BaselineModel = BaselineModel.default(),
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
- if isinstance(main_model, FittingModel):
409
- main_model = main_model.value
410
- if isinstance(baseline_model, BaselineModel):
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
- class SmoothMode(Enum):
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 _smooth(array: np.ndarray, mode: SmoothMode = SmoothMode.default(), window: int = 5) -> np.ndarray:
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
- if isinstance(mode, SmoothMode):
41
- mode = mode.value
42
- def smooth_savgol(x: np.ndarray) -> np.ndarray:
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 smoothing_methods[mode](array)
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.0"
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.3"
18
+ funcnodes-core = ">=0.1.4"
19
19
  pybaselines = "*"
20
20
  Numba = "*"
21
21
  pentapy = "*"
File without changes