funcnodes-span 0.2.1__tar.gz → 0.2.2__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.1 → funcnodes_span-0.2.2}/PKG-INFO +1 -1
- {funcnodes_span-0.2.1 → funcnodes_span-0.2.2}/funcnodes_span/__init__.py +1 -1
- funcnodes_span-0.2.2/funcnodes_span/normalization.py +161 -0
- {funcnodes_span-0.2.1 → funcnodes_span-0.2.2}/funcnodes_span/peak_analysis.py +28 -4
- {funcnodes_span-0.2.1 → funcnodes_span-0.2.2}/pyproject.toml +1 -1
- funcnodes_span-0.2.1/funcnodes_span/normalization.py +0 -54
- {funcnodes_span-0.2.1 → funcnodes_span-0.2.2}/README.md +0 -0
- {funcnodes_span-0.2.1 → funcnodes_span-0.2.2}/funcnodes_span/baseline.py +0 -0
- {funcnodes_span-0.2.1 → funcnodes_span-0.2.2}/funcnodes_span/smoothing.py +0 -0
|
@@ -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.2"
|
|
9
9
|
|
|
10
10
|
NODE_SHELF = fn.Shelf(
|
|
11
11
|
name="Spectral Analysis",
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from typing import Literal, Tuple
|
|
2
|
+
from funcnodes import NodeDecorator, Shelf
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.interpolate import interp1d
|
|
5
|
+
from exposedfunctionality import controlled_wrapper
|
|
6
|
+
import funcnodes as fn
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NormMode(fn.DataEnum):
|
|
10
|
+
ZERO_ONE = "zero_one"
|
|
11
|
+
MINUS_ONE_ONE = "minus_one_one"
|
|
12
|
+
SUM_ABS = "sum_abs"
|
|
13
|
+
SUM = "sum"
|
|
14
|
+
EUCLIDEAN = "euclidean"
|
|
15
|
+
MEAN_STD = "mean_std"
|
|
16
|
+
MAX = "max"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@NodeDecorator(id="span.basics.norm", name="Normalization node")
|
|
20
|
+
def _norm(array: np.ndarray, mode: NormMode = NormMode.ZERO_ONE) -> np.ndarray:
|
|
21
|
+
# """
|
|
22
|
+
# Apply different normalizations to the array.
|
|
23
|
+
|
|
24
|
+
# Args:
|
|
25
|
+
# array (np.ndarray): The input array to be normalized.
|
|
26
|
+
# mode (NormMode): The normalization mode to apply. Defaults to NormMode.ZERO_ONE.
|
|
27
|
+
|
|
28
|
+
# Returns:
|
|
29
|
+
# np.ndarray: The normalized array.
|
|
30
|
+
|
|
31
|
+
# Raises:
|
|
32
|
+
# ValueError: If an unsupported normalization mode is provided.
|
|
33
|
+
# """
|
|
34
|
+
mode = NormMode.v(mode)
|
|
35
|
+
normalization_methods = {
|
|
36
|
+
NormMode.ZERO_ONE.value: lambda x: (x - np.amin(x)) / (np.amax(x) - np.amin(x)),
|
|
37
|
+
NormMode.MINUS_ONE_ONE.value: lambda x: 2
|
|
38
|
+
* ((x - np.amin(x)) / (np.amax(x) - np.amin(x)))
|
|
39
|
+
- 1,
|
|
40
|
+
NormMode.SUM_ABS.value: lambda x: x / np.abs(x).sum(),
|
|
41
|
+
NormMode.SUM.value: lambda x: x / x.sum(),
|
|
42
|
+
NormMode.EUCLIDEAN.value: lambda x: x / np.sqrt((x**2).sum()),
|
|
43
|
+
NormMode.MEAN_STD.value: lambda x: (x - x.mean()) / x.std(),
|
|
44
|
+
NormMode.MAX.value: lambda x: x / x.max(),
|
|
45
|
+
}
|
|
46
|
+
if mode not in normalization_methods.keys():
|
|
47
|
+
raise ValueError(f"Unsupported normalization mode: {mode}")
|
|
48
|
+
return normalization_methods[mode](array)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def density_normalization(
|
|
52
|
+
x: np.ndarray,
|
|
53
|
+
y: np.ndarray,
|
|
54
|
+
num_points: int = None,
|
|
55
|
+
distance_estimation: Literal["median", "mean", "min", "max"] = "median",
|
|
56
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
57
|
+
"""
|
|
58
|
+
Redistributes the x and y coordinates to have evenly spaced x-values while retaining original points.
|
|
59
|
+
|
|
60
|
+
Parameters:
|
|
61
|
+
x (np.ndarray): Uneven, unsorted x coordinates.
|
|
62
|
+
y (np.ndarray): Corresponding y values.
|
|
63
|
+
num_points (int, optional): Total number of points in the final grid.
|
|
64
|
+
Must be greater than or equal to the number of unique original x values.
|
|
65
|
+
If None, it is estimated based on the specified distance estimation method.
|
|
66
|
+
distance_estimation (Literal["median", "mean", "min", "max"], optional):
|
|
67
|
+
Method to estimate the spacing between points when num_points is not provided.
|
|
68
|
+
Options include "median", "mean", "min", "max". Defaults to "median".
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Tuple[np.ndarray, np.ndarray]:
|
|
72
|
+
- x_new: Evenly spaced x coordinates including original x points.
|
|
73
|
+
- y_new: Corresponding interpolated y values."""
|
|
74
|
+
# Convert inputs to numpy arrays
|
|
75
|
+
x = np.array(x)
|
|
76
|
+
y = np.array(y)
|
|
77
|
+
|
|
78
|
+
# if x is already evenly spaced, return the input
|
|
79
|
+
if len(np.unique(np.diff(x))) == 1:
|
|
80
|
+
return x, y
|
|
81
|
+
|
|
82
|
+
# Sort the data based on x values
|
|
83
|
+
sorted_indices = np.argsort(x)
|
|
84
|
+
x_sorted = x[sorted_indices]
|
|
85
|
+
y_sorted = y[sorted_indices]
|
|
86
|
+
|
|
87
|
+
# Handle duplicate x-values by averaging their y-values
|
|
88
|
+
unique_x, indices, counts = np.unique(
|
|
89
|
+
x_sorted, return_index=True, return_counts=True
|
|
90
|
+
)
|
|
91
|
+
y_unique = np.array([y_sorted[i : i + c].mean() for i, c in zip(indices, counts)])
|
|
92
|
+
|
|
93
|
+
# Determine number of points for the evenly spaced grid
|
|
94
|
+
|
|
95
|
+
if num_points is None:
|
|
96
|
+
distance_func = {
|
|
97
|
+
"mean": np.mean,
|
|
98
|
+
"median": np.median,
|
|
99
|
+
"min": np.min,
|
|
100
|
+
"max": np.max,
|
|
101
|
+
}.get(distance_estimation, np.median) # Default to median if key not found
|
|
102
|
+
|
|
103
|
+
unique_diffs = np.diff(unique_x)
|
|
104
|
+
|
|
105
|
+
# Handle the case where there is only one unique_x point
|
|
106
|
+
if len(unique_diffs) == 0:
|
|
107
|
+
num_points = 1
|
|
108
|
+
else:
|
|
109
|
+
estimated_diff = distance_func(unique_diffs)
|
|
110
|
+
total_range = unique_x.max() - unique_x.min()
|
|
111
|
+
|
|
112
|
+
# Prevent division by zero
|
|
113
|
+
if estimated_diff == 0:
|
|
114
|
+
num_points = len(unique_x)
|
|
115
|
+
else:
|
|
116
|
+
num_points = int(total_range / estimated_diff)
|
|
117
|
+
else:
|
|
118
|
+
num_points = int(num_points)
|
|
119
|
+
# Create evenly spaced x-values
|
|
120
|
+
x_new = np.linspace(unique_x.min(), unique_x.max(), num_points)
|
|
121
|
+
|
|
122
|
+
# Create an interpolation function
|
|
123
|
+
interp_func = interp1d(unique_x, y_unique, kind="linear", fill_value="extrapolate")
|
|
124
|
+
|
|
125
|
+
# Compute the interpolated y-values
|
|
126
|
+
y_new = interp_func(x_new)
|
|
127
|
+
|
|
128
|
+
return x_new, y_new
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@NodeDecorator(
|
|
132
|
+
id="span.norm.density",
|
|
133
|
+
name="Density normalization node",
|
|
134
|
+
description="Redistributes the x and y coordinates to have evenly spaced x-values while retaining original points.",
|
|
135
|
+
outputs=[
|
|
136
|
+
{
|
|
137
|
+
"name": "x_new",
|
|
138
|
+
"description": "Evenly spaced x coordinates including original x points.",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "y_new",
|
|
142
|
+
"description": "Corresponding interpolated y values.",
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
)
|
|
146
|
+
@controlled_wrapper(density_normalization, wrapper_attribute="__fnwrapped__")
|
|
147
|
+
def density_normalization_node(
|
|
148
|
+
x: np.ndarray,
|
|
149
|
+
y: np.ndarray,
|
|
150
|
+
num_points: int = None,
|
|
151
|
+
distance_estimation: Literal["median", "mean", "min", "max"] = "median",
|
|
152
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
153
|
+
return density_normalization(x, y, num_points, distance_estimation)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
NORM_NODE_SHELF = Shelf(
|
|
157
|
+
nodes=[_norm, density_normalization_node],
|
|
158
|
+
subshelves=[],
|
|
159
|
+
name="Normalization",
|
|
160
|
+
description="Normalization of the spectra",
|
|
161
|
+
)
|
|
@@ -13,6 +13,7 @@ import plotly.graph_objs as go
|
|
|
13
13
|
from plotly.subplots import make_subplots
|
|
14
14
|
import re
|
|
15
15
|
from dataclasses import dataclass
|
|
16
|
+
from .normalization import density_normalization
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
@dataclass
|
|
@@ -218,13 +219,36 @@ def peak_finder(
|
|
|
218
219
|
distance = float(distance) if distance is not None else None
|
|
219
220
|
prominence = float(prominence) if prominence is not None else None
|
|
220
221
|
width = float(width) if width is not None else None
|
|
221
|
-
wlen =
|
|
222
|
+
wlen = float(wlen) if wlen is not None else None
|
|
222
223
|
rel_height = float(rel_height)
|
|
223
|
-
plateau_size =
|
|
224
|
+
plateau_size = float(plateau_size) if plateau_size is not None else None
|
|
224
225
|
|
|
225
226
|
height = 0.05 * np.max(y_array) if height is None else height
|
|
226
227
|
noise_level = 5000 if noise_level is None else noise_level
|
|
227
228
|
|
|
229
|
+
if x_array is not None:
|
|
230
|
+
x_array, y_array = density_normalization(
|
|
231
|
+
x_array,
|
|
232
|
+
y_array,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
xdiff = x_array[1] - x_array[0]
|
|
236
|
+
# if x is given width is based on the x scale and has to be converted to index
|
|
237
|
+
if width is not None:
|
|
238
|
+
width = width / xdiff
|
|
239
|
+
|
|
240
|
+
# same for distance
|
|
241
|
+
if distance is not None:
|
|
242
|
+
distance = distance / xdiff
|
|
243
|
+
|
|
244
|
+
# same for wlen
|
|
245
|
+
if wlen is not None:
|
|
246
|
+
wlen = wlen / xdiff
|
|
247
|
+
|
|
248
|
+
# same for plateau_size
|
|
249
|
+
if plateau_size is not None:
|
|
250
|
+
plateau_size = plateau_size / xdiff
|
|
251
|
+
|
|
228
252
|
# Make a copy of the input array
|
|
229
253
|
y_array_copy = np.copy(y_array)
|
|
230
254
|
|
|
@@ -235,8 +259,8 @@ def peak_finder(
|
|
|
235
259
|
prominence=prominence,
|
|
236
260
|
height=height,
|
|
237
261
|
distance=distance,
|
|
238
|
-
width=width,
|
|
239
|
-
wlen=wlen,
|
|
262
|
+
width=max(1, width) if width is not None else None,
|
|
263
|
+
wlen=int(wlen) if wlen is not None else None,
|
|
240
264
|
rel_height=rel_height,
|
|
241
265
|
plateau_size=plateau_size,
|
|
242
266
|
)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
from funcnodes import NodeDecorator, Shelf
|
|
2
|
-
import numpy as np
|
|
3
|
-
|
|
4
|
-
import funcnodes as fn
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class NormMode(fn.DataEnum):
|
|
8
|
-
ZERO_ONE = "zero_one"
|
|
9
|
-
MINUS_ONE_ONE = "minus_one_one"
|
|
10
|
-
SUM_ABS = "sum_abs"
|
|
11
|
-
SUM = "sum"
|
|
12
|
-
EUCLIDEAN = "euclidean"
|
|
13
|
-
MEAN_STD = "mean_std"
|
|
14
|
-
MAX = "max"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@NodeDecorator(id="span.basics.norm", name="Normalization node")
|
|
18
|
-
def _norm(array: np.ndarray, mode: NormMode = NormMode.ZERO_ONE) -> np.ndarray:
|
|
19
|
-
# """
|
|
20
|
-
# Apply different normalizations to the array.
|
|
21
|
-
|
|
22
|
-
# Args:
|
|
23
|
-
# array (np.ndarray): The input array to be normalized.
|
|
24
|
-
# mode (NormMode): The normalization mode to apply. Defaults to NormMode.ZERO_ONE.
|
|
25
|
-
|
|
26
|
-
# Returns:
|
|
27
|
-
# np.ndarray: The normalized array.
|
|
28
|
-
|
|
29
|
-
# Raises:
|
|
30
|
-
# ValueError: If an unsupported normalization mode is provided.
|
|
31
|
-
# """
|
|
32
|
-
mode = NormMode.v(mode)
|
|
33
|
-
normalization_methods = {
|
|
34
|
-
NormMode.ZERO_ONE.value: lambda x: (x - np.amin(x)) / (np.amax(x) - np.amin(x)),
|
|
35
|
-
NormMode.MINUS_ONE_ONE.value: lambda x: 2
|
|
36
|
-
* ((x - np.amin(x)) / (np.amax(x) - np.amin(x)))
|
|
37
|
-
- 1,
|
|
38
|
-
NormMode.SUM_ABS.value: lambda x: x / np.abs(x).sum(),
|
|
39
|
-
NormMode.SUM.value: lambda x: x / x.sum(),
|
|
40
|
-
NormMode.EUCLIDEAN.value: lambda x: x / np.sqrt((x**2).sum()),
|
|
41
|
-
NormMode.MEAN_STD.value: lambda x: (x - x.mean()) / x.std(),
|
|
42
|
-
NormMode.MAX.value: lambda x: x / x.max(),
|
|
43
|
-
}
|
|
44
|
-
if mode not in normalization_methods.keys():
|
|
45
|
-
raise ValueError(f"Unsupported normalization mode: {mode}")
|
|
46
|
-
return normalization_methods[mode](array)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
NORM_NODE_SHELF = Shelf(
|
|
50
|
-
nodes=[_norm],
|
|
51
|
-
subshelves=[],
|
|
52
|
-
name="Normalization",
|
|
53
|
-
description="Normalization of the spectra",
|
|
54
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|