Skip to content
71 changes: 32 additions & 39 deletions aeon/transformations/series/_clasp.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
"""
ClaSP (Classification Score Profile) Transformer implementation.

Notes
-----
As described in
@inproceedings{clasp2021,
title={ClaSP - Time Series Segmentation},
author={Sch"afer, Patrick and Ermshaus, Arik and Leser, Ulf},
booktitle={CIKM},
year={2021}
}
"""
"""ClaSP (Classification Score Profile) Transformer implementation."""

__maintainer__ = []
__all__ = ["ClaSPTransformer"]
Expand All @@ -26,7 +14,7 @@
from aeon.utils.validation import check_n_jobs


def _sliding_window(X, m):
def _sliding_window(X, m: int):
"""Return the sliding windows for a time series and a window size.

Parameters
Expand Down Expand Up @@ -74,7 +62,7 @@ def _sliding_dot_product(query, time_series):


@njit(fastmath=True, cache=True)
def _sliding_mean_std(X, m):
def _sliding_mean_std(X, m: int):
"""Return the sliding mean and std for a time series and a window size.

Parameters
Expand Down Expand Up @@ -102,7 +90,9 @@ def _sliding_mean_std(X, m):


@njit(fastmath=True, cache=True, parallel=True)
def _compute_distances_iterative(X, m, k, n_jobs=1, slack=0.5):
def _compute_distances_iterative(
X, m: int, k: int, n_jobs: int = 1, slack: float = 0.5
):
"""Compute kNN indices with dot-product.

No-loops implementation for a time series, given
Expand Down Expand Up @@ -176,7 +166,7 @@ def _compute_distances_iterative(X, m, k, n_jobs=1, slack=0.5):


@njit(fastmath=True, cache=True)
def _calc_knn_labels(knn_mask, split_idx, m):
def _calc_knn_labels(knn_mask, split_idx: int, m: int):
"""Compute kNN indices relabeling at a given split index.

Parameters
Expand Down Expand Up @@ -318,7 +308,7 @@ def _roc_auc_score(y_score, y_true):


@njit(fastmath=True)
def _calc_profile(m, knn_mask, score, exclusion_zone):
def _calc_profile(m: int, knn_mask, score, exclusion_zone: int):
"""Calculate ClaSP profile for the kNN indices and a score.

Parameters
Expand Down Expand Up @@ -349,12 +339,12 @@ def _calc_profile(m, knn_mask, score, exclusion_zone):

def clasp(
X,
m,
k_neighbours=3,
m: int,
k_neighbours: int = 3,
score=_roc_auc_score,
interpolate=True,
exclusion_radius=0.05,
n_jobs=1,
interpolate: bool = True,
exclusion_radius: float = 0.05,
n_jobs: int = 1,
):
"""Calculate ClaSP for a time series and a window size.

