manual.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. __all__ = ["define_prototype"]
  2. import numpy as np
  3. from scipy.signal import gaussian
  4. def define_prototype(
  5. sig1: float = 6.3,
  6. sig2: float = 13.6,
  7. baseline: float = 0.3,
  8. prominance: float = 0.25,
  9. apex_location:float | None = None,
  10. window_size: int=100,
  11. noise: bool=False,
  12. return_params: bool = False
  13. ) -> np.ndarray | tuple[np.ndarray, tuple[float, float, float, float, int]]:
  14. """
  15. Define a manual prototype composed of two Gaussians.
  16. The default parameters are set to the values used in the paper.
  17. They are learned from the data and are used to define the prototype.
  18. We compute the onset and offset as 3 * sig1 and 3 * sig2, respectively.
  19. If these values are not inside the window, the definition will raise an error.
  20. Parameters:
  21. sig1 (float): Standard deviation of the first Gaussian.
  22. sig2 (float): Standard deviation of the second Gaussian.
  23. baseline (float): Baseline value of the prototype.
  24. prominance (float): Prominence of the Gaussians.
  25. apex_location (int | None): Location of the apex of the prototype. If None, it is set to 40% of the window size.
  26. window_size (int): Size of the window.
  27. noise (bool): Flag indicating whether to add noise to the prototype.
  28. return_params (bool): Flag indicating whether to return the prototype parameters.
  29. Returns:
  30. numpy.ndarray: The manual prototype.
  31. """
  32. if apex_location is None:
  33. # default 40% of the window size
  34. # this how the prototype is defined in the paper
  35. apex_location = 0.4
  36. apex_location = int(window_size * apex_location)
  37. onset_x = apex_location - 3 * sig1
  38. offset_x = apex_location + 3 * sig2
  39. if onset_x < 0 or offset_x > window_size:
  40. raise ValueError("Onset and offset are not inside the window. Choose different parameters.")
  41. # TODO replace with scipy.stats.norm.pdf
  42. y1 = -prominance * gaussian(window_size*2, std=sig1) + baseline
  43. y2 = -prominance * gaussian(window_size*2, std=sig2) + baseline
  44. y = np.append(y1[:window_size], y2[window_size:])
  45. if noise:
  46. y = y + _noise_fct(0.02, window_size)
  47. y = y[window_size - apex_location: 2*window_size - apex_location]
  48. if return_params:
  49. return y, (sig1, sig2, baseline, prominance, apex_location)
  50. return y
  51. def _noise_fct(
  52. noise_std: float,
  53. window_size=10
  54. ) -> np.ndarray:
  55. "Random noise based on learned data."
  56. # create custom random generator to not interfere with other random calls!
  57. rand_generator = np.random.RandomState(0)
  58. return (rand_generator.random(2*window_size) * 2 - 1) * noise_std