Repository: aeon-toolkit/aeon
Pull request #2155 (2024-10-06 20:32:40)

[REF] Rename tags
Renames tags to replace `-` with `_` and replaces the odd `:removes` capability tag names.

aeon-actions-bot[bot]: 
## Thank you for contributing to `aeon`

I have added the following labels to this PR based on the title: **[ $\\color{#BDD9DB}{\\textsf{refactor}}$ ]**.
This PR changes too many different packages (>3) for automatic addition of labels, please manually add package labels if relevant.

The [Checks](https://github.com/aeon-toolkit/aeon/pull/2155/checks) tab will show the status of our automated tests. You can click on individual test runs in the tab or "Details" in the panel below to see more information if there is a failure.

If our `pre-commit` code quality check fails, any trivial fixes will automatically be pushed to your PR unless it is a draft.

Don't hesitate to ask questions on the `aeon` [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA) channel if you have any.

### PR CI actions

These checkboxes will add labels to enable/disable CI functionality for this PR. This may not take effect immediately, and a new commit may be required to run the new configuration.

- [ ] Run `pre-commit` checks for all files
- [ ] Run `mypy` typecheck tests
- [ ] Run all `pytest` tests and configurations
- [ ] Run all notebook example tests
- [ ] Run numba-disabled `codecov` tests
- [ ] Stop automatic `pre-commit` fixes (always disabled for drafts)
- [ ] Disable numba cache loading
- [ ] Push an empty commit to re-run CI checks
    

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 265e81c2..4a647e75 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.6.0
+    rev: v5.0.0
     hooks:
       - id: check-ast
       - id: check-added-large-files
@@ -29,7 +29,7 @@ repos:
         args: [ "--create", "--python-folders", "aeon" ]
 
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.6.8
+    rev: v0.6.9
     hooks:
       - id: ruff
         args: [ "--fix"]
diff --git a/aeon/anomaly_detection/_left_stampi.py b/aeon/anomaly_detection/_left_stampi.py
index 38c08f2b..a6980dc9 100644
--- a/aeon/anomaly_detection/_left_stampi.py
+++ b/aeon/anomaly_detection/_left_stampi.py
@@ -77,7 +77,7 @@ class LeftSTAMPi(BaseAnomalyDetector):
         "capability:multivariate": False,
         "capability:missing_values": False,
         "fit_is_empty": False,
-        "cant-pickle": True,
+        "cant_pickle": True,
         "python_dependencies": ["stumpy"],
     }
 
diff --git a/aeon/base/_base.py b/aeon/base/_base.py
index 80324d34..31af2889 100644
--- a/aeon/base/_base.py
+++ b/aeon/base/_base.py
@@ -51,8 +51,8 @@ class BaseEstimator(_BaseEstimator):
     _tags = {
         "python_version": None,
         "python_dependencies": None,
-        "cant-pickle": False,
-        "non-deterministic": False,
+        "cant_pickle": False,
+        "non_deterministic": False,
         "algorithm_type": None,
         "capability:missing_values": False,
         "capability:multithreading": False,
diff --git a/aeon/base/estimator/compose/collection_pipeline.py b/aeon/base/estimator/compose/collection_pipeline.py
index 0cac282a..a5708af7 100644
--- a/aeon/base/estimator/compose/collection_pipeline.py
+++ b/aeon/base/estimator/compose/collection_pipeline.py
@@ -111,9 +111,7 @@ class BaseCollectionPipeline(_HeterogenousMetaEstimator, BaseCollectionEstimator
             if (
                 isinstance(e[1], BaseEstimator)
                 and e[1].get_tag("capability:missing_values", False, raise_error=False)
-                and e[1].get_tag(
-                    "capability:missing_values:removes", False, raise_error=False
-                )
+                and e[1].get_tag("removes_missing_values", False, raise_error=False)
             ):
                 missing_rm_tag = True
                 break
@@ -142,9 +140,7 @@ class BaseCollectionPipeline(_HeterogenousMetaEstimator, BaseCollectionEstimator
                 isinstance(e[1], BaseEstimator)
                 and e[1].get_tag("capability:unequal_length", False, raise_error=False)
                 and (
-                    e[1].get_tag(
-                        "capability:unequal_length:removes", False, raise_error=False
-                    )
+                    e[1].get_tag("removes_unequal_length", False, raise_error=False)
                     or e[1].get_tag("output_data_type", raise_error=False) == "Tabular"
                 )
             ):
diff --git a/aeon/classification/deep_learning/_inception_time.py b/aeon/classification/deep_learning/_inception_time.py
index 6b377d56..90a9edd9 100644
--- a/aeon/classification/deep_learning/_inception_time.py
+++ b/aeon/classification/deep_learning/_inception_time.py
@@ -160,8 +160,8 @@ class InceptionTimeClassifier(BaseClassifier):
     _tags = {
         "python_dependencies": "tensorflow",
         "capability:multivariate": True,
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "algorithm_type": "deeplearning",
     }
 
diff --git a/aeon/classification/deep_learning/_lite_time.py b/aeon/classification/deep_learning/_lite_time.py
index f16e136d..e00abe79 100644
--- a/aeon/classification/deep_learning/_lite_time.py
+++ b/aeon/classification/deep_learning/_lite_time.py
@@ -110,8 +110,8 @@ class LITETimeClassifier(BaseClassifier):
     _tags = {
         "python_dependencies": "tensorflow",
         "capability:multivariate": True,
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "algorithm_type": "deeplearning",
     }
 
diff --git a/aeon/classification/deep_learning/base.py b/aeon/classification/deep_learning/base.py
index 837945ee..5d691cb6 100644
--- a/aeon/classification/deep_learning/base.py
+++ b/aeon/classification/deep_learning/base.py
@@ -42,8 +42,8 @@ class BaseDeepClassifier(BaseClassifier, ABC):
         "X_inner_type": "numpy3D",
         "capability:multivariate": True,
         "algorithm_type": "deeplearning",
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "python_dependencies": "tensorflow",
     }
 
diff --git a/aeon/classification/dictionary_based/_mrsqm.py b/aeon/classification/dictionary_based/_mrsqm.py
index 3ebe00a0..ce793f54 100644
--- a/aeon/classification/dictionary_based/_mrsqm.py
+++ b/aeon/classification/dictionary_based/_mrsqm.py
@@ -73,7 +73,7 @@ class MrSQMClassifier(BaseClassifier):
     _tags = {
         "X_inner_type": "numpy3D",
         "algorithm_type": "dictionary",
-        "cant-pickle": True,
+        "cant_pickle": True,
         "python_dependencies": "mrsqm",
     }
 
diff --git a/aeon/classification/shapelet_based/_ls.py b/aeon/classification/shapelet_based/_ls.py
index 1c667611..b8253975 100644
--- a/aeon/classification/shapelet_based/_ls.py
+++ b/aeon/classification/shapelet_based/_ls.py
@@ -95,7 +95,7 @@ class LearningShapeletClassifier(BaseClassifier):
     _tags = {
         "capability:multivariate": True,
         "algorithm_type": "shapelet",
-        "cant-pickle": True,
+        "cant_pickle": True,
         "python_dependencies": ["tslearn", "tensorflow"],
     }
 
diff --git a/aeon/classification/shapelet_based/_rdst.py b/aeon/classification/shapelet_based/_rdst.py
index 0c5c92bb..166ca100 100644
--- a/aeon/classification/shapelet_based/_rdst.py
+++ b/aeon/classification/shapelet_based/_rdst.py
@@ -132,7 +132,7 @@ class RDSTClassifier(BaseClassifier):
         "capability:unequal_length": True,
         "capability:multithreading": True,
         "X_inner_type": ["np-list", "numpy3D"],
-        "non-deterministic": True,  # due to random_state bug in MacOS #324
+        "non_deterministic": True,  # due to random_state bug in MacOS #324
         "algorithm_type": "shapelet",
     }
 
diff --git a/aeon/clustering/deep_learning/base.py b/aeon/clustering/deep_learning/base.py
index f0c7eba1..fc7b54c7 100644
--- a/aeon/clustering/deep_learning/base.py
+++ b/aeon/clustering/deep_learning/base.py
@@ -35,8 +35,8 @@ class BaseDeepClusterer(BaseClusterer, ABC):
         "X_inner_type": "numpy3D",
         "capability:multivariate": True,
         "algorithm_type": "deeplearning",
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "python_dependencies": "tensorflow",
     }
 
diff --git a/aeon/distances/_shift_scale_invariant.py b/aeon/distances/_shift_scale_invariant.py
index 4c99182c..2a07beb2 100644
--- a/aeon/distances/_shift_scale_invariant.py
+++ b/aeon/distances/_shift_scale_invariant.py
@@ -132,7 +132,7 @@ def _univariate_shift_scale_invariant_distance(
     x: np.ndarray, y: np.ndarray, max_shift: int
 ) -> tuple[float, np.ndarray]:
     if np.array_equal(x, y):
-        return 0, y
+        return 0.0, y
     min_dist = _scale_d(x, y)
     best_shifted_y = y
 
diff --git a/aeon/regression/convolution_based/_mr_hydra.py b/aeon/regression/convolution_based/_mr_hydra.py
index bcb66d96..c360f437 100644
--- a/aeon/regression/convolution_based/_mr_hydra.py
+++ b/aeon/regression/convolution_based/_mr_hydra.py
@@ -61,7 +61,7 @@ class MultiRocketHydraRegressor(BaseRegressor):
         "capability:multithreading": True,
         "algorithm_type": "convolution",
         "python_dependencies": "torch",
-        "non-deterministic": True,  # todo, presumed issue with mutlirocket
+        "non_deterministic": True,  # todo, presumed issue with mutlirocket
     }
 
     def __init__(self, n_kernels=8, n_groups=64, n_jobs=1, random_state=None):