Expand All @@ -368,9 +358,9 @@ def clasp(
The number of knn to use
score : function
Scoring method used
interpolate:
interpolate : bool
Interpolate the profile
exclusion_radius : int
exclusion_radius : float
Blind spot of the profile to the corners
n_jobs : int
Number of jobs to be used.
Expand Down Expand Up @@ -403,13 +393,13 @@ class ClaSPTransformer(BaseSeriesTransformer):

Parameters
----------
window_length : int, default = 10
window_length : int, default = 10
size of window for sliding.
scoring_metric : string, default = ROC_AUC
scoring_metric : str, default = "ROC_AUC"
the scoring metric to use in ClaSP - choose from ROC_AUC or F1
exclusion_radius : int
exclusion_radius : float, default = 0.05
Exclusion Radius for change points to be non-trivial matches
n_jobs : int
n_jobs : int, default = 1
Number of jobs to be used.

Notes
Expand Down Expand Up @@ -444,10 +434,10 @@ class ClaSPTransformer(BaseSeriesTransformer):

def __init__(
self,
window_length=10,
scoring_metric="ROC_AUC",
exclusion_radius=0.05,
n_jobs=1,
window_length: int = 10,
scoring_metric: str = "ROC_AUC",
exclusion_radius: float = 0.05,
n_jobs: int = 1,
):
self.window_length = int(window_length)
self.scoring_metric = scoring_metric
Expand Down Expand Up @@ -479,14 +469,17 @@ def _transform(self, X, y=None):

if len(X) - self.window_length < 2 * self.exclusion_radius * len(X):
warnings.warn(
"Period-Length is larger than size of the time series", stacklevel=1
"Period-Length is larger than size of the time series",
stacklevel=2,
)

if X.dtype != np.float64:
warnings.warn(
f"dtype is {X.dtype} but should be {np.float64}. "
f"Will apply conversion to float64 now",
stacklevel=1,
(
f"dtype is {X.dtype} but should be {np.float64}. "
"Will apply conversion to float64 now"
),
stacklevel=2,
)

scoring_metric_call = self._check_scoring_metric(self.scoring_metric)
Expand All @@ -504,12 +497,12 @@ def _transform(self, X, y=None):

return Xt

def _check_scoring_metric(self, scoring_metric):
def _check_scoring_metric(self, scoring_metric: str):
"""Check which scoring metric to use.

Parameters
----------
scoring_metric : string
scoring_metric : str
Choose from "ROC_AUC" or "F1"

Returns
Expand Down
3 changes: 2 additions & 1 deletion docs/developer_guide/coding_standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ example can be found [here](https://gist.github.com/nateGeorge/5455d2c57fb33c1ae
- Code formatting according to [black](https://black.readthedocs.io/) and [flake8](https://flake8.pycqa.org/en/)
- Documentation formatting using the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html)
style
- Guidelines for [referencing externally written code](referencing_code)

## Code formatting and linting

Expand All @@ -25,7 +26,7 @@ We adhere to the code formatting standards using the following `pre-commit` hook
- [nbQA](https://github.com/nbQA-dev/nbQA) to lint and format Jupyter notebooks using
the above hooks
- [ruff](https://docs.astral.sh/ruff/)'s [pydocstyle](https://docs.astral.sh/ruff/rules/#pydocstyle-d)
module to enforce the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard>)
module to enforce the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard)
documentation style
- [pyupgrade](https://github.com/asottile/pyupgrade) to upgrade Python syntax to modern
standards
Expand Down
136 changes: 136 additions & 0 deletions docs/developer_guide/referencing_code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
(referencing_code)=
# Referencing Externally Written Code

When contributing code to `aeon` that is adapted, inspired by, or directly copied
from external sources, it is important to properly acknowledge the original authors
and include any necessary license information. This applies to code from other
repositories, research papers, Stack Overflow answers, or any other external source.

Proper attribution is both a legal requirement (to comply with open-source licenses)
and a matter of good practice in the open-source community.

## When is attribution required?

Attribution is required whenever code in `aeon`:

- Is directly copied or closely adapted from another repository or codebase
- Is inspired by or based on an existing implementation, even if significantly rewritten
- Implements an algorithm from a research paper using code provided by the authors
- Is adapted from a Stack Overflow answer or similar community resource

## How to add attribution

Attribution should be added in the **docstring** of the class or function containing
the external code. The correct place is in the `Notes` section of the docstring.

The format should include:

1. A description of how the code was used (e.g. "Adapted from", "Directly copied from",
"Inspired by")
2. A link to the original source
3. Copyright information and license (if available)

### Examples

**Code adapted from another repository:**

```python
Notes
-----
Adapted from the statsmodels 0.14.4 implementation
https://github.com/statsmodels/statsmodels/blob/main/statsmodels/tsa/filters/bk_filter.py
Copyright (c) 2009-2018 statsmodels Developers, BSD-3
```

**Code adapted from a repository by the original author:**

```python
Notes
-----
Adapted from the implementation from Ismail-Fawaz et. al
https://github.com/MSD-IRIMAS/LITE
by the code owner.
```

**Code adapted from multiple sources:**

```python
Notes
-----
This code was adapted from the tslearn and pyts functions.
pyts code:
https://pyts.readthedocs.io/en/latest/_modules/pyts/metrics/dtw.html
Copyright (c) 2018, Johann Faouzi and pyts contributors, BSD-3
tslearn code (line 974):
https://github.com/tslearn-team/tslearn/blob/main/tslearn/metrics/dtw_variants.py
Copyright (c) 2017, Romain Tavenard, BSD-2
```

**Code used with explicit permission from the owner:**

```python
Notes
-----
Parts of the code are adapted from https://github.com/hfawaz/cd-diagram
with permission from the owner.
```

**Code adapted from a class-level implementation (in the class docstring):**

```python
class MyEstimator:
"""My estimator.

...

Notes
-----
Parts (i.e. get_params and set_params) adapted from the scikit-learn 1.5.0
``_BaseComposition`` class in utils/metaestimators.py.
https://github.com/scikit-learn/scikit-learn/
Copyright (c) 2007-2024 The scikit-learn developers, BSD-3
"""
```

## File-level attribution

If an entire file is adapted from an external source, attribution should be added
at the top of the file in the module docstring. For example:

```python
"""Utility methods to print system info for debugging.

Notes
-----
Adapted from the scikit-learn 1.5.0 show_versions function.
https://github.com/scikit-learn/scikit-learn/
Copyright (c) 2007-2024 The scikit-learn developers, BSD-3
"""
```

## License compatibility

`aeon` uses the [BSD-3-Clause license](https://github.com/aeon-toolkit/aeon/blob/main/LICENSE).
When incorporating external code, you must ensure the source license is compatible
with BSD-3-Clause. The following licenses are generally compatible:

- BSD-2-Clause
- BSD-3-Clause
- MIT License

If you are unsure whether a license is compatible, please raise the question in your
pull request or ask on the `aeon` [Discord](https://discord.com/invite/54ACzaFsnSA)
before submitting.

Code under licenses such as GPL is **not compatible** with `aeon`'s BSD-3-Clause
license and should not be incorporated.

## Summary checklist

Before submitting a pull request that includes externally written code, ensure:

- [ ] Attribution is added in the `Notes` section of the relevant docstring
- [ ] The original source URL is included
- [ ] Copyright information and license are included where available
- [ ] The source license is compatible with BSD-3-Clause
- [ ] If adapted from a paper's own implementation, this is noted explicitly
Loading