funcnodes-span 0.1.6__tar.gz → 0.1.8__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.6 → funcnodes_span-0.1.8}/PKG-INFO +1 -1
- {funcnodes_span-0.1.6 → funcnodes_span-0.1.8}/funcnodes_span/__init__.py +1 -1
- {funcnodes_span-0.1.6 → funcnodes_span-0.1.8}/funcnodes_span/peak_analysis.py +132 -6
- {funcnodes_span-0.1.6 → funcnodes_span-0.1.8}/pyproject.toml +1 -3
- {funcnodes_span-0.1.6 → funcnodes_span-0.1.8}/README.md +0 -0
- {funcnodes_span-0.1.6 → funcnodes_span-0.1.8}/funcnodes_span/normalization.py +0 -0
- {funcnodes_span-0.1.6 → funcnodes_span-0.1.8}/funcnodes_span/smoothing.py +0 -0
|
@@ -2,10 +2,11 @@ from funcnodes import NodeDecorator, Shelf
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from exposedfunctionality import controlled_wrapper
|
|
5
|
-
from typing import Optional, TypedDict, List
|
|
5
|
+
from typing import Optional, TypedDict, List, Tuple
|
|
6
6
|
from scipy.signal import find_peaks
|
|
7
7
|
from scipy.stats import norm
|
|
8
|
-
from scipy import interpolate
|
|
8
|
+
from scipy import signal, interpolate
|
|
9
|
+
from scipy.ndimage import gaussian_filter1d
|
|
9
10
|
import copy
|
|
10
11
|
import lmfit
|
|
11
12
|
import plotly.graph_objs as go
|
|
@@ -179,7 +180,7 @@ def compute_peak_properties(
|
|
|
179
180
|
return peak_properties
|
|
180
181
|
|
|
181
182
|
|
|
182
|
-
@NodeDecorator(id="span.basics.peaks", name="Peak finder
|
|
183
|
+
@NodeDecorator(id="span.basics.peaks", name="Peak finder")
|
|
183
184
|
@controlled_wrapper(find_peaks, wrapper_attribute="__fnwrapped__")
|
|
184
185
|
def peak_finder(
|
|
185
186
|
x_array: np.ndarray,
|
|
@@ -310,14 +311,14 @@ class FittingModel(Enum):
|
|
|
310
311
|
Moffat = "Moffat"
|
|
311
312
|
Pearson4 = "Pearson4"
|
|
312
313
|
Pearson7 = "Pearson7"
|
|
314
|
+
SkewedGaussian = "Skewed Gaussian"
|
|
315
|
+
SkewedVoigt = "Skewed Voigt"
|
|
313
316
|
StudentsT = "StudentsT"
|
|
314
317
|
BreitWigner = "Breit-Wigner"
|
|
315
318
|
LogNormal = "Log-Normal"
|
|
316
319
|
DampedOscillator = "Damped Oscillator"
|
|
317
320
|
DampedHarmonicOscillator = "Damped Harmonic Oscillator"
|
|
318
321
|
ExponentialGaussian = "Exponential Gaussian"
|
|
319
|
-
SkewedGaussian = "Skewed Gaussian"
|
|
320
|
-
SkewedVoigt = "Skewed Voigt"
|
|
321
322
|
ThermalDistribution = "Thermal Distribution"
|
|
322
323
|
Doniach = "Doniach"
|
|
323
324
|
PowerLaw = "Power Law"
|
|
@@ -332,6 +333,25 @@ class FittingModel(Enum):
|
|
|
332
333
|
return cls.Gaussian.value
|
|
333
334
|
|
|
334
335
|
|
|
336
|
+
@NodeDecorator(
|
|
337
|
+
"span.basics.interpolation_1d",
|
|
338
|
+
name="Interpolation 1D",
|
|
339
|
+
outputs=[
|
|
340
|
+
{
|
|
341
|
+
"name": "x_interpolated",
|
|
342
|
+
},
|
|
343
|
+
{"name": "y_interpolated"},
|
|
344
|
+
],
|
|
345
|
+
)
|
|
346
|
+
def interpolation_1d(
|
|
347
|
+
x: np.array, y: np.array, multipled_by: int = 10
|
|
348
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
349
|
+
f_interpol = interpolate.interp1d(x, y)
|
|
350
|
+
x_interpolated = np.linspace(x[0], x[-1], num=len(x) * multipled_by, endpoint=True)
|
|
351
|
+
y_interpolated = f_interpol(x_interpolated)
|
|
352
|
+
return x_interpolated, y_interpolated
|
|
353
|
+
|
|
354
|
+
|
|
335
355
|
@NodeDecorator(id="span.basics.fit", name="Fit 1D")
|
|
336
356
|
def fit_1D(
|
|
337
357
|
x_array: np.ndarray,
|
|
@@ -469,6 +489,109 @@ def fit_1D(
|
|
|
469
489
|
return peak_properties_list
|
|
470
490
|
|
|
471
491
|
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
@NodeDecorator(
|
|
496
|
+
"span.basics.force_fit",
|
|
497
|
+
name="Advanced peak finder",
|
|
498
|
+
)
|
|
499
|
+
def force_peak_finder(
|
|
500
|
+
x: np.array,
|
|
501
|
+
y: np.array,
|
|
502
|
+
basic_peaks: PeakProperties,
|
|
503
|
+
) -> List[PeakProperties]:
|
|
504
|
+
# """
|
|
505
|
+
# Identify and return the two peaks around the main peak in the given peaks dictionary.
|
|
506
|
+
|
|
507
|
+
# Parameters:
|
|
508
|
+
# - peaks (dict): A dictionary containing peak information.
|
|
509
|
+
# It should have the keys 'peaks' and 'data'.
|
|
510
|
+
# 'peaks' should contain a list of dictionaries with keys 'Initial index', 'Index', and 'Ending index'.
|
|
511
|
+
# 'data' should contain arrays 'x' and 'y'.
|
|
512
|
+
|
|
513
|
+
# Returns:
|
|
514
|
+
# - dict: A dictionary containing information about the two identified peaks.
|
|
515
|
+
# """
|
|
516
|
+
peaks = copy.deepcopy(basic_peaks)
|
|
517
|
+
|
|
518
|
+
main_peak_i_index = peaks["i_index"]
|
|
519
|
+
main_peak_r_index = peaks["index"]
|
|
520
|
+
main_peak_f_index = peaks["f_index"]
|
|
521
|
+
y_array = y
|
|
522
|
+
x_array = x
|
|
523
|
+
# Calculate first and second derivatives
|
|
524
|
+
y_array_p = np.diff(y_array, 1, -1, y_array[0])
|
|
525
|
+
y_array_pp = np.diff(y_array, 2, -1, y_array[0:2])
|
|
526
|
+
# Smooth derivatives using Gaussian filter
|
|
527
|
+
y_array_p = gaussian_filter1d(y_array_p, 5)
|
|
528
|
+
y_array_pp = gaussian_filter1d(y_array_pp, 5)
|
|
529
|
+
|
|
530
|
+
maxx = [main_peak_r_index]
|
|
531
|
+
minn = [main_peak_i_index, main_peak_f_index]
|
|
532
|
+
# Find local maxima and minima of derivatives
|
|
533
|
+
max_p = signal.argrelmax(y_array_p)[0]
|
|
534
|
+
min_p = signal.argrelmin(y_array_p)[0]
|
|
535
|
+
max_pp = signal.argrelmax(y_array_pp)[0]
|
|
536
|
+
min_pp = signal.argrelmin(y_array_pp)[0]
|
|
537
|
+
|
|
538
|
+
# main_peak_i_index = peaks["i_index"]
|
|
539
|
+
# main_peak_r_index = peaks["index"]
|
|
540
|
+
# main_peak_f_index = peaks["f_index"]
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
# Determine which peak is on the left and right side of the main peak
|
|
544
|
+
if (
|
|
545
|
+
x_array[main_peak_r_index] - x_array[main_peak_i_index]
|
|
546
|
+
> x_array[main_peak_f_index] - x_array[main_peak_r_index]
|
|
547
|
+
): # seond peak is in the leftside of the max peak #TODO: fix this
|
|
548
|
+
common_point = max([num for num in max_pp if num < main_peak_r_index])
|
|
549
|
+
|
|
550
|
+
print("Left convoluted")
|
|
551
|
+
peak1 = {
|
|
552
|
+
"I.Index": main_peak_i_index,
|
|
553
|
+
"R.Index": max(
|
|
554
|
+
[num for num in min_p if num < main_peak_r_index]
|
|
555
|
+
), # TODO: fix this
|
|
556
|
+
"F.Index": common_point,
|
|
557
|
+
}
|
|
558
|
+
peak2 = {
|
|
559
|
+
"I.Index": common_point,
|
|
560
|
+
"R.Index": main_peak_r_index,
|
|
561
|
+
"F.Index": main_peak_f_index,
|
|
562
|
+
}
|
|
563
|
+
else:
|
|
564
|
+
common_point = next((x for x in max_pp if x > main_peak_r_index), None)
|
|
565
|
+
print("Right convoluted")
|
|
566
|
+
peak1 = {
|
|
567
|
+
"I.Index": main_peak_i_index,
|
|
568
|
+
"R.Index": main_peak_r_index,
|
|
569
|
+
"F.Index": common_point,
|
|
570
|
+
}
|
|
571
|
+
peak2 = {
|
|
572
|
+
"I.Index": common_point,
|
|
573
|
+
"R.Index": next((x for x in max_p if x > main_peak_r_index), None),
|
|
574
|
+
"F.Index": main_peak_f_index,
|
|
575
|
+
}
|
|
576
|
+
peak_lst = []
|
|
577
|
+
peak_lst.append([peak1["I.Index"], peak1["R.Index"], peak1["F.Index"]])
|
|
578
|
+
peak_lst.append([peak2["I.Index"], peak2["R.Index"], peak2["F.Index"]])
|
|
579
|
+
peak_properties_list = []
|
|
580
|
+
for peak_nr, peak in enumerate(peak_lst):
|
|
581
|
+
peak_properties = compute_peak_properties(
|
|
582
|
+
x_array=x_array,
|
|
583
|
+
y_array=y_array,
|
|
584
|
+
peak_indices=peak,
|
|
585
|
+
peak_nr=peak_nr,
|
|
586
|
+
is_force_fitted=True,
|
|
587
|
+
)
|
|
588
|
+
peak_properties_list.append(peak_properties)
|
|
589
|
+
|
|
590
|
+
return peak_properties_list
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
|
|
472
595
|
# Define a mapping from "C0", "C1", etc., to CSS color names
|
|
473
596
|
color_map = {
|
|
474
597
|
"C0": "blue",
|
|
@@ -487,6 +610,9 @@ color_map = {
|
|
|
487
610
|
@NodeDecorator(id="span.basics.fit.plot", name="Plot fit 1D")
|
|
488
611
|
def plot_fitted_peaks(peaks: List[PeakProperties]) -> go.Figure:
|
|
489
612
|
peak = peaks[0]
|
|
613
|
+
if not peak['_is_fitted']:
|
|
614
|
+
raise ValueError("No fitting information is available.")
|
|
615
|
+
|
|
490
616
|
x = peak["fitting_info"]["userkws"]["x"]
|
|
491
617
|
# Extract data from peaks
|
|
492
618
|
y = peak["fitting_info"]["data"]
|
|
@@ -549,7 +675,7 @@ def plot_fitted_peaks(peaks: List[PeakProperties]) -> go.Figure:
|
|
|
549
675
|
|
|
550
676
|
|
|
551
677
|
PEAKS_NODE_SHELF = Shelf(
|
|
552
|
-
nodes=[peak_finder, fit_1D, plot_fitted_peaks],
|
|
678
|
+
nodes=[peak_finder, interpolation_1d, force_peak_finder, fit_1D, plot_fitted_peaks],
|
|
553
679
|
subshelves=[],
|
|
554
680
|
name="Peak analysis",
|
|
555
681
|
description="Tools for the peak analysis of the spectra",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "funcnodes-span"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.8"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["Kourosh Rezaei <kouroshrezaei90@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -8,12 +8,10 @@ readme = "README.md"
|
|
|
8
8
|
[tool.poetry.dependencies]
|
|
9
9
|
python = "^3.11"
|
|
10
10
|
scipy = "^1.14.0"
|
|
11
|
-
|
|
12
11
|
lmfit = "^1.3.2"
|
|
13
12
|
funcnodes = "^0.2.21"
|
|
14
13
|
funcnodes_numpy = "^0.1.57"
|
|
15
14
|
funcnodes_pandas = "^0.1.9"
|
|
16
|
-
|
|
17
15
|
funcnodes_plotly = "^0.1.7"
|
|
18
16
|
|
|
19
17
|
pooch = "^1.8.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|