diff --git a/aeon/regression/deep_learning/_inception_time.py b/aeon/regression/deep_learning/_inception_time.py
index a5bd4595..19493ac6 100644
--- a/aeon/regression/deep_learning/_inception_time.py
+++ b/aeon/regression/deep_learning/_inception_time.py
@@ -165,8 +165,8 @@ class InceptionTimeRegressor(BaseRegressor):
     _tags = {
         "python_dependencies": "tensorflow",
         "capability:multivariate": True,
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "algorithm_type": "deeplearning",
     }
 
diff --git a/aeon/regression/deep_learning/_lite_time.py b/aeon/regression/deep_learning/_lite_time.py
index 5a2079df..0d010431 100644
--- a/aeon/regression/deep_learning/_lite_time.py
+++ b/aeon/regression/deep_learning/_lite_time.py
@@ -111,8 +111,8 @@ class LITETimeRegressor(BaseRegressor):
     _tags = {
         "python_dependencies": "tensorflow",
         "capability:multivariate": True,
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "algorithm_type": "deeplearning",
     }
 
diff --git a/aeon/regression/deep_learning/base.py b/aeon/regression/deep_learning/base.py
index 987ea890..0e54ad24 100644
--- a/aeon/regression/deep_learning/base.py
+++ b/aeon/regression/deep_learning/base.py
@@ -35,8 +35,8 @@ class BaseDeepRegressor(BaseRegressor, ABC):
         "X_inner_type": "numpy3D",
         "capability:multivariate": True,
         "algorithm_type": "deeplearning",
