funcnodes-span 0.1.22__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.1.22 → funcnodes_span-0.2.1}/PKG-INFO +2 -2
- {funcnodes_span-0.1.22 → funcnodes_span-0.2.1}/funcnodes_span/__init__.py +1 -1
- {funcnodes_span-0.1.22 → funcnodes_span-0.2.1}/funcnodes_span/baseline.py +672 -18
- {funcnodes_span-0.1.22 → funcnodes_span-0.2.1}/funcnodes_span/normalization.py +10 -12
- {funcnodes_span-0.1.22 → funcnodes_span-0.2.1}/funcnodes_span/peak_analysis.py +50 -38
- {funcnodes_span-0.1.22 → funcnodes_span-0.2.1}/funcnodes_span/smoothing.py +57 -52
- {funcnodes_span-0.1.22 → funcnodes_span-0.2.1}/pyproject.toml +2 -2
- {funcnodes_span-0.1.22 → 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.1
|
|
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.1
|
|
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
|
|
5
|
-
from typing import Optional, Tuple
|
|
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,
|
|
@@ -1480,6 +1470,667 @@ SPLINE_NODE_SHELF = Shelf(
|
|
|
1480
1470
|
)
|
|
1481
1471
|
|
|
1482
1472
|
|
|
1473
|
+
@NodeDecorator(
|
|
1474
|
+
"pybaselines.smooth.ipsa",
|
|
1475
|
+
name="ipsa",
|
|
1476
|
+
outputs=[
|
|
1477
|
+
{"name": "baseline_corrected"},
|
|
1478
|
+
{"name": "baseline"},
|
|
1479
|
+
{"name": "params"},
|
|
1480
|
+
],
|
|
1481
|
+
)
|
|
1482
|
+
@controlled_wrapper(pybaselines.smooth.ipsa, wrapper_attribute="__fnwrapped__")
|
|
1483
|
+
def _ipsa(
|
|
1484
|
+
data: np.ndarray,
|
|
1485
|
+
x_data: Optional[np.ndarray] = None,
|
|
1486
|
+
half_window: Optional[int] = None,
|
|
1487
|
+
max_iter: int = 500,
|
|
1488
|
+
tol: Optional[float] = None,
|
|
1489
|
+
roi: Optional[np.ndarray] = None,
|
|
1490
|
+
original_criteria: bool = False,
|
|
1491
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1492
|
+
baseline, params = pybaselines.smooth.ipsa(
|
|
1493
|
+
data,
|
|
1494
|
+
x_data=x_data,
|
|
1495
|
+
max_iter=max_iter,
|
|
1496
|
+
half_window=half_window,
|
|
1497
|
+
roi=roi,
|
|
1498
|
+
tol=tol,
|
|
1499
|
+
original_criteria=original_criteria,
|
|
1500
|
+
)
|
|
1501
|
+
baseline_corrected = data - baseline
|
|
1502
|
+
return baseline_corrected, baseline, params
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
@NodeDecorator(
|
|
1506
|
+
"pybaselines.smooth.noise_median",
|
|
1507
|
+
name="noise_median",
|
|
1508
|
+
outputs=[
|
|
1509
|
+
{"name": "baseline_corrected"},
|
|
1510
|
+
{"name": "baseline"},
|
|
1511
|
+
{"name": "params"},
|
|
1512
|
+
],
|
|
1513
|
+
)
|
|
1514
|
+
@controlled_wrapper(pybaselines.smooth.noise_median, wrapper_attribute="__fnwrapped__")
|
|
1515
|
+
def _noise_median(
|
|
1516
|
+
data: np.ndarray,
|
|
1517
|
+
x_data: Optional[np.ndarray] = None,
|
|
1518
|
+
half_window: Optional[int] = None,
|
|
1519
|
+
smooth_half_window: Optional[int] = None,
|
|
1520
|
+
sigma: Optional[float] = None,
|
|
1521
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1522
|
+
baseline, params = pybaselines.smooth.noise_median(
|
|
1523
|
+
data,
|
|
1524
|
+
x_data=x_data,
|
|
1525
|
+
smooth_half_window=smooth_half_window,
|
|
1526
|
+
half_window=half_window,
|
|
1527
|
+
sigma=sigma,
|
|
1528
|
+
)
|
|
1529
|
+
baseline_corrected = data - baseline
|
|
1530
|
+
return baseline_corrected, baseline, params
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
class Side(fn.DataEnum):
|
|
1534
|
+
both = "both"
|
|
1535
|
+
left = "left"
|
|
1536
|
+
right = "right"
|
|
1537
|
+
|
|
1538
|
+
|
|
1539
|
+
@NodeDecorator(
|
|
1540
|
+
"pybaselines.smooth.ria",
|
|
1541
|
+
name="ria",
|
|
1542
|
+
outputs=[
|
|
1543
|
+
{"name": "baseline_corrected"},
|
|
1544
|
+
{"name": "baseline"},
|
|
1545
|
+
{"name": "params"},
|
|
1546
|
+
],
|
|
1547
|
+
)
|
|
1548
|
+
@controlled_wrapper(pybaselines.smooth.ria, wrapper_attribute="__fnwrapped__")
|
|
1549
|
+
def _ria(
|
|
1550
|
+
data: np.ndarray,
|
|
1551
|
+
x_data: Optional[np.ndarray] = None,
|
|
1552
|
+
half_window: Optional[int] = None,
|
|
1553
|
+
max_iter: int = 500,
|
|
1554
|
+
tol: float = 0.01,
|
|
1555
|
+
side: Side = Side.both,
|
|
1556
|
+
width_scale: float = 0.1,
|
|
1557
|
+
height_scale: float = 1.0,
|
|
1558
|
+
sigma_scale: float = 1.0 / 12.0,
|
|
1559
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1560
|
+
side = Side.v(side)
|
|
1561
|
+
baseline, params = pybaselines.smooth.ria(
|
|
1562
|
+
data,
|
|
1563
|
+
x_data=x_data,
|
|
1564
|
+
max_iter=max_iter,
|
|
1565
|
+
tol=tol,
|
|
1566
|
+
half_window=half_window,
|
|
1567
|
+
side=side,
|
|
1568
|
+
width_scale=width_scale,
|
|
1569
|
+
height_scale=height_scale,
|
|
1570
|
+
sigma_scale=sigma_scale,
|
|
1571
|
+
)
|
|
1572
|
+
baseline_corrected = data - baseline
|
|
1573
|
+
return baseline_corrected, baseline, params
|
|
1574
|
+
|
|
1575
|
+
|
|
1576
|
+
@NodeDecorator(
|
|
1577
|
+
"pybaselines.smooth.snip",
|
|
1578
|
+
name="snip",
|
|
1579
|
+
outputs=[
|
|
1580
|
+
{"name": "baseline_corrected"},
|
|
1581
|
+
{"name": "baseline"},
|
|
1582
|
+
{"name": "params"},
|
|
1583
|
+
],
|
|
1584
|
+
)
|
|
1585
|
+
@controlled_wrapper(pybaselines.smooth.snip, wrapper_attribute="__fnwrapped__")
|
|
1586
|
+
def _snip(
|
|
1587
|
+
data: np.ndarray,
|
|
1588
|
+
x_data: Optional[np.ndarray] = None,
|
|
1589
|
+
decreasing: bool = False,
|
|
1590
|
+
smooth_half_window: Optional[int] = None,
|
|
1591
|
+
filter_order: int = 2,
|
|
1592
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1593
|
+
baseline, params = pybaselines.smooth.snip(
|
|
1594
|
+
data,
|
|
1595
|
+
x_data=x_data,
|
|
1596
|
+
decreasing=decreasing,
|
|
1597
|
+
smooth_half_window=smooth_half_window,
|
|
1598
|
+
filter_order=filter_order,
|
|
1599
|
+
)
|
|
1600
|
+
baseline_corrected = data - baseline
|
|
1601
|
+
return baseline_corrected, baseline, params
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
@NodeDecorator(
|
|
1605
|
+
"pybaselines.smooth.swima",
|
|
1606
|
+
name="swima",
|
|
1607
|
+
outputs=[
|
|
1608
|
+
{"name": "baseline_corrected"},
|
|
1609
|
+
{"name": "baseline"},
|
|
1610
|
+
{"name": "params"},
|
|
1611
|
+
],
|
|
1612
|
+
)
|
|
1613
|
+
@controlled_wrapper(pybaselines.smooth.swima, wrapper_attribute="__fnwrapped__")
|
|
1614
|
+
def _swima(
|
|
1615
|
+
data: np.ndarray,
|
|
1616
|
+
x_data: Optional[np.ndarray] = None,
|
|
1617
|
+
min_half_window: int = 3,
|
|
1618
|
+
max_half_window: Optional[int] = None,
|
|
1619
|
+
smooth_half_window: Optional[int] = None,
|
|
1620
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1621
|
+
baseline, params = pybaselines.smooth.swima(
|
|
1622
|
+
data,
|
|
1623
|
+
x_data=x_data,
|
|
1624
|
+
min_half_window=min_half_window,
|
|
1625
|
+
smooth_half_window=smooth_half_window,
|
|
1626
|
+
max_half_window=max_half_window,
|
|
1627
|
+
)
|
|
1628
|
+
baseline_corrected = data - baseline
|
|
1629
|
+
return baseline_corrected, baseline, params
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
SMOOTH_NODE_SHELF = Shelf(
|
|
1633
|
+
nodes=[_ipsa, _noise_median, _ria, _snip, _swima],
|
|
1634
|
+
subshelves=[],
|
|
1635
|
+
name="Smooth",
|
|
1636
|
+
description="Fits a smooth baseline",
|
|
1637
|
+
)
|
|
1638
|
+
|
|
1639
|
+
|
|
1640
|
+
@NodeDecorator(
|
|
1641
|
+
"pybaselines.classification.cwt_br",
|
|
1642
|
+
name="cwt_br",
|
|
1643
|
+
outputs=[
|
|
1644
|
+
{"name": "baseline_corrected"},
|
|
1645
|
+
{"name": "baseline"},
|
|
1646
|
+
{"name": "params"},
|
|
1647
|
+
],
|
|
1648
|
+
)
|
|
1649
|
+
@controlled_wrapper(
|
|
1650
|
+
pybaselines.classification.cwt_br, wrapper_attribute="__fnwrapped__"
|
|
1651
|
+
)
|
|
1652
|
+
def _cwt_br(
|
|
1653
|
+
data: np.ndarray,
|
|
1654
|
+
x_data: Optional[np.ndarray] = None,
|
|
1655
|
+
poly_order: int = 5,
|
|
1656
|
+
scale: Optional[np.ndarray] = None,
|
|
1657
|
+
num_std: float = 1.0,
|
|
1658
|
+
min_length: int = 2,
|
|
1659
|
+
max_iter: int = 50,
|
|
1660
|
+
tol: float = 0.001,
|
|
1661
|
+
symmetric: bool = False,
|
|
1662
|
+
weights: Optional[np.ndarray] = None,
|
|
1663
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1664
|
+
baseline, params = pybaselines.classification.cwt_br(
|
|
1665
|
+
data,
|
|
1666
|
+
x_data=x_data,
|
|
1667
|
+
poly_order=poly_order,
|
|
1668
|
+
scale=scale,
|
|
1669
|
+
num_std=num_std,
|
|
1670
|
+
min_length=min_length,
|
|
1671
|
+
max_iter=max_iter,
|
|
1672
|
+
tol=tol,
|
|
1673
|
+
symmetric=symmetric,
|
|
1674
|
+
weights=weights,
|
|
1675
|
+
)
|
|
1676
|
+
baseline_corrected = data - baseline
|
|
1677
|
+
return baseline_corrected, baseline, params
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
@NodeDecorator(
|
|
1681
|
+
"pybaselines.classification.dietrich",
|
|
1682
|
+
name="dietrich",
|
|
1683
|
+
outputs=[
|
|
1684
|
+
{"name": "baseline_corrected"},
|
|
1685
|
+
{"name": "baseline"},
|
|
1686
|
+
{"name": "params"},
|
|
1687
|
+
],
|
|
1688
|
+
)
|
|
1689
|
+
@controlled_wrapper(
|
|
1690
|
+
pybaselines.classification.dietrich, wrapper_attribute="__fnwrapped__"
|
|
1691
|
+
)
|
|
1692
|
+
def _dietrich(
|
|
1693
|
+
data: np.ndarray,
|
|
1694
|
+
x_data: Optional[np.ndarray] = None,
|
|
1695
|
+
smooth_half_window: Optional[int] = None,
|
|
1696
|
+
interp_half_window: int = 5,
|
|
1697
|
+
poly_order: int = 5,
|
|
1698
|
+
max_iter: int = 50,
|
|
1699
|
+
tol: float = 0.001,
|
|
1700
|
+
num_std: float = 1.0,
|
|
1701
|
+
min_length: int = 2,
|
|
1702
|
+
return_coef: bool = False,
|
|
1703
|
+
weights: Optional[np.ndarray] = None,
|
|
1704
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1705
|
+
baseline, params = pybaselines.classification.dietrich(
|
|
1706
|
+
data,
|
|
1707
|
+
x_data=x_data,
|
|
1708
|
+
poly_order=poly_order,
|
|
1709
|
+
smooth_half_window=smooth_half_window,
|
|
1710
|
+
num_std=num_std,
|
|
1711
|
+
min_length=min_length,
|
|
1712
|
+
max_iter=max_iter,
|
|
1713
|
+
tol=tol,
|
|
1714
|
+
interp_half_window=interp_half_window,
|
|
1715
|
+
return_coef=return_coef,
|
|
1716
|
+
weights=weights,
|
|
1717
|
+
)
|
|
1718
|
+
baseline_corrected = data - baseline
|
|
1719
|
+
return baseline_corrected, baseline, params
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
@NodeDecorator(
|
|
1723
|
+
"pybaselines.classification.fabc",
|
|
1724
|
+
name="fabc",
|
|
1725
|
+
outputs=[
|
|
1726
|
+
{"name": "baseline_corrected"},
|
|
1727
|
+
{"name": "baseline"},
|
|
1728
|
+
{"name": "params"},
|
|
1729
|
+
],
|
|
1730
|
+
)
|
|
1731
|
+
@controlled_wrapper(pybaselines.classification.fabc, wrapper_attribute="__fnwrapped__")
|
|
1732
|
+
def _fabc(
|
|
1733
|
+
data: np.ndarray,
|
|
1734
|
+
x_data: Optional[np.ndarray] = None,
|
|
1735
|
+
lam: float = 1000000.0,
|
|
1736
|
+
scale: Optional[np.ndarray] = None,
|
|
1737
|
+
num_std: float = 3.0,
|
|
1738
|
+
diff_order: int = 2,
|
|
1739
|
+
min_length: int = 2,
|
|
1740
|
+
weights_as_mask: bool = False,
|
|
1741
|
+
weights: Optional[np.ndarray] = None,
|
|
1742
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1743
|
+
baseline, params = pybaselines.classification.fabc(
|
|
1744
|
+
data,
|
|
1745
|
+
x_data=x_data,
|
|
1746
|
+
lam=lam,
|
|
1747
|
+
scale=scale,
|
|
1748
|
+
num_std=num_std,
|
|
1749
|
+
min_length=min_length,
|
|
1750
|
+
diff_order=diff_order,
|
|
1751
|
+
weights_as_mask=weights_as_mask,
|
|
1752
|
+
weights=weights,
|
|
1753
|
+
)
|
|
1754
|
+
baseline_corrected = data - baseline
|
|
1755
|
+
return baseline_corrected, baseline, params
|
|
1756
|
+
|
|
1757
|
+
|
|
1758
|
+
@NodeDecorator(
|
|
1759
|
+
"pybaselines.classification.fastchrom",
|
|
1760
|
+
name="fastchrom",
|
|
1761
|
+
outputs=[
|
|
1762
|
+
{"name": "baseline_corrected"},
|
|
1763
|
+
{"name": "baseline"},
|
|
1764
|
+
{"name": "params"},
|
|
1765
|
+
],
|
|
1766
|
+
)
|
|
1767
|
+
@controlled_wrapper(
|
|
1768
|
+
pybaselines.classification.fastchrom, wrapper_attribute="__fnwrapped__"
|
|
1769
|
+
)
|
|
1770
|
+
def _fastchrom(
|
|
1771
|
+
data: np.ndarray,
|
|
1772
|
+
x_data: Optional[np.ndarray] = None,
|
|
1773
|
+
half_window: Optional[int] = None,
|
|
1774
|
+
threshold: Optional[float] = None,
|
|
1775
|
+
min_fwhm: Optional[int] = None,
|
|
1776
|
+
interp_half_window: int = 5,
|
|
1777
|
+
smooth_half_window: Optional[int] = None,
|
|
1778
|
+
max_iter: int = 100,
|
|
1779
|
+
min_length: int = 2,
|
|
1780
|
+
weights: Optional[np.ndarray] = None,
|
|
1781
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1782
|
+
baseline, params = pybaselines.classification.fastchrom(
|
|
1783
|
+
data,
|
|
1784
|
+
x_data=x_data,
|
|
1785
|
+
half_window=half_window,
|
|
1786
|
+
threshold=threshold,
|
|
1787
|
+
min_fwhm=min_fwhm,
|
|
1788
|
+
min_length=min_length,
|
|
1789
|
+
max_iter=max_iter,
|
|
1790
|
+
smooth_half_window=smooth_half_window,
|
|
1791
|
+
interp_half_window=interp_half_window,
|
|
1792
|
+
weights=weights,
|
|
1793
|
+
)
|
|
1794
|
+
baseline_corrected = data - baseline
|
|
1795
|
+
return baseline_corrected, baseline, params
|
|
1796
|
+
|
|
1797
|
+
|
|
1798
|
+
@NodeDecorator(
|
|
1799
|
+
"pybaselines.classification.golotvin",
|
|
1800
|
+
name="golotvin",
|
|
1801
|
+
outputs=[
|
|
1802
|
+
{"name": "baseline_corrected"},
|
|
1803
|
+
{"name": "baseline"},
|
|
1804
|
+
{"name": "params"},
|
|
1805
|
+
],
|
|
1806
|
+
)
|
|
1807
|
+
@controlled_wrapper(
|
|
1808
|
+
pybaselines.classification.golotvin, wrapper_attribute="__fnwrapped__"
|
|
1809
|
+
)
|
|
1810
|
+
def _golotvin(
|
|
1811
|
+
data: np.ndarray,
|
|
1812
|
+
x_data: Optional[np.ndarray] = None,
|
|
1813
|
+
half_window: Optional[int] = None,
|
|
1814
|
+
num_std: float = 2.0,
|
|
1815
|
+
sections: int = 32,
|
|
1816
|
+
threshold: Optional[float] = None,
|
|
1817
|
+
interp_half_window: int = 5,
|
|
1818
|
+
smooth_half_window: Optional[int] = None,
|
|
1819
|
+
min_length: int = 2,
|
|
1820
|
+
weights: Optional[np.ndarray] = None,
|
|
1821
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1822
|
+
baseline, params = pybaselines.classification.golotvin(
|
|
1823
|
+
data,
|
|
1824
|
+
x_data=x_data,
|
|
1825
|
+
half_window=half_window,
|
|
1826
|
+
threshold=threshold,
|
|
1827
|
+
num_std=num_std,
|
|
1828
|
+
min_length=min_length,
|
|
1829
|
+
sections=sections,
|
|
1830
|
+
smooth_half_window=smooth_half_window,
|
|
1831
|
+
interp_half_window=interp_half_window,
|
|
1832
|
+
weights=weights,
|
|
1833
|
+
)
|
|
1834
|
+
baseline_corrected = data - baseline
|
|
1835
|
+
return baseline_corrected, baseline, params
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
@NodeDecorator(
|
|
1839
|
+
"pybaselines.classification.rubberband",
|
|
1840
|
+
name="rubberband",
|
|
1841
|
+
outputs=[
|
|
1842
|
+
{"name": "baseline_corrected"},
|
|
1843
|
+
{"name": "baseline"},
|
|
1844
|
+
{"name": "params"},
|
|
1845
|
+
],
|
|
1846
|
+
)
|
|
1847
|
+
@controlled_wrapper(
|
|
1848
|
+
pybaselines.classification.rubberband, wrapper_attribute="__fnwrapped__"
|
|
1849
|
+
)
|
|
1850
|
+
def _rubberband(
|
|
1851
|
+
data: np.ndarray,
|
|
1852
|
+
x_data: Optional[np.ndarray] = None,
|
|
1853
|
+
segments: Union[int, np.ndarray] = 1,
|
|
1854
|
+
lam: Optional[float] = None,
|
|
1855
|
+
diff_order: int = 2,
|
|
1856
|
+
smooth_half_window: Optional[int] = None,
|
|
1857
|
+
weights: Optional[np.ndarray] = None,
|
|
1858
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1859
|
+
baseline, params = pybaselines.classification.rubberband(
|
|
1860
|
+
data,
|
|
1861
|
+
x_data=x_data,
|
|
1862
|
+
segments=segments,
|
|
1863
|
+
lam=lam,
|
|
1864
|
+
diff_order=diff_order,
|
|
1865
|
+
smooth_half_window=smooth_half_window,
|
|
1866
|
+
weights=weights,
|
|
1867
|
+
)
|
|
1868
|
+
baseline_corrected = data - baseline
|
|
1869
|
+
return baseline_corrected, baseline, params
|
|
1870
|
+
|
|
1871
|
+
|
|
1872
|
+
@NodeDecorator(
|
|
1873
|
+
"pybaselines.classification.std_distribution",
|
|
1874
|
+
name="std_distribution",
|
|
1875
|
+
outputs=[
|
|
1876
|
+
{"name": "baseline_corrected"},
|
|
1877
|
+
{"name": "baseline"},
|
|
1878
|
+
{"name": "params"},
|
|
1879
|
+
],
|
|
1880
|
+
)
|
|
1881
|
+
@controlled_wrapper(
|
|
1882
|
+
pybaselines.classification.std_distribution, wrapper_attribute="__fnwrapped__"
|
|
1883
|
+
)
|
|
1884
|
+
def _std_distribution(
|
|
1885
|
+
data: np.ndarray,
|
|
1886
|
+
x_data: Optional[np.ndarray] = None,
|
|
1887
|
+
half_window: Optional[int] = None,
|
|
1888
|
+
interp_half_window: int = 5,
|
|
1889
|
+
fill_half_window: int = 3,
|
|
1890
|
+
num_std: float = 1.1,
|
|
1891
|
+
smooth_half_window: Optional[int] = None,
|
|
1892
|
+
weights: Optional[np.ndarray] = None,
|
|
1893
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1894
|
+
baseline, params = pybaselines.classification.std_distribution(
|
|
1895
|
+
data,
|
|
1896
|
+
x_data=x_data,
|
|
1897
|
+
half_window=half_window,
|
|
1898
|
+
interp_half_window=interp_half_window,
|
|
1899
|
+
fill_half_window=fill_half_window,
|
|
1900
|
+
smooth_half_window=smooth_half_window,
|
|
1901
|
+
num_std=num_std,
|
|
1902
|
+
weights=weights,
|
|
1903
|
+
)
|
|
1904
|
+
baseline_corrected = data - baseline
|
|
1905
|
+
return baseline_corrected, baseline, params
|
|
1906
|
+
|
|
1907
|
+
|
|
1908
|
+
CLASSIFICATION_NODE_SHELF = Shelf(
|
|
1909
|
+
nodes=[
|
|
1910
|
+
_cwt_br,
|
|
1911
|
+
_dietrich,
|
|
1912
|
+
_fabc,
|
|
1913
|
+
_fastchrom,
|
|
1914
|
+
_golotvin,
|
|
1915
|
+
_rubberband,
|
|
1916
|
+
_std_distribution,
|
|
1917
|
+
],
|
|
1918
|
+
subshelves=[],
|
|
1919
|
+
name="Classification",
|
|
1920
|
+
description="Fits a classification baseline",
|
|
1921
|
+
)
|
|
1922
|
+
|
|
1923
|
+
|
|
1924
|
+
class Method(fn.DataEnum):
|
|
1925
|
+
modpoly = "modpoly"
|
|
1926
|
+
imodpoly = "imodpoly"
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
@NodeDecorator(
|
|
1930
|
+
"pybaselines.optimizers.adaptive_minmax",
|
|
1931
|
+
name="adaptive_minmax",
|
|
1932
|
+
outputs=[
|
|
1933
|
+
{"name": "baseline_corrected"},
|
|
1934
|
+
{"name": "baseline"},
|
|
1935
|
+
{"name": "params"},
|
|
1936
|
+
],
|
|
1937
|
+
)
|
|
1938
|
+
@controlled_wrapper(
|
|
1939
|
+
pybaselines.optimizers.adaptive_minmax, wrapper_attribute="__fnwrapped__"
|
|
1940
|
+
)
|
|
1941
|
+
def _adaptive_minmax(
|
|
1942
|
+
data: np.ndarray,
|
|
1943
|
+
x_data: Optional[np.ndarray] = None,
|
|
1944
|
+
poly_order: Optional[Union[int, List[int]]] = None,
|
|
1945
|
+
method: Method = Method.modpoly,
|
|
1946
|
+
constrained_fraction: Union[float, List[float]] = 0.01,
|
|
1947
|
+
constrained_weight: Union[float, List[float]] = 100000.0,
|
|
1948
|
+
estimation_poly_order: int = 2,
|
|
1949
|
+
weights: Optional[np.ndarray] = None,
|
|
1950
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1951
|
+
method = Method.v(method)
|
|
1952
|
+
baseline, params = pybaselines.optimizers.adaptive_minmax(
|
|
1953
|
+
data,
|
|
1954
|
+
x_data=x_data,
|
|
1955
|
+
poly_order=poly_order,
|
|
1956
|
+
constrained_fraction=constrained_fraction,
|
|
1957
|
+
constrained_weight=constrained_weight,
|
|
1958
|
+
method=method,
|
|
1959
|
+
estimation_poly_order=estimation_poly_order,
|
|
1960
|
+
weights=weights,
|
|
1961
|
+
)
|
|
1962
|
+
baseline_corrected = data - baseline
|
|
1963
|
+
return baseline_corrected, baseline, params
|
|
1964
|
+
|
|
1965
|
+
|
|
1966
|
+
class MethodColab(fn.DataEnum):
|
|
1967
|
+
airpls = "airpls"
|
|
1968
|
+
arpls = "arpls"
|
|
1969
|
+
asls = "asls"
|
|
1970
|
+
aspls = "aspls"
|
|
1971
|
+
derpsalsa = "derpsalsa"
|
|
1972
|
+
drpls = "drpls"
|
|
1973
|
+
iarpls = "iarpls"
|
|
1974
|
+
iasls = "iasls"
|
|
1975
|
+
psalsa = "psalsa"
|
|
1976
|
+
|
|
1977
|
+
|
|
1978
|
+
@NodeDecorator(
|
|
1979
|
+
"pybaselines.optimizers.collab_pls",
|
|
1980
|
+
name="collab_pls",
|
|
1981
|
+
outputs=[
|
|
1982
|
+
{"name": "baseline_corrected"},
|
|
1983
|
+
{"name": "baseline"},
|
|
1984
|
+
{"name": "params"},
|
|
1985
|
+
],
|
|
1986
|
+
)
|
|
1987
|
+
@controlled_wrapper(
|
|
1988
|
+
pybaselines.optimizers.collab_pls, wrapper_attribute="__fnwrapped__"
|
|
1989
|
+
)
|
|
1990
|
+
def _collab_pls(
|
|
1991
|
+
data: np.ndarray,
|
|
1992
|
+
x_data: Optional[np.ndarray] = None,
|
|
1993
|
+
average_dataset: bool = True,
|
|
1994
|
+
method: MethodColab = MethodColab.asls,
|
|
1995
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
1996
|
+
method = MethodColab.v(method)
|
|
1997
|
+
baseline, params = pybaselines.optimizers.collab_pls(
|
|
1998
|
+
data,
|
|
1999
|
+
x_data=x_data,
|
|
2000
|
+
method=method,
|
|
2001
|
+
average_dataset=average_dataset,
|
|
2002
|
+
)
|
|
2003
|
+
baseline_corrected = data - baseline
|
|
2004
|
+
return baseline_corrected, baseline, params
|
|
2005
|
+
|
|
2006
|
+
|
|
2007
|
+
class MethodAll(fn.DataEnum):
|
|
2008
|
+
goldindec = "goldindec"
|
|
2009
|
+
imodpoly = "imodpoly"
|
|
2010
|
+
loess = "loess"
|
|
2011
|
+
modpoly = "modpoly"
|
|
2012
|
+
penalizedpoly = "penalizedpoly"
|
|
2013
|
+
poly = "poly"
|
|
2014
|
+
quant_reg = "quant_reg"
|
|
2015
|
+
airpls = "airpls"
|
|
2016
|
+
arpls = "arpls"
|
|
2017
|
+
asls = "asls"
|
|
2018
|
+
aspls = "aspls"
|
|
2019
|
+
derpsalsa = "derpsalsa"
|
|
2020
|
+
drpls = "drpls"
|
|
2021
|
+
iarpls = "iarpls"
|
|
2022
|
+
iasls = "iasls"
|
|
2023
|
+
psalsa = "psalsa"
|
|
2024
|
+
amormol = "amormol"
|
|
2025
|
+
imor = "imor"
|
|
2026
|
+
jbcd = "jbcd"
|
|
2027
|
+
mor = "mor"
|
|
2028
|
+
mormol = "mormol"
|
|
2029
|
+
mpls = "mpls"
|
|
2030
|
+
mpspline = "mpspline"
|
|
2031
|
+
mwmv = "mwmv"
|
|
2032
|
+
rolling_ball = "rolling_ball"
|
|
2033
|
+
tophat = "tophat"
|
|
2034
|
+
corner_cutting = "corner_cutting"
|
|
2035
|
+
irsqr = "irsqr"
|
|
2036
|
+
mixture_model = "mixture_model"
|
|
2037
|
+
pspline_airpls = "pspline_airpls"
|
|
2038
|
+
pspline_asls = "pspline_asls"
|
|
2039
|
+
pspline_aspls = "pspline_aspls"
|
|
2040
|
+
pspline_derpsalsa = "pspline_derpsalsa"
|
|
2041
|
+
pspline_drpls = "pspline_drpls"
|
|
2042
|
+
pspline_iarpls = "pspline_iarpls"
|
|
2043
|
+
pspline_iasls = "pspline_iasls"
|
|
2044
|
+
pspline_mpls = "pspline_mpls"
|
|
2045
|
+
pspline_psalsa = "pspline_psalsa"
|
|
2046
|
+
cwt_br = "cwt_br"
|
|
2047
|
+
dietrich = "dietrich"
|
|
2048
|
+
fabc = "fabc"
|
|
2049
|
+
fastchrom = "fastchrom"
|
|
2050
|
+
golotvin = "golotvin"
|
|
2051
|
+
rubberband = "rubberband"
|
|
2052
|
+
std_distribution = "std_distribution"
|
|
2053
|
+
|
|
2054
|
+
|
|
2055
|
+
@NodeDecorator(
|
|
2056
|
+
"pybaselines.optimizers.custom_bc",
|
|
2057
|
+
name="custom_bc",
|
|
2058
|
+
outputs=[
|
|
2059
|
+
{"name": "baseline_corrected"},
|
|
2060
|
+
{"name": "baseline"},
|
|
2061
|
+
{"name": "params"},
|
|
2062
|
+
],
|
|
2063
|
+
)
|
|
2064
|
+
@controlled_wrapper(pybaselines.optimizers.custom_bc, wrapper_attribute="__fnwrapped__")
|
|
2065
|
+
def _custom_bc(
|
|
2066
|
+
data: np.ndarray,
|
|
2067
|
+
x_data: Optional[np.ndarray] = None,
|
|
2068
|
+
sampling: Union[int, np.ndarray] = 1,
|
|
2069
|
+
lam: Optional[float] = None,
|
|
2070
|
+
diff_order: int = 2,
|
|
2071
|
+
method: MethodAll = MethodAll.asls,
|
|
2072
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
2073
|
+
method = MethodAll.v(method)
|
|
2074
|
+
baseline, params = pybaselines.optimizers.custom_bc(
|
|
2075
|
+
data,
|
|
2076
|
+
x_data=x_data,
|
|
2077
|
+
diff_order=diff_order,
|
|
2078
|
+
lam=lam,
|
|
2079
|
+
method=method,
|
|
2080
|
+
sampling=sampling,
|
|
2081
|
+
)
|
|
2082
|
+
baseline_corrected = data - baseline
|
|
2083
|
+
return baseline_corrected, baseline, params
|
|
2084
|
+
|
|
2085
|
+
|
|
2086
|
+
@NodeDecorator(
|
|
2087
|
+
"pybaselines.optimizers.optimize_extended_range",
|
|
2088
|
+
name="optimize_extended_range",
|
|
2089
|
+
outputs=[
|
|
2090
|
+
{"name": "baseline_corrected"},
|
|
2091
|
+
{"name": "baseline"},
|
|
2092
|
+
{"name": "params"},
|
|
2093
|
+
],
|
|
2094
|
+
)
|
|
2095
|
+
@controlled_wrapper(
|
|
2096
|
+
pybaselines.optimizers.optimize_extended_range, wrapper_attribute="__fnwrapped__"
|
|
2097
|
+
)
|
|
2098
|
+
def _optimize_extended_range(
|
|
2099
|
+
data: np.ndarray,
|
|
2100
|
+
x_data: Optional[np.ndarray] = None,
|
|
2101
|
+
side: Side = Side.both,
|
|
2102
|
+
width_scale: float = 0.1,
|
|
2103
|
+
height_scale: float = 1.0,
|
|
2104
|
+
sigma_scale: float = 1.0 / 12.0,
|
|
2105
|
+
min_value: float = 2.0,
|
|
2106
|
+
max_value: float = 8.0,
|
|
2107
|
+
step: int = 1,
|
|
2108
|
+
method: MethodAll = MethodAll.asls,
|
|
2109
|
+
) -> Tuple[np.ndarray, np.ndarray, dict]:
|
|
2110
|
+
method = MethodAll.v(method)
|
|
2111
|
+
side = Side.v(side)
|
|
2112
|
+
baseline, params = pybaselines.optimizers.optimize_extended_range(
|
|
2113
|
+
data,
|
|
2114
|
+
x_data=x_data,
|
|
2115
|
+
width_scale=width_scale,
|
|
2116
|
+
height_scale=height_scale,
|
|
2117
|
+
method=method,
|
|
2118
|
+
min_value=min_value,
|
|
2119
|
+
sigma_scale=sigma_scale,
|
|
2120
|
+
max_value=max_value,
|
|
2121
|
+
step=step,
|
|
2122
|
+
)
|
|
2123
|
+
baseline_corrected = data - baseline
|
|
2124
|
+
return baseline_corrected, baseline, params
|
|
2125
|
+
|
|
2126
|
+
|
|
2127
|
+
OPTIMIZERS_NODE_SHELF = Shelf(
|
|
2128
|
+
nodes=[_adaptive_minmax, _collab_pls, _custom_bc, _optimize_extended_range],
|
|
2129
|
+
subshelves=[],
|
|
2130
|
+
name="Optimizers",
|
|
2131
|
+
description="Fits a optimizers baseline",
|
|
2132
|
+
)
|
|
2133
|
+
|
|
1483
2134
|
BASELINE_NODE_SHELF = Shelf(
|
|
1484
2135
|
nodes=[],
|
|
1485
2136
|
subshelves=[
|
|
@@ -1487,6 +2138,9 @@ BASELINE_NODE_SHELF = Shelf(
|
|
|
1487
2138
|
WHITTAKER_NODE_SHELF,
|
|
1488
2139
|
MORPHOLOGICAL_NODE_SHELF,
|
|
1489
2140
|
SPLINE_NODE_SHELF,
|
|
2141
|
+
SMOOTH_NODE_SHELF,
|
|
2142
|
+
CLASSIFICATION_NODE_SHELF,
|
|
2143
|
+
OPTIMIZERS_NODE_SHELF,
|
|
1490
2144
|
],
|
|
1491
2145
|
name="Baseline correction",
|
|
1492
2146
|
description="Provides different techniques for fitting baselines to experimental data using pybaselines.",
|
|
@@ -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.1
|
|
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
|