DeepPeak 0.0.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.
Files changed (65) hide show
  1. deeppeak-0.0.1/.flake8 +11 -0
  2. deeppeak-0.0.1/.github/dependabot.yml +11 -0
  3. deeppeak-0.0.1/.github/workflows/deploy_PyPi.yml +20 -0
  4. deeppeak-0.0.1/.github/workflows/deploy_anaconda.yml +21 -0
  5. deeppeak-0.0.1/.github/workflows/deploy_coverage.yml +25 -0
  6. deeppeak-0.0.1/.github/workflows/deploy_documentation.yml +24 -0
  7. deeppeak-0.0.1/.gitignore +28 -0
  8. deeppeak-0.0.1/DeepPeak/__init__.py +5 -0
  9. deeppeak-0.0.1/DeepPeak/_version.py +34 -0
  10. deeppeak-0.0.1/DeepPeak/algorithms/__init__.py +1 -0
  11. deeppeak-0.0.1/DeepPeak/algorithms/non_maximum_suppression.py +330 -0
  12. deeppeak-0.0.1/DeepPeak/dataset.py +54 -0
  13. deeppeak-0.0.1/DeepPeak/directories.py +38 -0
  14. deeppeak-0.0.1/DeepPeak/helper.py +50 -0
  15. deeppeak-0.0.1/DeepPeak/machine_learning/__init__.py +0 -0
  16. deeppeak-0.0.1/DeepPeak/machine_learning/classifier/__init__.py +3 -0
  17. deeppeak-0.0.1/DeepPeak/machine_learning/classifier/autoencoder.py +90 -0
  18. deeppeak-0.0.1/DeepPeak/machine_learning/classifier/base.py +219 -0
  19. deeppeak-0.0.1/DeepPeak/machine_learning/classifier/dense.py +63 -0
  20. deeppeak-0.0.1/DeepPeak/machine_learning/classifier/utils.py +250 -0
  21. deeppeak-0.0.1/DeepPeak/machine_learning/classifier/wavenet.py +88 -0
  22. deeppeak-0.0.1/DeepPeak/machine_learning/weights/ROI_128.keras +0 -0
  23. deeppeak-0.0.1/DeepPeak/machine_learning/weights/__init__.py +0 -0
  24. deeppeak-0.0.1/DeepPeak/machine_learning/weights/temporary.keras +0 -0
  25. deeppeak-0.0.1/DeepPeak/signals.py +345 -0
  26. deeppeak-0.0.1/DeepPeak/utils.py +169 -0
  27. deeppeak-0.0.1/DeepPeak/visualization.py +767 -0
  28. deeppeak-0.0.1/DeepPeak.egg-info/PKG-INFO +74 -0
  29. deeppeak-0.0.1/DeepPeak.egg-info/SOURCES.txt +63 -0
  30. deeppeak-0.0.1/DeepPeak.egg-info/dependency_links.txt +1 -0
  31. deeppeak-0.0.1/DeepPeak.egg-info/requires.txt +23 -0
  32. deeppeak-0.0.1/DeepPeak.egg-info/top_level.txt +1 -0
  33. deeppeak-0.0.1/LICENSE +21 -0
  34. deeppeak-0.0.1/PKG-INFO +74 -0
  35. deeppeak-0.0.1/README.rst +11 -0
  36. deeppeak-0.0.1/development/confidence.py +183 -0
  37. deeppeak-0.0.1/development/dev_0.py +107 -0
  38. deeppeak-0.0.1/development/dev_1.py +70 -0
  39. deeppeak-0.0.1/development/train_roi_model.py +113 -0
  40. deeppeak-0.0.1/development/use_roi_model.py +53 -0
  41. deeppeak-0.0.1/docs/Makefile +19 -0
  42. deeppeak-0.0.1/docs/examples/README.rst +137 -0
  43. deeppeak-0.0.1/docs/examples/classifier_autoencoder.py +94 -0
  44. deeppeak-0.0.1/docs/examples/classifier_dense.py +92 -0
  45. deeppeak-0.0.1/docs/examples/classifier_wavenet.py +92 -0
  46. deeppeak-0.0.1/docs/examples/data_generation.py +50 -0
  47. deeppeak-0.0.1/docs/examples/non_maximum_suppression.py +49 -0
  48. deeppeak-0.0.1/docs/images/logo.png +0 -0
  49. deeppeak-0.0.1/docs/make.bat +35 -0
  50. deeppeak-0.0.1/docs/source/_static/default.css +19 -0
  51. deeppeak-0.0.1/docs/source/_static/logo.png +0 -0
  52. deeppeak-0.0.1/docs/source/_static/thumbnail.png +0 -0
  53. deeppeak-0.0.1/docs/source/code.rst +4 -0
  54. deeppeak-0.0.1/docs/source/conf.py +175 -0
  55. deeppeak-0.0.1/docs/source/examples.rst +12 -0
  56. deeppeak-0.0.1/docs/source/gallery/index.rst +256 -0
  57. deeppeak-0.0.1/docs/source/index.rst +13 -0
  58. deeppeak-0.0.1/docs/source/references.rst +3 -0
  59. deeppeak-0.0.1/docs/source/sg_execution_times.rst +49 -0
  60. deeppeak-0.0.1/docs/source/theory.rst +2 -0
  61. deeppeak-0.0.1/meta.yaml +57 -0
  62. deeppeak-0.0.1/pyproject.toml +99 -0
  63. deeppeak-0.0.1/setup.cfg +4 -0
  64. deeppeak-0.0.1/tests/__init__.py +0 -0
  65. deeppeak-0.0.1/tests/classifiers.py +67 -0