-        "non-deterministic": True,
-        "cant-pickle": True,
+        "non_deterministic": True,
+        "cant_pickle": True,
         "python_dependencies": "tensorflow",
     }
 
diff --git a/aeon/regression/shapelet_based/_rdst.py b/aeon/regression/shapelet_based/_rdst.py
index 6e0ef58d..4bd5e53d 100644
--- a/aeon/regression/shapelet_based/_rdst.py
+++ b/aeon/regression/shapelet_based/_rdst.py
@@ -113,7 +113,7 @@ class RDSTRegressor(BaseRegressor):
         "capability:unequal_length": True,
         "capability:multithreading": True,
         "X_inner_type": ["np-list", "numpy3D"],
-        "non-deterministic": True,  # due to random_state bug in MacOS #324
+        "non_deterministic": True,  # due to random_state bug in MacOS #324
         "algorithm_type": "shapelet",
     }
 
diff --git a/aeon/transformations/base.py b/aeon/transformations/base.py
index bde6bc04..c6b53133 100644
--- a/aeon/transformations/base.py
+++ b/aeon/transformations/base.py
@@ -16,7 +16,7 @@ class BaseTransformer(BaseEstimator, ABC):
         "fit_is_empty": False,
         "capability:inverse_transform": False,
         "capability:missing_values": False,
-        "capability:missing_values:removes": False,
+        "removes_missing_values": False,
     }
 
     def __init__(self):
diff --git a/aeon/transformations/collection/_pad.py b/aeon/transformations/collection/_pad.py
index f7f66a63..f3e7f7de 100644
--- a/aeon/transformations/collection/_pad.py
+++ b/aeon/transformations/collection/_pad.py
@@ -53,7 +53,7 @@ class Padder(BaseCollectionTransformer):
         "fit_is_empty": False,
         "capability:multivariate": True,
         "capability:unequal_length": True,
-        "capability:unequal_length:removes": True,
+        "removes_unequal_length": True,
     }
 
     def __init__(self, pad_length=None, fill_value=0):
diff --git a/aeon/transformations/collection/_truncate.py b/aeon/transformations/collection/_truncate.py
index ff90c5c2..8818b138 100644
--- a/aeon/transformations/collection/_truncate.py
+++ b/aeon/transformations/collection/_truncate.py
@@ -39,7 +39,7 @@ class Truncator(BaseCollectionTransformer):
         "X_inner_type": ["np-list", "numpy3D"],
         "capability:multivariate": True,
         "capability:unequal_length": True,
-        "capability:unequal_length:removes": True,
+        "removes_unequal_length": True,
     }
 
     def __init__(self, truncated_length=None):
