Repository: aeon-toolkit/aeon
Pull request #1983 (2024-08-16 20:55:03)

📝 Add Cyril-Meyer as a contributor for code
Adds @Cyril-Meyer as a contributor for code.

This was requested by Cyril-Meyer [in this comment](https://github.com/aeon-toolkit/aeon/pull/1967#issuecomment-2294238676)

[skip ci]


Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 3c7419da..c6e9a8c1 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2454,7 +2454,8 @@
       "profile": "https://github.com/Cyril-Meyer",
       "contributions": [
         "test",
-        "doc"
+        "doc",
+        "code"
       ]
     },
     {
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index ca17b59a..096c600d 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,7 +1,7 @@
 # Contributors
 
 <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
-[![All Contributors](https://img.shields.io/badge/all_contributors-243-orange.svg)](#contributors)
+[![All Contributors](https://img.shields.io/badge/all_contributors-244-orange.svg)](#contributors)
 <!-- ALL-CONTRIBUTORS-BADGE:END -->
 
 This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
@@ -76,7 +76,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
     <tr>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/ClaudiaSanches"><img src="https://avatars3.githubusercontent.com/u/28742178?v=4?s=80" width="80px;" alt="ClaudiaSanches"/><br /><sub><b>ClaudiaSanches</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=ClaudiaSanches" title="Code">💻</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=ClaudiaSanches" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://sites.google.com/view/corvinpaul/"><img src="https://avatars.githubusercontent.com/u/5957191?v=4?s=80" width="80px;" alt="Corvin Paul"/><br /><sub><b>Corvin Paul</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=corvusrabus" title="Documentation">📖</a></td>
-      <td align="center" valign="top" width="12.5%"><a href="https://github.com/Cyril-Meyer"><img src="https://avatars.githubusercontent.com/u/69190238?v=4?s=80" width="80px;" alt="Cyril Meyer"/><br /><sub><b>Cyril Meyer</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=Cyril-Meyer" title="Tests">⚠️</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=Cyril-Meyer" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="12.5%"><a href="https://github.com/Cyril-Meyer"><img src="https://avatars.githubusercontent.com/u/69190238?v=4?s=80" width="80px;" alt="Cyril Meyer"/><br /><sub><b>Cyril Meyer</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=Cyril-Meyer" title="Tests">⚠️</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=Cyril-Meyer" title="Documentation">📖</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=Cyril-Meyer" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/DBCerigo"><img src="https://avatars.githubusercontent.com/u/8318425?v=4?s=80" width="80px;" alt="Daniel Burkhardt Cerigo"/><br /><sub><b>Daniel Burkhardt Cerigo</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=DBCerigo" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://www.linkedin.com/in/dnylam/"><img src="https://avatars.githubusercontent.com/u/30182852?v=4?s=80" width="80px;" alt="Daniel L."/><br /><sub><b>Daniel L.</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=Datadote" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://www.linkedin.com/in/daniel-martin-martinez"><img src="https://avatars.githubusercontent.com/dainelli98?s=80" width="80px;" alt="Daniel Martín Martínez"/><br /><sub><b>Daniel Martín Martínez</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=dainelli98" title="Documentation">📖</a> <a href="https://github.com/aeon-toolkit/aeon/issues?q=author%3Adainelli98" title="Bug reports">🐛</a></td>
@@ -298,13 +298,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/julu98"><img src="https://avatars.githubusercontent.com/u/133243896?v=4?s=80" width="80px;" alt="julu98"/><br /><sub><b>julu98</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/issues?q=author%3Ajulu98" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/kkoziara"><img src="https://avatars1.githubusercontent.com/u/4346849?v=4?s=80" width="80px;" alt="kkoziara"/><br /><sub><b>kkoziara</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=kkoziara" title="Code">💻</a> <a href="https://github.com/aeon-toolkit/aeon/issues?q=author%3Akkoziara" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/matteogales"><img src="https://avatars0.githubusercontent.com/u/9269326?v=4?s=80" width="80px;" alt="matteogales"/><br /><sub><b>matteogales</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=matteogales" title="Code">💻</a> <a href="#design-matteogales" title="Design">🎨</a> <a href="#ideas-matteogales" title="Ideas, Planning, & Feedback">🤔</a></td>
-      <td align="center" valign="top" width="12.5%"><a href="https://github.com/neuron283"><img src="https://avatars.githubusercontent.com/u/177816976?v=4?s=80" width="80px;" alt="neuron283"/><br /><sub><b>neuron283</b></sub></a><br /><a href="#maintenance-neuron283" title="Maintenance">🚧</a></td>
+      <td align="center" valign="top" width="12.5%"><a href="https://github.com/neuron283"><img src="https://avatars.githubusercontent.com/u/177816976?v=4?s=80" width="80px;" alt="neuron283"/><br /><sub><b>neuron283</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=neuron283" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/nileenagp"><img src="https://avatars.githubusercontent.com/u/48993630?v=4?s=80" width="80px;" alt="nileenagp"/><br /><sub><b>nileenagp</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=nileenagp" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/oleskiewicz"><img src="https://avatars1.githubusercontent.com/u/5682158?v=4?s=80" width="80px;" alt="oleskiewicz"/><br /><sub><b>oleskiewicz</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=oleskiewicz" title="Code">💻</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=oleskiewicz" title="Documentation">📖</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=oleskiewicz" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/pabworks"><img src="https://avatars.githubusercontent.com/u/32725127?v=4?s=80" width="80px;" alt="pabworks"/><br /><sub><b>pabworks</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=pabworks" title="Code">💻</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=pabworks" title="Tests">⚠️</a></td>
-      <td align="center" valign="top" width="12.5%"><a href="https://github.com/xloem"><img src="https://avatars.githubusercontent.com/u/279585?v=4?s=80" width="80px;" alt="patiently pending world peace"/><br /><sub><b>patiently pending world peace</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=xloem" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="12.5%"><a href="https://github.com/xloem"><img src="https://avatars.githubusercontent.com/u/279585?v=4?s=80" width="80px;" alt="patiently pending world peace"/><br /><sub><b>patiently pending world peace</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=xloem" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/raishubham1"><img src="https://avatars3.githubusercontent.com/u/29356417?v=4?s=80" width="80px;" alt="raishubham1"/><br /><sub><b>raishubham1</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=raishubham1" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/simone-pignotti"><img src="https://avatars1.githubusercontent.com/u/44410066?v=4?s=80" width="80px;" alt="simone-pignotti"/><br /><sub><b>simone-pignotti</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=simone-pignotti" title="Code">💻</a> <a href="https://github.com/aeon-toolkit/aeon/issues?q=author%3Asimone-pignotti" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/sophijka"><img src="https://avatars2.githubusercontent.com/u/47450591?v=4?s=80" width="80px;" alt="sophijka"/><br /><sub><b>sophijka</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=sophijka" title="Documentation">📖</a> <a href="#maintenance-sophijka" title="Maintenance">🚧</a></td>
@@ -312,9 +312,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/tensorflow-as-tf"><img src="https://avatars.githubusercontent.com/u/51345718?v=4?s=80" width="80px;" alt="tensorflow-as-tf"/><br /><sub><b>tensorflow-as-tf</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=tensorflow-as-tf" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/vNtzYy"><img src="https://avatars.githubusercontent.com/u/111287448?v=4?s=80" width="80px;" alt="vNtzYy"/><br /><sub><b>vNtzYy</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/issues?q=author%3AvNtzYy" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/vedazeren"><img src="https://avatars3.githubusercontent.com/u/63582874?v=4?s=80" width="80px;" alt="vedazeren"/><br /><sub><b>vedazeren</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=vedazeren" title="Code">💻</a> <a href="https://github.com/aeon-toolkit/aeon/commits?author=vedazeren" title="Tests">⚠️</a></td>
-      <td align="center" valign="top" width="12.5%"><a href="https://github.com/vincent-nich12"><img src="https://avatars3.githubusercontent.com/u/36476633?v=4?s=80" width="80px;" alt="vincent-nich12"/><br /><sub><b>vincent-nich12</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=vincent-nich12" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="12.5%"><a href="https://github.com/vincent-nich12"><img src="https://avatars3.githubusercontent.com/u/36476633?v=4?s=80" width="80px;" alt="vincent-nich12"/><br /><sub><b>vincent-nich12</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=vincent-nich12" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/vollmersj"><img src="https://avatars2.githubusercontent.com/u/12613127?v=4?s=80" width="80px;" alt="vollmersj"/><br /><sub><b>vollmersj</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=vollmersj" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/xiaobenbenecho"><img src="https://avatars.githubusercontent.com/u/17461849?v=4?s=80" width="80px;" alt="xiaobenbenecho"/><br /><sub><b>xiaobenbenecho</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=xiaobenbenecho" title="Code">💻</a></td>
       <td align="center" valign="top" width="12.5%"><a href="https://github.com/xiaopu222"><img src="https://avatars.githubusercontent.com/u/118000577?v=4?s=80" width="80px;" alt="xiaopu222"/><br /><sub><b>xiaopu222</b></sub></a><br /><a href="https://github.com/aeon-toolkit/aeon/commits?author=xiaopu222" title="Documentation">📖</a></td>
diff --git a/aeon/transformations/series/__init__.py b/aeon/transformations/series/__init__.py
index c07f3c6a..5097207d 100644
--- a/aeon/transformations/series/__init__.py
+++ b/aeon/transformations/series/__init__.py
@@ -5,6 +5,7 @@ __all__ = [
     "BaseSeriesTransformer",
     "ClearSkyTransformer",
     "ClaSPTransformer",
+    "DFTSeriesTransformer",
     "Dobin",
     "MatrixProfileSeriesTransformer",
     "StatsModelsACF",
@@ -26,6 +27,7 @@ from aeon.transformations.series._bkfilter import BKFilter
 from aeon.transformations.series._boxcox import BoxCoxTransformer
 from aeon.transformations.series._clasp import ClaSPTransformer
 from aeon.transformations.series._clear_sky import ClearSkyTransformer
+from aeon.transformations.series._dft import DFTSeriesTransformer
 from aeon.transformations.series._dobin import Dobin
 from aeon.transformations.series._matrix_profile import MatrixProfileSeriesTransformer
 from aeon.transformations.series._pca import PCASeriesTransformer
diff --git a/aeon/transformations/series/_dft.py b/aeon/transformations/series/_dft.py
new file mode 100644
index 00000000..1f603c39
--- /dev/null
+++ b/aeon/transformations/series/_dft.py
@@ -0,0 +1,88 @@
+"""Discrete Fourier Approximation filter transformation."""
+
+__maintainer__ = ["Cyril-Meyer"]
+__all__ = ["DFTSeriesTransformer"]
+
+
+import numpy as np
+
+from aeon.transformations.series.base import BaseSeriesTransformer
+
+
+class DFTSeriesTransformer(BaseSeriesTransformer):
+    """Filter a times series using Discrete Fourier Approximation (DFT).
+
+    Parameters
+    ----------
+    r : float
+        Proportion of Fourier terms to retain [0, 1]
+
+    sort : bool
+        Sort the Fourier terms by amplitude to keep most important terms
+
+    Notes
+    -----
+    More information of the NumPy FFT functions used
+    https://numpy.org/doc/stable/reference/routines.fft.html
+
+    References
+    ----------
+    .. [1] Cooley, J.W., & Tukey, J.W. (1965).
+       An algorithm for the machine calculation of complex Fourier series.
+       Mathematics of Computation, 19, 297-301.
+
+    Examples
+    --------
+    >>> import numpy as np
+    >>> from aeon.transformations.series._dft import DFTSeriesTransformer
+    >>> X = np.random.random((2, 100)) # Random series length 100
+    >>> dft = DFTSeriesTransformer()
+    >>> X_ = dft.fit_transform(X)
+    >>> X_.shape
+    (2, 100)
+    """
+
+    _tags = {
+        "capability:multivariate": True,
+        "X_inner_type": "np.ndarray",
+        "fit_is_empty": True,
+    }
+
+    def __init__(self, r=0.5, sort=False):
+        self.r = r
+        self.sort = sort
+        super().__init__(axis=1)
+
+    def _transform(self, X, y=None):
+        """Transform X and return a transformed version.
+
+        Parameters
+        ----------
+        X : np.ndarray
+            time series in shape (n_channels, n_timepoints)
+        y : ignored argument for interface compatibility
+
+        Returns
+        -------
+        transformed version of X
+        """
+        # Compute DFT
+        dft = np.fft.fft(X)
+
+        # Mask array of terms to keep and number of terms to keep
+        mask = np.zeros_like(dft, dtype=bool)
+        keep = max(int(self.r * dft.shape[1]), 1)
+
+        # If sort is set, sort the indices by the decreasing dft amplitude
+        if self.sort:
+            sorted_indices = np.argsort(np.abs(dft))[:, ::-1]
+            for i in range(dft.shape[0]):
+                mask[i, sorted_indices[i, 0:keep]] = True
+        # Else, keep the first terms
+        else:
+            mask[:, 0:keep] = True
+
+        # Invert DFT with masked terms
+        X_ = np.fft.ifft(dft * mask).real
+
+        return X_
diff --git a/docs/api_reference/transformations.rst b/docs/api_reference/transformations.rst
index a1b614a6..d34cc96d 100644
--- a/docs/api_reference/transformations.rst
+++ b/docs/api_reference/transformations.rst
@@ -186,6 +186,14 @@ Filtering and denoising
 
     BKFilter
 
+.. currentmodule:: aeon.transformations.series._dft
+
+.. autosummary::
+    :toctree: auto_generated/
+    :template: class.rst
+
+    DFTSeriesTransformer
+
 Slope
 ~~~~~~~~~~~~~~~~~~~~~~
 


Test patch:
diff --git a/aeon/transformations/series/tests/test_dft.py b/aeon/transformations/series/tests/test_dft.py
new file mode 100644
index 00000000..6ffe04b7
--- /dev/null
+++ b/aeon/transformations/series/tests/test_dft.py
@@ -0,0 +1,37 @@
+"""Tests for DFT transformation."""
+
+__maintainer__ = []
+
+import numpy as np
+import pytest
+
+
+@pytest.mark.parametrize("r", [0.00, 0.25, 0.50, 0.75, 1.00])
+@pytest.mark.parametrize("sort", [True, False])
+def test_dft(r, sort):
+    """Test the functionality of DFT transformation."""
+    n_samples = 100
+    t = np.linspace(0, 10, n_samples)
+    x1 = (
+        0.5 * np.sin(2 * np.pi * 1 * t)
+        + 0.2 * np.sin(2 * np.pi * 5 * t)
+        + 0.1 * np.sin(2 * np.pi * 10 * t)
+    )
+    x2 = (
+        0.4 * np.sin(2 * np.pi * 1.5 * t)
+        + 0.3 * np.sin(2 * np.pi * 4 * t)
+        + 0.1 * np.sin(2 * np.pi * 8 * t)
+    )
+    x12 = np.array([x1, x2])
+    x12r = x12 + np.random.random((2, n_samples)) * 0.25
+
+    from aeon.transformations.series._dft import DFTSeriesTransformer
+
+    dft = DFTSeriesTransformer(r=r, sort=sort)
+    x_1 = dft.fit_transform(x1)
+    x_2 = dft.fit_transform(x2)
+    x_12 = dft.fit_transform(x12)
+    dft.fit_transform(x12r)
+
+    np.testing.assert_almost_equal(x_1[0], x_12[0], decimal=4)
+    np.testing.assert_almost_equal(x_2[0], x_12[1], decimal=4)