deeppeak-0.0.1/.flake8 ADDED
@@ -0,0 +1,11 @@
1
+ [flake8]
2
+ max-line-length = 300
3
+ exclude =
4
+ .git
5
+ docs/build
6
+ docs/source/gallery
7
+ development
8
+
9
+ count = true
10
+ per-file-ignores =
11
+ __init__.py: F403, F401
@@ -0,0 +1,11 @@
1
+ # Specify reviewers for pull requests
2
+
3
+ version: 2
4
+ updates:
5
+ - package-ecosystem: "pip"
6
+ directory: "/"
7
+ target-branch: "master"
8
+ schedule:
9
+ interval: "monthly"
10
+ day: "sunday"
11
+ time: "09:00"
@@ -0,0 +1,20 @@
1
+ name: Deploy to PyPi
2
+
3
+ on:
4
+ push:
5
+ branches: [ "master" ]
6
+ tags:
7
+ - '*'
8
+ pull_request:
9
+ branches: [ "master" ]
10
+
11
+ permissions:
12
+ contents: write
13
+
14
+ jobs:
15
+ pure_package:
16
+ uses: MartinPdeS/MPSActions/.github/workflows/publish_pure_package_to_PyPi.yml@master
17
+ with:
18
+ python-version: "3.11"
19
+ secrets:
20
+ PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,21 @@
1
+ name: Deploy Conda Packages
2
+
3
+ on:
4
+ push:
5
+ branches: [ "master" ]
6
+ tags:
7
+ - '*'
8
+ pull_request:
9
+ branches: [ "master" ]
10
+
11
+ permissions:
12
+ contents: write
13
+
14
+ jobs:
15
+ ManyLinux_x86_64:
16
+ uses: MartinPdeS/MPSActions/.github/workflows/publish_compiled_package_to_anaconda.yml@master
17
+ with:
18
+ python-versions: '["3.11", "3.12"]'
19
+ secrets:
20
+ ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }}
21
+
@@ -0,0 +1,25 @@
1
+ # Simple workflow for deploying static content to GitHub Pages
2
+ name: Deploy coverage
3
+
4
+ on:
5
+ push:
6
+ branches: [ "master" ]
7
+ tags:
8
+ - '*'
9
+ pull_request:
10
+ branches: [ "master" ]
11
+
12
+
13
+ # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
14
+ permissions:
15
+ contents: write
16
+ pages: write
17
+ id-token: write
18
+
19
+ jobs:
20
+ ManyLinux_x86_64:
21
+ uses: MartinPdeS/MPSActions/.github/workflows/publish_coverage.yml@master
22
+ with:
23
+ python-version: "3.11"
24
+ secrets:
25
+ _GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,24 @@
1
+ # Simple workflow for deploying static content to GitHub Pages
2
+ name: Deploy documentation to github page
3
+
4
+ on:
5
+ push:
6
+ branches: [ "master" ]
7
+ tags:
8
+ - '*'
9
+ pull_request:
10
+ branches: [ "master" ]
11
+
12
+
13
+ # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
14
+ permissions:
15
+ contents: write
16
+ pages: write
17
+ id-token: write
18
+
19
+ jobs:
20
+ ManyLinux_x86_64:
21
+ uses: MartinPdeS/MPSActions/.github/workflows/publish_documentation.yml@master
22
+ with:
23
+ python-version: "3.11"
24
+ package-name: "DeepPeak"
@@ -0,0 +1,28 @@
1
+ # General patterns
2
+ *.DS_store
3
+ *.ipynb_checkpoints*
4
+ *__pycache__*
5
+ *.so
6
+ *.a
7
+ .coverage*
8
+ *.pytest_cache
9
+ htmlcov
10
+ dist
11
+ build
12
+ *.egg*
13
+ *.whl
14
+ CMakeCache.txt
15
+ CMakeFiles
16
+ install_manifest.txt
17
+ cmake_install.cmake
18
+ wheelhouse
19
+
20
+ # Docs-specific patterns
21
+ docs/source/gallery/*
22
+ !docs/source/gallery/index.rst
23
+ PyOptik/data/**/test*
24
+ PyOptik/data/**/example*
25
+
26
+ # Override for documentation_build
27
+ !documentation_build
28
+ !documentation_build/**
@@ -0,0 +1,5 @@
1
+ try:
2
+ from ._version import version as __version__ # noqa: F401
3
+
4
+ except ImportError:
5
+ __version__ = "0.0.0"
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.0.1'
32
+ __version_tuple__ = version_tuple = (0, 0, 1)
33
+
34
+ __commit_id__ = commit_id = 'g39ce84fdb'
@@ -0,0 +1 @@
1
+ from .non_maximum_suppression import NonMaximumSuppression
@@ -0,0 +1,330 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ from numpy.typing import NDArray
5
+ import matplotlib.pyplot as plt
6
+
7
+
8
+ class NonMaximumSuppression:
9
+ r"""
10
+ Detect up to three equal-width Gaussian pulses in a one-dimensional signal.
11
+
12
+ The detector operates in two stages:
13
+
14
+ 1. **Matched filtering**
15
+ The input signal is correlated with a unit-energy Gaussian kernel.
16
+
17
+ 2. **Non-maximum suppression**
18
+ Candidate peaks are selected as local maxima above a threshold.
19
+
20
+ .. math::
21
+
22
+ y(t) = \sum_{k=1}^A a_k \exp\!\left(-\frac{(t - \mu_k)^2}{2\sigma^2}\right) + \eta(t)
23
+
24
+ where all pulses share the same width :math:`\sigma`.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ gaussian_sigma: float,
30
+ *,
31
+ threshold: float | str = "auto",
32
+ minimum_separation: float | None = None,
33
+ maximum_number_of_pulses: int = 3,
34
+ kernel_truncation_radius_in_sigmas: float = 3.5,
35
+ ) -> None:
36
+ r"""
37
+ Parameters
38
+ ----------
39
+ gaussian_sigma : float
40
+ The known common Gaussian standard deviation :math:`\sigma`.
41
+ threshold : float | "auto"
42
+ Threshold on the matched-filter output.
43
+ If ``"auto"``, it is set to :math:`4.5 \,\hat\sigma_n` where
44
+ :math:`\hat\sigma_n` is a robust noise estimate.
45
+ minimum_separation : float | None
46
+ Minimum allowed peak separation in time units.
47
+ Defaults to :math:`\sigma` if None.
48
+ maximum_number_of_pulses : int
49
+ Maximum number of pulses to return (1-N).
50
+ kernel_truncation_radius_in_sigmas : float
51
+ Radius of the Gaussian FIR kernel in multiples of :math:`\sigma`.
52
+ """
53
+ self.gaussian_sigma = float(gaussian_sigma)
54
+ self.threshold = threshold
55
+ self.minimum_separation = minimum_separation
56
+ self.maximum_number_of_pulses = int(maximum_number_of_pulses)
57
+ self.kernel_truncation_radius_in_sigmas = float(kernel_truncation_radius_in_sigmas)
58
+
59
+ # Results after detection (coarse, no quadratic refinement)
60
+ self.gaussian_kernel_: NDArray[np.float64] | None = None
61
+ self.matched_filter_output_: NDArray[np.float64] | None = None
62
+ self.peak_indices_: NDArray[np.int_] | None = None
63
+ self.peak_times_: NDArray[np.float64] | None = None
64
+ self.peak_heights_: NDArray[np.float64] | None = None
65
+ self.threshold_used_: float | None = None
66
+ self.suppression_half_window_in_samples_: int | None = None
67
+ self.results: dict[str, object] | None = None
68
+
69
+ def run(self, time_samples: NDArray[np.float64], signal: NDArray[np.float64]) -> dict[str, object]:
70
+ r"""
71
+ Run the detection pipeline (matched filter + non-maximum suppression).
72
+
73
+ Parameters
74
+ ----------
75
+ time_samples : array
76
+ Uniform sample times :math:`t[n]` with spacing :math:`\Delta t`.
77
+ signal : array
78
+ Input signal samples :math:`y[n]`.
79
+
80
+ Returns
81
+ -------
82
+ dict
83
+ Results including coarse peak times (sample-aligned), their heights,
84
+ the matched filter output, and configuration used.
85
+
86
+ Notes
87
+ -----
88
+ Matched filter correlation:
89
+
90
+ .. math::
91
+
92
+ r[n] = \sum_m y[m] \, g[m-n]
93
+
94
+ Non-maximum suppression keeps peaks :math:`n` such that
95
+
96
+ .. math::
97
+
98
+ r[n] = \max_{|k-n|\leq W} r[k], \qquad r[n] \ge \tau.
99
+ """
100
+ assert signal.ndim == 1 and time_samples.ndim == 1 and len(signal) == len(time_samples), \
101
+ "signal and time_samples must be one-dimensional arrays of the same length"
102
+
103
+ sample_interval = float(time_samples[1] - time_samples[0])
104
+
105
+ # 1) Build unit-energy Gaussian kernel and apply matched filter (correlation)
106
+ gaussian_kernel = self._build_gaussian_kernel(
107
+ sample_interval=sample_interval,
108
+ gaussian_sigma=self.gaussian_sigma,
109
+ truncation_radius_in_sigmas=self.kernel_truncation_radius_in_sigmas,
110
+ )
111
+ matched_filter_output = self._correlate(signal, gaussian_kernel)
112
+
113
+ # 2) Determine suppression window and threshold
114
+ if self.minimum_separation is None:
115
+ minimum_separation = self.gaussian_sigma
116
+ else:
117
+ minimum_separation = self.minimum_separation
118
+
119
+ suppression_half_window_in_samples = int(max(1, np.round(minimum_separation / sample_interval / 2.0)))
120
+
121
+ if self.threshold == "auto":
122
+ noise_sigma = self._estimate_noise_std(matched_filter_output)
123
+ threshold_value = 4.5 * noise_sigma
124
+ else:
125
+ threshold_value = float(self.threshold)
126
+
127
+ # 3) Non-maximum suppression (coarse indices only)
128
+ peak_indices = self._non_maximum_suppression(
129
+ values=matched_filter_output,
130
+ half_window=suppression_half_window_in_samples,
131
+ threshold=threshold_value,
132
+ max_peaks=self.maximum_number_of_pulses,
133
+ )
134
+
135
+ # Coarse peak times and heights (no sub-sample refinement)
136
+ peak_times = time_samples[peak_indices] if peak_indices.size else np.empty(0)
137
+ peak_heights = matched_filter_output[peak_indices] if peak_indices.size else np.empty(0)
138
+
139
+ # Save results on the instance
140
+ self.gaussian_kernel_ = gaussian_kernel
141
+ self.matched_filter_output_ = matched_filter_output
142
+ self.peak_indices_ = np.sort(peak_indices)
143
+ order = np.argsort(peak_times)
144
+ self.peak_times_ = peak_times[order]
145
+ self.peak_heights_ = peak_heights[order]
146
+ self.threshold_used_ = float(threshold_value)
147
+ self.suppression_half_window_in_samples_ = int(suppression_half_window_in_samples)
148
+
149
+ self.results = {
150
+ "signal": signal,
151
+ "time_samples": time_samples,
152
+ "peak_indices": self.peak_indices_,
153
+ "peak_times": self.peak_times_,
154
+ "peak_heights": self.peak_heights_,
155
+ "matched_filter_output": matched_filter_output,
156
+ "gaussian_kernel": gaussian_kernel,
157
+ "threshold_used": float(threshold_value),
158
+ "suppression_half_window_in_samples": int(suppression_half_window_in_samples),
159
+ }
160
+ return self.results
161
+
162
+ def plot(self) -> None:
163
+ r"""
164
+ Plot the input signal, matched-filter output, and coarse peak locations.
165
+ """
166
+ from MPSPlots.styles import mps as plot_style
167
+ if not getattr(self, "results", None):
168
+ raise ValueError("Computation not done yet, use .run() first.")
169
+
170
+ with plt.style.context(plot_style):
171
+ t = self.results["time_samples"]
172
+ y = self.results["signal"]
173
+ r = self.results["matched_filter_output"]
174
+ peaks_t = self.results["peak_times"]
175
+
176
+ plt.figure()
177
+ plt.plot(t, y, label="signal y(t)")
178
+ plt.plot(t, r, label="matched filter output r(t)")
179
+ for m in peaks_t:
180
+ plt.axvline(m, linestyle="--", alpha=0.6, label=None)
181
+ plt.title("Equal-width Gaussian pulse detection (coarse)")
182
+ plt.xlabel("t")
183
+ plt.ylabel("amplitude")
184
+ plt.legend()
185
+ plt.show()
186
+
187
+ # ---------------- Static helper methods ----------------
188
+
189
+ @staticmethod
190
+ def full_width_half_maximum_to_sigma(fwhm: float) -> float:
191
+ r"""
192
+ Convert full width at half maximum (FWHM) to Gaussian standard deviation.
193
+
194
+ .. math::
195
+
196
+ \text{FWHM} = 2 \sqrt{2 \ln 2} \,\sigma \;\;\Rightarrow\;\;
197
+ \sigma = \frac{\text{FWHM}}{2\sqrt{2\ln 2}}
198
+ """
199
+ return fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0)))
200
+
201
+ # ---------------- Private methods (implementation) ----------------
202
+
203
+ @staticmethod
204
+ def _build_gaussian_kernel(
205
+ sample_interval: float, gaussian_sigma: float, truncation_radius_in_sigmas: float
206
+ ) -> NDArray[np.float64]:
207
+ r"""
208
+ Construct a discrete Gaussian kernel and normalize to unit energy.
209
+
210
+ .. math::
211
+
212
+ g[k] = \exp\!\left(-\tfrac{1}{2} \left(\frac{k \,\Delta t}{\sigma}\right)^2\right),
213
+ \quad k = -L, \dots, L,
214
+
215
+ where :math:`L = \left\lceil \dfrac{\text{radius}\,\sigma}{\Delta t} \right\rceil`
216
+ and the discrete energy satisfies :math:`\sum_k g[k]^2 = 1`.
217
+ """
218
+ half_length = int(np.ceil(truncation_radius_in_sigmas * gaussian_sigma / sample_interval))
219
+ time_axis = np.arange(-half_length, half_length + 1, dtype=float) * sample_interval
220
+ kernel = np.exp(-0.5 * (time_axis / gaussian_sigma) ** 2)
221
+ kernel /= np.sqrt(np.sum(kernel ** 2))
222
+ return kernel
223
+
224
+ @staticmethod
225
+ def _correlate(signal: NDArray[np.float64], kernel: NDArray[np.float64]) -> NDArray[np.float64]:
226
+ r"""
227
+ Discrete correlation (matched filter):
228
+
229
+ .. math::
230
+
231
+ r[n] = \sum_m y[m] \, g[m-n].
232
+
233
+ Implemented as convolution with the reversed kernel.
234
+ """
235
+ return np.convolve(signal, kernel[::-1], mode="same")
236
+
237
+ @staticmethod
238
+ def _non_maximum_suppression(
239
+ values: NDArray[np.float64],
240
+ half_window: int,
241
+ threshold: float,
242
+ max_peaks: int,
243
+ ) -> NDArray[np.int_]:
244
+ r"""
245
+ Non-maximum suppression.
246
+
247
+ Keep index :math:`n` if
248
+
249
+ .. math::
250
+
251
+ r[n] = \max_{|k-n|\leq W} r[k], \quad r[n] \ge \tau,
252
+
253
+ where :math:`W` is the half-window and :math:`\tau` is the threshold.
254
+
255
+ Returns at most ``max_peaks`` indices with the largest responses.
256
+ """
257
+ if half_window < 1:
258
+ core = (values[1:-1] > values[:-2]) & (values[1:-1] >= values[2:]) & (values[1:-1] >= threshold)
259
+ idx = np.where(core)[0] + 1
260
+ else:
261
+ window_len = 2 * half_window + 1
262
+ padded = np.pad(values, (half_window, half_window), mode="edge")
263
+ windows = NonMaximumSuppression._sliding_window_view_1d(padded, window_len)
264
+ local_max = windows.max(axis=1)
265
+ idx = np.where((values >= local_max) & (values >= threshold))[0]
266
+
267
+ if idx.size > max_peaks:
268
+ keep = np.argpartition(values[idx], -max_peaks)[-max_peaks:]
269
+ idx = idx[keep]
270
+ idx = idx[np.argsort(values[idx])]
271
+
272
+ return np.sort(idx)
273
+
274
+ @staticmethod
275
+ def _estimate_noise_std(values: NDArray[np.float64]) -> float:
276
+ r"""
277
+ Estimate noise standard deviation from median absolute deviation (MAD).
278
+
279
+ .. math::
280
+
281
+ m = \text{median}(x), \quad
282
+ MAD = \text{median}(|x-m|), \quad
283
+ \hat\sigma_n \approx 1.4826 \, MAD
284
+ """
285
+ m = np.median(values)
286
+ mad = np.median(np.abs(values - m))
287
+ return 1.4826 * mad
288
+
289
+ # ---------------- Local replacement for sliding_window_view ----------------
290
+
291
+ @staticmethod
292
+ def _sliding_window_view_1d(
293
+ array: NDArray[np.float64],
294
+ window_length: int,
295
+ ) -> NDArray[np.float64]:
296
+ r"""
297
+ Create a 2D strided view of 1D ``array`` with a moving window of length ``window_length``.
298
+
299
+ The returned view has shape :math:`(N - L + 1, L)` where
300
+ :math:`N` is the length of ``array`` and :math:`L` is ``window_length``.
301
+ No data is copied.
302
+
303
+ This function replicates the essential behavior of
304
+ :code:`numpy.lib.stride_tricks.sliding_window_view` for the 1D case,
305
+ without importing it directly.
306
+
307
+ Parameters
308
+ ----------
309
+ array : array
310
+ One-dimensional input array.
311
+ window_length : int
312
+ Length :math:`L` of each sliding window (must satisfy :math:`1 \le L \le N`).
313
+
314
+ Returns
315
+ -------
316
+ array
317
+ A read-only view of shape :math:`(N-L+1, L)`.
318
+ """
319
+ if array.ndim != 1:
320
+ raise ValueError("array must be one-dimensional")
321
+ if not (1 <= window_length <= array.shape[0]):
322
+ raise ValueError("window_length must satisfy 1 <= L <= len(array)")
323
+
324
+ N = array.shape[0]
325
+ stride = array.strides[0]
326
+ shape = (N - window_length + 1, window_length)
327
+ strides = (stride, stride)
328
+ view = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
329
+ view.setflags(write=False)
330
+ return view
@@ -0,0 +1,54 @@
1
+ import matplotlib.pyplot as plt
2
+ from MPSPlots.styles import mps as plot_style
3
+
4
+ class DataSet:
5
+ """
6
+ A simple container class for datasets.
7
+
8
+ This class dynamically sets attributes based on the provided keyword arguments,
9
+ allowing for flexible storage of various dataset components.
10
+
11
+ Parameters
12
+ ----------
13
+ **kwargs : dict
14
+ Keyword arguments to be set as attributes of the instance.
15
+ """
16
+ def __init__(self, **kwargs):
17
+ for key, value in kwargs.items():
18
+ setattr(self, key, value)
19
+
20
+ def plot(self, number_of_samples: int | None = None, ax: plt.Axes = None, show: bool = True):
21
+ sample_count = self.signals.shape[0]
22
+
23
+ if number_of_samples is None:
24
+ number_of_samples = sample_count
25
+
26
+ with plt.style.context(plot_style):
27
+ figure, axes = plt.subplots(nrows=number_of_samples, ncols=1, figsize=(8, 3 * number_of_samples), squeeze=False)
28
+
29
+ for idx, ax in enumerate(axes.flatten()):
30
+ ax.plot(self.x_values, self.signals[idx], label='signal')
31
+ ax.vlines(self.positions[idx], ymin=0, ymax=1, transform=ax.get_xaxis_transform(), color='red', linestyle='--', linewidth=1, label='positions')
32
+
33
+ if self.region_of_interest is not None:
34
+ roi_patch = ax.fill_between(self.x_values, y1=0, y2=1,
35
+ where=(self.region_of_interest[idx] != 0),
36
+ color='lightblue',
37
+ alpha=1.0,
38
+ transform=ax.get_xaxis_transform(),
39
+ )
40
+
41
+ # build legend (add custom entry for ROI if present)
42
+ handles, labels = ax.get_legend_handles_labels()
43
+ if self.region_of_interest is not None:
44
+ handles.append(roi_patch)
45
+ labels.append("ROI")
46
+
47
+ by_label = {}
48
+ for h, l in zip(handles, labels):
49
+ if l and not l.startswith("_") and l not in by_label:
50
+ by_label[l] = h
51
+ ax.legend(by_label.values(), by_label.keys())
52
+
53
+ if show:
54
+ plt.show()
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from pathlib import Path
5
+ import DeepPeak
6
+
7
+
8
+ __all__ = [
9
+ 'root_path',
10
+ 'project_path',
11
+ 'doc_path',
12
+ 'doc_css_path',
13
+ 'logo_path'
14
+ ]
15
+
16
+ root_path = Path(DeepPeak.__path__[0])
17
+
18
+ project_path = root_path.parents[0]
19
+
20
+ weights_path = root_path.joinpath('weights')
21
+
22
+ example_directory = root_path.joinpath('examples')
23
+
24
+ doc_path = project_path.joinpath('docs')
25
+
26
+ doc_css_path = doc_path.joinpath('source/_static/default.css')
27
+
28
+ logo_path = doc_path.joinpath('images/logo.png')
29
+
30
+ examples_path = root_path.joinpath('examples')
31
+
32
+ if __name__ == '__main__':
33
+ for path_name in __all__:
34
+ path = locals()[path_name]
35
+ print(path)
36
+ assert path.exists(), f"Path {path_name} do not exists"
37
+
38
+ # -
@@ -0,0 +1,50 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+ from functools import wraps
4
+ from MPSPlots.styles import mps as plot_style
5
+ import matplotlib.pyplot as plt
6
+
7
+
8
+ def mpl_plot(function):
9
+ @wraps(function)
10
+ def wrapper(self, show: bool = True, save_as: str = None, **kwargs):
11
+ """
12
+ Decorator to set the plot style and handle figure saving and showing.
13
+ This decorator applies a specific plot style, saves the figure if a filename is provided,
14
+ and shows the plot if requested.
15
+
16
+ Parameters
17
+ ----------
18
+ function : callable
19
+ The plotting function to be wrapped.
20
+ show : bool, optional
21
+ Whether to display the plot (default: True).
22
+ save_as : str, optional
23
+ If provided, the figure is saved to this filename.
24
+ **kwargs : dict
25
+ Additional keyword arguments to pass to the plotting function.
26
+
27
+ Returns
28
+ -------
29
+ plt.Figure
30
+ The figure containing the plot.
31
+ """
32
+ # Create plot
33
+ with plt.style.context(plot_style):
34
+ figure, ax = plt.subplots(1, 1, figsize=(12, 5))
35
+
36
+ with plt.style.context(plot_style):
37
+ function(self, figure=figure, ax=ax, **kwargs)
38
+
39
+ figure.tight_layout()
40
+
41
+ if save_as is not None:
42
+ plt.savefig(save_as)
43
+ print(f"Figure saved as {save_as}")
44
+
45
+ if show:
46
+ plt.show()
47
+
48
+ return figure
49
+
50
+ return wrapper
File without changes
@@ -0,0 +1,3 @@
1
+ from .wavenet import WaveNet
2
+ from .dense import DenseNet
3
+ from .autoencoder import Autoencoder