diff --git a/aeon/transformations/collection/base.py b/aeon/transformations/collection/base.py
index edaa76be..79dc5ca3 100644
--- a/aeon/transformations/collection/base.py
+++ b/aeon/transformations/collection/base.py
@@ -43,7 +43,7 @@ class BaseCollectionTransformer(
     _tags = {
         "input_data_type": "Collection",
         "output_data_type": "Collection",
-        "capability:unequal_length:removes": False,
+        "removes_unequal_length": False,
     }
 
     def __init__(self):
diff --git a/aeon/transformations/collection/channel_selection/_channel_scorer.py b/aeon/transformations/collection/channel_selection/_channel_scorer.py
index 32c211dc..1559d7c5 100644
--- a/aeon/transformations/collection/channel_selection/_channel_scorer.py
+++ b/aeon/transformations/collection/channel_selection/_channel_scorer.py
@@ -1,30 +1,51 @@
+"""Selects channels based on an estimate of performance."""
+
+__maintainer__ = ["TonyBagnall"]
+__all__ = ["ChannelScorer"]
+
 import math
-from typing import Optional
+from typing import Callable
+from typing import Dict as TypingDict
+from typing import List as TypingList
+from typing import Optional, Union
 
 import numpy as np
-from sklearn.metrics import accuracy_score
+from sklearn.metrics import accuracy_score, mean_squared_error
 
+from aeon.base import BaseEstimator
 from aeon.classification.base import BaseClassifier
-from aeon.classification.convolution_based._rocket_classifier import RocketClassifier
+from aeon.regression.base import BaseRegressor
 from aeon.transformations.collection.channel_selection.base import BaseChannelSelector
 
-__maintainer__ = ["TonyBagnall"]
-__all__ = ["ChannelScorer"]
-
 
 class ChannelScorer(BaseChannelSelector):
-    """Channel scorer performs channel selection using a single channel classifier.
+    """Performs channel selection using a single channel classifier or regressor.
 
-    ChannelScorer uses a time series classifier to score each channel using an
-    estimate of accuracy on the training data, then selects a proportion of the top
+    ChannelScorer uses a time series classifier or a regressor to score each channel
+    using an estimate of accuracy on the training data fro classifier or mean
+    squared error for regressor, then selects a proportion of the top
     channels to keep. Can be configured through the constructor to use any time
-    series classifier and could easily be adapted to use forward selection or elbow
+    series estimator and could easily be adapted to use forward selection or elbow
     class methods. Approximately as described in [1]_.
 
     Parameters
     ----------
-    classifier, BaseClassifier, default = MiniROCKET
-    proportion : float, default = 0.2
+    estimator : BaseEstimator
+        The time series estimator used to score each channel.
+
+    scoring_function : Callable, optional (default=None)
+        Scoring function used to evaluate the performance of each channel.
+        Defaults to:
+        - `accuracy_score` if using a classifier.
+        - `mean_squared_error` if using a regressor.
+
+    score_sign : float, optional (default=None)
+        The sign used to determine whether higher or lower scores are better.
+        Defaults to:
+        - 1.0 for accuracy_score (higher scores are better).
+        - -1.0 for mean_squared_error (lower scores are better).
+
+    proportion : float, default = 0.4
         Proportion of channels to keep, rounded up to nearest integer.
 
     References
@@ -41,16 +62,20 @@ class ChannelScorer(BaseChannelSelector):
 
     def __init__(
         self,
-        classifier: Optional[BaseClassifier] = None,
+        estimator: Optional[BaseEstimator],
+        scoring_function: Callable = None,
+        score_sign: float = None,
         proportion: float = 0.4,
     ):
         self.proportion = proportion
-        self.classifier = classifier
+        self.estimator = estimator
+        self.scoring_function = scoring_function
+        self.score_sign = score_sign
         super().__init__()
 
-    def _fit(self, X, y):
+    def _fit(self, X: np.ndarray, y: Union[np.ndarray, TypingList]):
         """
-        Fit ECP to a specified X and y.
+        Fit to a specified X and y.
 
         Parameters
         ----------
@@ -66,23 +91,37 @@ class ChannelScorer(BaseChannelSelector):
         if self.proportion <= 0 or self.proportion > 1:
             raise ValueError("proportion must be in the range 0-1")
 
-        if self.classifier is None:
-            self.classifier_ = RocketClassifier(
-                rocket_transform="minirocket", num_kernels=5000
+        if isinstance(self.estimator, BaseClassifier):
+            self.estimator_ = self.estimator.clone()
+            scoring_function = (
+                accuracy_score
+                if self.scoring_function is None
+                else self.scoring_function
             )
-        elif not isinstance(self.classifier, BaseClassifier):
-            raise ValueError(
-                "parameter classifier must be None or an instance of a  "
-                "BaseClassifier."
+            score_sign = 1.0 if self.score_sign is None else self.score_sign
+        elif isinstance(self.estimator, BaseRegressor):
+            self.estimator_ = self.estimator.clone()
+            scoring_function = (
+                mean_squared_error
+                if self.scoring_function is None
+                else self.scoring_function
             )
+            score_sign = -1.0 if self.score_sign is None else self.score_sign
         else:
-            self.classifier_ = self.classifier.clone()
+            raise ValueError(
+                "parameter estimator must be an instance of BaseClassifier, "
+                "BaseRegressor."
+            )
+
         n_channels = X.shape[1]
         scores = np.zeros(n_channels)
         # Evaluate each channel with the classifier
         for i in range(n_channels):
-            preds = self.classifier_.fit_predict(X[:, i, :], y)
-            scores[i] = accuracy_score(y, preds)
+            preds = self.estimator_.fit_predict(X[:, i, :], y)
+            scores[i] = score_sign * scoring_function(
+                y, preds
+            )  # Applying the proper scoring function
+
         # Select the top n_keep channels
         sorted_indices = np.argsort(-scores)
         n_keep = math.ceil(n_channels * self.proportion)
@@ -90,7 +129,7 @@ class ChannelScorer(BaseChannelSelector):
         return self
 
     @classmethod
-    def get_test_params(cls, parameter_set="default"):
+    def get_test_params(cls, parameter_set: str = "default") -> TypingDict[str, any]:
         """Return testing parameter settings for the estimator.
 
         Parameters
@@ -106,4 +145,4 @@ class ChannelScorer(BaseChannelSelector):
         """
         from aeon.classification import DummyClassifier
 
-        return {"classifier": DummyClassifier(), "proportion": 0.4}
+        return {"estimator": DummyClassifier(), "proportion": 0.4}
diff --git a/aeon/transformations/collection/convolution_based/rocketGPU/base.py b/aeon/transformations/collection/convolution_based/rocketGPU/base.py
index 9e87231e..c6e689cf 100644
--- a/aeon/transformations/collection/convolution_based/rocketGPU/base.py
+++ b/aeon/transformations/collection/convolution_based/rocketGPU/base.py
@@ -21,7 +21,7 @@ class BaseROCKETGPU(BaseCollectionTransformer):
         "capability:multivariate": True,
         "algorithm_type": "convolution",
         "capability:unequal_length": False,
-        "cant-pickle": True,
+        "cant_pickle": True,
         "python_dependencies": "tensorflow",
     }
 
diff --git a/aeon/utils/tags/_tags.py b/aeon/utils/tags/_tags.py
index 4f06c3cc..650a7c4d 100644
--- a/aeon/utils/tags/_tags.py
+++ b/aeon/utils/tags/_tags.py
@@ -34,13 +34,13 @@ ESTIMATOR_TAGS = {
         "as str or list of str. e.g. 'tsfresh>=0.20.0', "
         "'[tsfresh>=0.20.0, pandas<2.0.0]'. If None, no restriction.",
     },
-    "non-deterministic": {
+    "non_deterministic": {
         "class": "estimator",
         "type": "bool",
         "description": "The estimator is non-deterministic, and multiple runs will "
         "not produce the same output even if a `random_state` is set.",
     },
-    "cant-pickle": {
+    "cant_pickle": {
         "class": "estimator",
         "type": "bool",
         "description": "The estimator cannot be pickled.",
@@ -134,13 +134,13 @@ ESTIMATOR_TAGS = {
         "type": "bool",
         "description": "Does this estimator require y to be passed in its methods?",
     },
-    "capability:unequal_length:removes": {
+    "removes_unequal_length": {
         "class": "collection-transformer",
         "type": "bool",
         "description": "Is the transformer result guaranteed to be equal length series "
         "or tabular?",
     },
-    "capability:missing_values:removes": {
+    "removes_missing_values": {
         "class": "transformer",
         "type": "bool",
         "description": "Is the transformer result guaranteed to have no missing "


Test patch:
diff --git a/aeon/base/tests/test_base.py b/aeon/base/tests/test_base.py
index 3de208cf..3104de5e 100644
--- a/aeon/base/tests/test_base.py
+++ b/aeon/base/tests/test_base.py
@@ -52,8 +52,8 @@ FIXTURE_CLASSCHILD = FixtureClassChild
 FIXTURE_CLASSCHILD_TAGS = {
     "python_version": None,
     "python_dependencies": None,
-    "cant-pickle": False,
-    "non-deterministic": False,
+    "cant_pickle": False,
+    "non_deterministic": False,
     "algorithm_type": None,
     "capability:missing_values": False,
     "capability:multithreading": False,
@@ -70,8 +70,8 @@ FIXTURE_OBJECT._tags_dynamic = {"A": 42424241, "B": 3}
 FIXTURE_OBJECT_TAGS = {
     "python_version": None,
     "python_dependencies": None,
-    "cant-pickle": False,
-    "non-deterministic": False,
+    "cant_pickle": False,
+    "non_deterministic": False,
     "algorithm_type": None,
     "capability:missing_values": False,
     "capability:multithreading": False,
@@ -182,8 +182,8 @@ FIXTURE_OBJECT_SET = deepcopy(FIXTURE_OBJECT).set_tags(**FIXTURE_TAG_SET)
 FIXTURE_OBJECT_SET_TAGS = {
     "python_version": None,
     "python_dependencies": None,
-    "cant-pickle": False,
-    "non-deterministic": False,
+    "cant_pickle": False,
+    "non_deterministic": False,
     "algorithm_type": None,
     "capability:missing_values": False,
     "capability:multithreading": False,
diff --git a/aeon/classification/compose/tests/test_pipeline.py b/aeon/classification/compose/tests/test_pipeline.py
index fe3ab025..2d1e607f 100644
--- a/aeon/classification/compose/tests/test_pipeline.py
+++ b/aeon/classification/compose/tests/test_pipeline.py
@@ -118,10 +118,10 @@ def test_unequal_tag_inference():
     assert t1.get_tag("capability:unequal_length")
     assert t1.get_tag("output_data_type") == "Tabular"
     assert t2.get_tag("capability:unequal_length")
-    assert t2.get_tag("capability:unequal_length:removes")
+    assert t2.get_tag("removes_unequal_length")
     assert not t2.get_tag("output_data_type") == "Tabular"
     assert t3.get_tag("capability:unequal_length")
-    assert not t3.get_tag("capability:unequal_length:removes")
+    assert not t3.get_tag("removes_unequal_length")
     assert not t3.get_tag("output_data_type") == "Tabular"
     assert not t4.get_tag("capability:unequal_length")
 
@@ -179,14 +179,14 @@ def test_missing_tag_inference():
 
     t1 = MockCollectionTransformer()
     t1 = t1.set_tags(
-        **{"capability:missing_values": True, "capability:missing_values:removes": True}
+        **{"capability:missing_values": True, "removes_missing_values": True}
     )
     t2 = TimeSeriesScaler()
     t3 = StandardScaler()
     t4 = Tabularizer()
 
     assert t1.get_tag("capability:missing_values")
-    assert t1.get_tag("capability:missing_values:removes")
+    assert t1.get_tag("removes_missing_values")
     assert not t2.get_tag("capability:missing_values")
 
     c1 = DummyClassifier()
diff --git a/aeon/clustering/compose/tests/test_pipeline.py b/aeon/clustering/compose/tests/test_pipeline.py
index f239b704..5225e1d2 100644
--- a/aeon/clustering/compose/tests/test_pipeline.py
+++ b/aeon/clustering/compose/tests/test_pipeline.py
@@ -117,10 +117,10 @@ def test_unequal_tag_inference():
     assert t1.get_tag("capability:unequal_length")
     assert t1.get_tag("output_data_type") == "Tabular"
     assert t2.get_tag("capability:unequal_length")
-    assert t2.get_tag("capability:unequal_length:removes")
+    assert t2.get_tag("removes_unequal_length")
     assert not t2.get_tag("output_data_type") == "Tabular"
     assert t3.get_tag("capability:unequal_length")
-    assert not t3.get_tag("capability:unequal_length:removes")
+    assert not t3.get_tag("removes_unequal_length")
     assert not t3.get_tag("output_data_type") == "Tabular"
     assert not t4.get_tag("capability:unequal_length")
 
@@ -178,15 +178,13 @@ def test_missing_tag_inference():
     # X[5, 0, 4] = np.nan
 
     t1 = MockCollectionTransformer()
-    t1.set_tags(
-        **{"capability:missing_values": True, "capability:missing_values:removes": True}
-    )
+    t1.set_tags(**{"capability:missing_values": True, "removes_missing_values": True})
     t2 = TimeSeriesScaler()
     t3 = StandardScaler()
     t4 = Tabularizer()
 
     assert t1.get_tag("capability:missing_values")
-    assert t1.get_tag("capability:missing_values:removes")
+    assert t1.get_tag("removes_missing_values")
     assert not t2.get_tag("capability:missing_values")
 
     # todo revisit with mock clusterer
diff --git a/aeon/regression/compose/tests/test_pipeline.py b/aeon/regression/compose/tests/test_pipeline.py
index ce3085d2..c644e9f6 100644
--- a/aeon/regression/compose/tests/test_pipeline.py
+++ b/aeon/regression/compose/tests/test_pipeline.py
@@ -118,10 +118,10 @@ def test_unequal_tag_inference():
     assert t1.get_tag("capability:unequal_length")
     assert t1.get_tag("output_data_type") == "Tabular"
     assert t2.get_tag("capability:unequal_length")
-    assert t2.get_tag("capability:unequal_length:removes")
+    assert t2.get_tag("removes_unequal_length")
     assert not t2.get_tag("output_data_type") == "Tabular"
     assert t3.get_tag("capability:unequal_length")
-    assert not t3.get_tag("capability:unequal_length:removes")
+    assert not t3.get_tag("removes_unequal_length")
     assert not t3.get_tag("output_data_type") == "Tabular"
     assert not t4.get_tag("capability:unequal_length")
 
@@ -178,15 +178,13 @@ def test_missing_tag_inference():
     # X[5, 0, 4] = np.nan
 
     t1 = MockCollectionTransformer()
-    t1.set_tags(
-        **{"capability:missing_values": True, "capability:missing_values:removes": True}
-    )
+    t1.set_tags(**{"capability:missing_values": True, "removes_missing_values": True})
     t2 = TimeSeriesScaler()
     t3 = StandardScaler()
     t4 = Tabularizer()
 
     assert t1.get_tag("capability:missing_values")
-    assert t1.get_tag("capability:missing_values:removes")
+    assert t1.get_tag("removes_missing_values")
     assert not t2.get_tag("capability:missing_values")
 
     c1 = DummyRegressor()
diff --git a/aeon/testing/estimator_checking/_estimator_checking.py b/aeon/testing/estimator_checking/_estimator_checking.py
index bd5af8ff..1e0f1621 100644
--- a/aeon/testing/estimator_checking/_estimator_checking.py
+++ b/aeon/testing/estimator_checking/_estimator_checking.py
@@ -113,7 +113,7 @@ def check_estimator(
     general checks for all estimators, and module-specific tests for classifiers,
     anomaly detectors, transformer, etc.
     Some checks may be skipped if the estimator has certain tags i.e.
-    `non-deterministic`.
+    `non_deterministic`.
 
     Parameters
     ----------
diff --git a/aeon/testing/estimator_checking/_yield_collection_transformation_checks.py b/aeon/testing/estimator_checking/_yield_collection_transformation_checks.py
index 20f859d5..ee7636d5 100644
--- a/aeon/testing/estimator_checking/_yield_collection_transformation_checks.py
+++ b/aeon/testing/estimator_checking/_yield_collection_transformation_checks.py
@@ -35,7 +35,7 @@ def check_channel_selectors(estimator_class):
     """
     X, _ = make_example_3d_numpy(n_cases=20, n_channels=6, n_timepoints=30)
     y = np.array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
-    cs = estimator_class()
+    cs = estimator_class.create_test_instance(return_first=True)
     assert not cs.get_tag("fit_is_empty")
     cs.fit(X, y)
     assert cs.channels_selected_ is not None
diff --git a/aeon/testing/estimator_checking/_yield_estimator_checks.py b/aeon/testing/estimator_checking/_yield_estimator_checks.py
index 172fc35b..5bc2b071 100644
--- a/aeon/testing/estimator_checking/_yield_estimator_checks.py
+++ b/aeon/testing/estimator_checking/_yield_estimator_checks.py
@@ -219,14 +219,14 @@ def _yield_estimator_checks(estimator_class, estimator_instances, datatypes):
                 datatype=datatypes[i][0],
             )
 
