Skip to content

[ENH] add a difference transformer to series transformations #2729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8c07ea3
add difference transformer to series transformations
TinaJin0228 Apr 6, 2025
321fdc2
add myself to .all-contributorsrc
TinaJin0228 Apr 6, 2025
8f78ec9
small modification
TinaJin0228 Apr 6, 2025
8b79897
Merge branch 'main' into feat/diff-transformer
TinaJin0228 Apr 6, 2025
606110c
modify according to reviews
TinaJin0228 Apr 13, 2025
c097579
Merge branch 'main' into feat/diff-transformer
TinaJin0228 Apr 14, 2025
174e126
Merge branch 'main' into feat/diff-transformer
MatthewMiddlehurst Apr 30, 2025
e4f84ae
Merge branch 'main' into feat/diff-transformer
TinaJin0228 May 12, 2025
73b8799
Merge branch 'main' into feat/diff-transformer
TonyBagnall May 16, 2025
0f1e79a
[ENH] add informational tags back to estimator docs (#2652)
inclinedadarsh May 17, 2025
a2e82d7
[DOC] Documentation improvement of BaseSeriesEstimator and BaseAeonEs…
kevinzb56 May 18, 2025
310ae99
[ENH] HydraTransformer Output changed (#2656)
lucifer4073 May 18, 2025
e31989e
[DOC] Improved Hidalgo Segmentation Notebook (#2616)
kavya-r30 May 18, 2025
62d1e91
new devs and workflow consolidation (#2797)
MatthewMiddlehurst May 18, 2025
3b861c0
all branches (#2809)
MatthewMiddlehurst May 18, 2025
d65c629
Automated `pre-commit` hook update (#2820)
aeon-actions-bot[bot] May 19, 2025
5741567
[MNT] Bump crs-k/stale-branches in the github-actions group (#2821)
dependabot[bot] May 19, 2025
1ebd5e4
fix: indexing in kdtw (#2826)
SebastianSchmidl May 20, 2025
1406526
[ENH] Added test cases for feature based clustering (#2690)
Ramana-Raja May 21, 2025
85ee3d9
[DOC] Fix inconsistent double backticks in updated similarity_search…
adityagh006 May 21, 2025
43c959b
delete some descriptions
TinaJin0228 May 23, 2025
b915120
Merge branch 'main' into feat/diff-transformer
SebastianSchmidl May 23, 2025
e631926
modification to _maintainer_
TinaJin0228 May 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,16 @@
"contributions": [
"code"
]
},
{
"login": "TinaJin0228",
"name": "Tina Jin",
"avatar_url": "https://avatars.githubusercontent.com/TinaJin0228",
"profile": "https://github.com/TinaJin0228",
"contributions": [
"code",
"doc"
]
}
],
"commitType": "docs"
Expand Down
2 changes: 2 additions & 0 deletions aeon/transformations/series/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"SIVSeriesTransformer",
"PCASeriesTransformer",
"WarpingSeriesTransformer",
"DifferenceTransformer",
]

from aeon.transformations.series._acf import (
Expand All @@ -32,6 +33,7 @@
from aeon.transformations.series._boxcox import BoxCoxTransformer
from aeon.transformations.series._clasp import ClaSPTransformer
from aeon.transformations.series._dft import DFTSeriesTransformer
from aeon.transformations.series._diff import DifferenceTransformer
from aeon.transformations.series._dobin import Dobin
from aeon.transformations.series._exp_smoothing import ExpSmoothingSeriesTransformer
from aeon.transformations.series._gauss import GaussSeriesTransformer
Expand Down
115 changes: 115 additions & 0 deletions aeon/transformations/series/_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import numpy as np

from aeon.transformations.series.base import BaseSeriesTransformer

__maintainer__ = ["Tina Jin"]
__all__ = ["DifferenceTransformer"]


class DifferenceTransformer(BaseSeriesTransformer):
"""
Calculates the n-th order difference of a time series.

Transforms a time series X into a series Y representing the difference
calculated `order` times.
- Order 1: Y[t] = X[t] - X[t-1]
- Order 2: Y[t] = (X[t] - X[t-1]) - (X[t-1] - X[t-2]) = X[t] - 2*X[t-1] + X[t-2]
- ... and so on.

The first `order` element(s) of the transformed series along the time axis
will be NaN, so that the output series will have the same shape as the input series.

Parameters
----------
order : int, default=1
The order of differencing. Must be a positive integer.

axis : int, default=1
The axis along which the difference is computed. Assumed to be the
time axis.
If `axis == 0`, assumes shape `(n_timepoints, n_channels)`.
If `axis == 1`, assumes shape `(n_channels, n_timepoints)`.

Notes
-----
This transformer assumes the input series does not contain NaN values where
the difference needs to be computed.

Examples
--------
>>> import numpy as np
>>> from aeon.transformations.series._diff import DifferenceTransformer
>>> X1 = np.array([[1, 3, 2, 5, 4, 7, 6, 9, 8, 10]])
>>> dt = DifferenceTransformer()
>>> Xt1 = dt.fit_transform(X1)
>>> print(Xt1)
[[nan 2. -1. 3. -1. 3. -1. 3. -1. 2.]]

>>> X2 = np.array([[1, 3, 2, 5, 4, 7, 6, 9, 8, 10]])
>>> dt2 = DifferenceTransformer(order=2)
>>> Xt2 = dt2.fit_transform(X2)
>>> print(Xt2)
[[nan nan -3. 4. -4. 4. -4. 4. -4. 3.]]

>>> X3 = np.array([[1, 2, 3, 4, 5], [5, 4, 3, 2, 1]])
>>> dt = DifferenceTransformer()
>>> Xt3 = dt.fit_transform(X3)
>>> print(Xt3)
[[nan 1. 1. 1. 1.]
[nan -1. -1. -1. -1.]]

>>> X4 = np.array([[1, 5], [2, 4], [3, 3], [4, 2], [5, 1]])
>>> dt_axis0 = DifferenceTransformer(axis=0)
>>> Xt4 = dt_axis0.fit_transform(X4, axis=0)
>>> print(Xt4)
[[nan nan]
[ 1. -1.]
[ 1. -1.]
[ 1. -1.]
[ 1. -1.]]
"""

_tags = {
"capability:multivariate": True,
"X_inner_type": "np.ndarray",
"fit_is_empty": True,
}

def __init__(self, order=1, axis=1):
if not isinstance(order, int) or order < 1:
raise ValueError(f"`order` must be a positive integer, but got {order}")
self.order = order
super().__init__(axis=axis)

def _transform(self, X, y=None):
"""
Perform the n-th order differencing transformation.

Parameters
----------
X : np.ndarray

y : ignored argument for interface compatibility

Returns
-------
Xt : np.ndarray
Transformed version of X with the same shape, containing the
n-th order difference.
The first `order` elements along the time axis are NaN.
"""
diff_X = np.diff(X, n=self.order, axis=self.axis)

# Check if diff_X is integer type.
# If so, cast to float to allow inserting np.nan.
if not np.issubdtype(diff_X.dtype, np.floating):
diff_X = diff_X.astype(np.float64)

# Insert the NaN at the beginning
nan_shape = list(X.shape)
nan_shape[self.axis] = self.order
nans_to_prepend = np.full(nan_shape, np.nan, dtype=np.float64)

Xt = np.concatenate([nans_to_prepend, diff_X], axis=self.axis)

return Xt
28 changes: 28 additions & 0 deletions aeon/transformations/series/tests/test_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Tests for Difference transformation."""

import numpy as np

from aeon.transformations.series._diff import DifferenceTransformer


def test_diff():
"""Tests basic first and second order differencing."""
X = np.array([[1.0, 4.0, 9.0, 16.0, 25.0, 36.0]])

dt1 = DifferenceTransformer(order=1)
Xt1 = dt1.fit_transform(X)
expected1 = np.array([[np.nan, 3.0, 5.0, 7.0, 9.0, 11.0]])

assert Xt1.shape == X.shape, "Shape mismatch for order 1"
np.testing.assert_allclose(
Xt1, expected1, equal_nan=True, err_msg="Value mismatch for order 1"
)

dt2 = DifferenceTransformer(order=2)
Xt2 = dt2.fit_transform(X)
expected2 = np.array([[np.nan, np.nan, 2.0, 2.0, 2.0, 2.0]])

assert Xt2.shape == X.shape, "Shape mismatch for order 2"
np.testing.assert_allclose(
Xt2, expected2, equal_nan=True, err_msg="Value mismatch for order 2"
)