funcnodes-span 0.1.7__tar.gz → 0.1.9__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.1.7
3
+ Version: 0.1.9
4
4
  Summary:
5
5
  Author: Kourosh Rezaei
6
6
  Author-email: kouroshrezaei90@gmail.com
@@ -4,7 +4,7 @@ from .normalization import NORM_NODE_SHELF as NORM
4
4
  from .smoothing import SMOOTH_NODE_SHELF as SMOOTH
5
5
  from .peak_analysis import PEAKS_NODE_SHELF as PEAK
6
6
 
7
- __version__ = "0.1.7"
7
+ __version__ = "0.1.9"
8
8
 
9
9
  NODE_SHELF = fn.Shelf(
10
10
  name="Spectral Analysis",
@@ -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
@@ -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,113 @@ 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: List[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
+ if len(basic_peaks) != 1:
517
+ raise ValueError("This method accepts only one main peak as an input.")
518
+ elif len(basic_peaks) == 0:
519
+ raise ValueError("No peaks found.")
520
+ peaks = copy.deepcopy(basic_peaks)
521
+
522
+ main_peak_i_index = peaks["i_index"]
523
+ main_peak_r_index = peaks["index"]
524
+ main_peak_f_index = peaks["f_index"]
525
+ y_array = y
526
+ x_array = x
527
+ # Calculate first and second derivatives
528
+ y_array_p = np.diff(y_array, 1, -1, y_array[0])
529
+ y_array_pp = np.diff(y_array, 2, -1, y_array[0:2])
530
+ # Smooth derivatives using Gaussian filter
531
+ y_array_p = gaussian_filter1d(y_array_p, 5)
532
+ y_array_pp = gaussian_filter1d(y_array_pp, 5)
533
+
534
+ maxx = [main_peak_r_index]
535
+ minn = [main_peak_i_index, main_peak_f_index]
536
+ # Find local maxima and minima of derivatives
537
+ max_p = signal.argrelmax(y_array_p)[0]
538
+ min_p = signal.argrelmin(y_array_p)[0]
539
+ max_pp = signal.argrelmax(y_array_pp)[0]
540
+ min_pp = signal.argrelmin(y_array_pp)[0]
541
+
542
+ # main_peak_i_index = peaks["i_index"]
543
+ # main_peak_r_index = peaks["index"]
544
+ # main_peak_f_index = peaks["f_index"]
545
+
546
+
547
+ # Determine which peak is on the left and right side of the main peak
548
+ if (
549
+ x_array[main_peak_r_index] - x_array[main_peak_i_index]
550
+ > x_array[main_peak_f_index] - x_array[main_peak_r_index]
551
+ ): # seond peak is in the leftside of the max peak #TODO: fix this
552
+ common_point = max([num for num in max_pp if num < main_peak_r_index])
553
+
554
+ print("Left convoluted")
555
+ peak1 = {
556
+ "I.Index": main_peak_i_index,
557
+ "R.Index": max(
558
+ [num for num in min_p if num < main_peak_r_index]
559
+ ), # TODO: fix this
560
+ "F.Index": common_point,
561
+ }
562
+ peak2 = {
563
+ "I.Index": common_point,
564
+ "R.Index": main_peak_r_index,
565
+ "F.Index": main_peak_f_index,
566
+ }
567
+ else:
568
+ common_point = next((x for x in max_pp if x > main_peak_r_index), None)
569
+ print("Right convoluted")
570
+ peak1 = {
571
+ "I.Index": main_peak_i_index,
572
+ "R.Index": main_peak_r_index,
573
+ "F.Index": common_point,
574
+ }
575
+ peak2 = {
576
+ "I.Index": common_point,
577
+ "R.Index": next((x for x in max_p if x > main_peak_r_index), None),
578
+ "F.Index": main_peak_f_index,
579
+ }
580
+ peak_lst = []
581
+ peak_lst.append([peak1["I.Index"], peak1["R.Index"], peak1["F.Index"]])
582
+ peak_lst.append([peak2["I.Index"], peak2["R.Index"], peak2["F.Index"]])
583
+ peak_properties_list = []
584
+ for peak_nr, peak in enumerate(peak_lst):
585
+ peak_properties = compute_peak_properties(
586
+ x_array=x_array,
587
+ y_array=y_array,
588
+ peak_indices=peak,
589
+ peak_nr=peak_nr,
590
+ is_force_fitted=True,
591
+ )
592
+ peak_properties_list.append(peak_properties)
593
+
594
+ return peak_properties_list
595
+
596
+
597
+
598
+
472
599
  # Define a mapping from "C0", "C1", etc., to CSS color names
473
600
  color_map = {
474
601
  "C0": "blue",
@@ -487,6 +614,9 @@ color_map = {
487
614
  @NodeDecorator(id="span.basics.fit.plot", name="Plot fit 1D")
488
615
  def plot_fitted_peaks(peaks: List[PeakProperties]) -> go.Figure:
489
616
  peak = peaks[0]
617
+ if not peak['_is_fitted']:
618
+ raise ValueError("No fitting information is available.")
619
+
490
620
  x = peak["fitting_info"]["userkws"]["x"]
491
621
  # Extract data from peaks
492
622
  y = peak["fitting_info"]["data"]
@@ -549,7 +679,7 @@ def plot_fitted_peaks(peaks: List[PeakProperties]) -> go.Figure:
549
679
 
550
680
 
551
681
  PEAKS_NODE_SHELF = Shelf(
552
- nodes=[peak_finder, fit_1D, plot_fitted_peaks],
682
+ nodes=[peak_finder, interpolation_1d, force_peak_finder, fit_1D, plot_fitted_peaks],
553
683
  subshelves=[],
554
684
  name="Peak analysis",
555
685
  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.7"
3
+ version = "0.1.9"
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