-        if not _get_tag(estimator, "cant-pickle", default=False):
+        if not _get_tag(estimator, "cant_pickle", default=False):
             yield partial(
                 check_persistence_via_pickle,
                 estimator=estimator,
                 datatype=datatypes[i][0],
             )
 
-        if not _get_tag(estimator, "non-deterministic", default=False):
+        if not _get_tag(estimator, "non_deterministic", default=False):
             yield partial(
                 check_fit_deterministic, estimator=estimator, datatype=datatypes[i][0]
             )
diff --git a/aeon/transformations/collection/channel_selection/tests/test_channel_scorer.py b/aeon/transformations/collection/channel_selection/tests/test_channel_scorer.py
index 5607be3a..a57ac558 100644
--- a/aeon/transformations/collection/channel_selection/tests/test_channel_scorer.py
+++ b/aeon/transformations/collection/channel_selection/tests/test_channel_scorer.py
@@ -3,32 +3,79 @@
 import numpy as np
 import pytest
 
+from aeon.regression import DummyRegressor
 from aeon.testing.data_generation import make_example_3d_numpy
 from aeon.testing.mock_estimators import MockClassifierFullTags
 from aeon.transformations.collection.channel_selection import ChannelScorer
 
 
-def test_channel_scorer():
-    """Test the channel scorer."""
+def test_channel_scorer_with_classifier():
+    """Test the channel scorer for classifier."""
     # Test selects the correct number of channels
-    cs = ChannelScorer(classifier=MockClassifierFullTags(), proportion=0.5)
+    cs = ChannelScorer(estimator=MockClassifierFullTags(), proportion=0.5)
     X, _ = make_example_3d_numpy(n_cases=20, n_channels=10)
     y = np.array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
     cs.fit(X, y)
     assert len(cs.channels_selected_) == 5
     X, _ = make_example_3d_numpy(n_cases=20, n_channels=9)
     assert len(cs.channels_selected_) == 5
+
+    # Test for proportion out of range
     with pytest.raises(ValueError, match="proportion must be in the range 0-1"):
-        cs = ChannelScorer(classifier=MockClassifierFullTags(), proportion=1.1)
+        cs = ChannelScorer(estimator=MockClassifierFullTags(), proportion=1.1)
         cs.fit(X, y)
+
     with pytest.raises(ValueError, match="proportion must be in the range 0-1"):
-        ChannelScorer(classifier=MockClassifierFullTags(), proportion=-1)
+        ChannelScorer(estimator=MockClassifierFullTags(), proportion=-1)
         cs.fit(X, y)
-    cs = ChannelScorer(classifier=MockClassifierFullTags(), proportion=1.0)
+
+    # Test for selecting all channels when proportion=1.0
+    cs = ChannelScorer(estimator=MockClassifierFullTags(), proportion=1.0)
     cs.fit(X, y)
     assert len(cs.channels_selected_) == 9
+
+    # Test for invalid estimator
+    with pytest.raises(
+        ValueError,
+        match="parameter estimator must be an instance of BaseClassifier, "
+        "BaseRegressor",
+    ):
+        cs = ChannelScorer(estimator="FOOBAR", proportion=0.5)
+        cs.fit(X, y)
+
+
+def test_channel_scorer_with_regressor():
+    """Test the channel scorer for regressor."""
+    # Test selects the correct number of channels
+    cs = ChannelScorer(estimator=DummyRegressor(), proportion=0.5)
+    X, _ = make_example_3d_numpy(n_cases=20, n_channels=10)
+    y = np.random.rand(20)
+    cs.fit(X, y)
+    assert len(cs.channels_selected_) == 5
+
+    X, _ = make_example_3d_numpy(n_cases=20, n_channels=9)
+    cs.fit(X, y)
+    assert len(cs.channels_selected_) == 5
+
+    # Test for proportion out of range
+    with pytest.raises(ValueError, match="proportion must be in the range 0-1"):
+        cs = ChannelScorer(estimator=DummyRegressor(), proportion=1.1)
+        cs.fit(X, y)
+
+    with pytest.raises(ValueError, match="proportion must be in the range 0-1"):
+        cs = ChannelScorer(estimator=DummyRegressor(), proportion=-0.1)
+        cs.fit(X, y)
+
+    # Test for selecting all channels when proportion=1.0
+    cs = ChannelScorer(estimator=DummyRegressor(), proportion=1.0)
+    cs.fit(X, y)
+    assert len(cs.channels_selected_) == 9
+
+    # Test for invalid estimator
     with pytest.raises(
-        ValueError, match="parameter classifier must be None or an instance of"
+        ValueError,
+        match="parameter estimator must be an instance of BaseClassifier, "
+        "BaseRegressor",
     ):
-        cs = ChannelScorer(classifier="FOOBAR", proportion=0.5)
+        cs = ChannelScorer(estimator="FOOBAR", proportion=0.5)
         cs.fit(X, y)
diff --git a/aeon/transformations/collection/compose/tests/test_pipeline.py b/aeon/transformations/collection/compose/tests/test_pipeline.py
index 8320b6fd..21131233 100644
--- a/aeon/transformations/collection/compose/tests/test_pipeline.py
+++ b/aeon/transformations/collection/compose/tests/test_pipeline.py
@@ -69,10 +69,10 @@ def test_unequal_tag_inference():
     assert t1.get_tag("capability:unequal_length")
     assert t1.get_tag("output_data_type") == "Tabular"
     assert t2.get_tag("capability:unequal_length")
-    assert t2.get_tag("capability:unequal_length:removes")
+    assert t2.get_tag("removes_unequal_length")
     assert not t2.get_tag("output_data_type") == "Tabular"
     assert t3.get_tag("capability:unequal_length")
-    assert not t3.get_tag("capability:unequal_length:removes")
+    assert not t3.get_tag("removes_unequal_length")
     assert not t3.get_tag("output_data_type") == "Tabular"
     assert not t4.get_tag("capability:unequal_length")
 
@@ -115,15 +115,13 @@ def test_missing_tag_inference():
     X, y = make_example_3d_numpy(n_cases=10, n_timepoints=12)
 
     t1 = MockCollectionTransformer()
-    t1.set_tags(
-        **{"capability:missing_values": True, "capability:missing_values:removes": True}
-    )
+    t1.set_tags(**{"capability:missing_values": True, "removes_missing_values": True})
     t2 = TimeSeriesScaler()
     t3 = StandardScaler()
     t4 = Tabularizer()
 
     assert t1.get_tag("capability:missing_values")
-    assert t1.get_tag("capability:missing_values:removes")
+    assert t1.get_tag("removes_missing_values")
     assert not t2.get_tag("capability:missing_values")
 
     # transformer chain removes missing values
diff --git a/aeon/utils/tags/tests/test_lookup.py b/aeon/utils/tags/tests/test_lookup.py
index f36202eb..fd3d57fa 100644
--- a/aeon/utils/tags/tests/test_lookup.py
+++ b/aeon/utils/tags/tests/test_lookup.py
@@ -16,7 +16,7 @@ def test_all_tags_for_estimator_classification():
 
     assert isinstance(tags, dict)
     assert "python_version" in tags
-    assert "non-deterministic" in tags
+    assert "non_deterministic" in tags
     assert "capability:unequal_length" in tags
     assert "capability:contractable" in tags
 
@@ -37,7 +37,7 @@ def test_all_tags_for_estimator_anomaly_detection():
 
     assert isinstance(tags, dict)
     assert "python_version" in tags
-    assert "non-deterministic" in tags
+    assert "non_deterministic" in tags
     assert "capability:unequal_length" not in tags
     assert "capability:contractable" not in tags
 
diff --git a/aeon/utils/tags/tests/test_validate.py b/aeon/utils/tags/tests/test_validate.py
index daf1afeb..ca80135c 100644
--- a/aeon/utils/tags/tests/test_validate.py
+++ b/aeon/utils/tags/tests/test_validate.py
@@ -25,7 +25,7 @@ def test_check_valid_tags(estimator):
     tags = {
         "python_version": ">3.8",
         "python_dependencies": None,
-        "non-deterministic": False,
+        "non_deterministic": False,
     }
 
     check_valid_tags(estimator, tags=tags, error_on_missing=False)
@@ -61,8 +61,8 @@ def test_check_valid_tags_invalid():
     tags = {
         "python_version": ">3.8",
         "python_dependencies": None,
-        "non-deterministic": 1,
+        "non_deterministic": 1,
     }
 
-    with pytest.raises(ValueError, match="Tag non-deterministic has an invalid value"):
+    with pytest.raises(ValueError, match="Tag non_deterministic has an invalid value"):
         check_valid_tags(MockClassifier, tags=tags)