Repository: aeon-toolkit/aeon
Pull request #1006 (2023-12-20 19:22:31)

[ENH] New regression problems
Code to load all 63 regression problems from TSC.com. To note

1. Data is by default loaded from tsc.com. If that fails and it is one of the original 19, it then tries monash. tsc.com is I think more reliable than monash and we have not yet hosted all 63 with them
2. There is a mechanism for dealing with missing or unequal length. It is in my opinion the least bad way of doing this. There are now flags for load_regression that allow the user to specify the original data or to load data sets with missing values removed and/or standardised to equal length in the way we have done in our experiments. This makes it much easier to reproduce experiments, and there has been confusion in the past about the algorithms for interpolatation and making equal length

added tests for this on overnights. I will do results loaders in a separate PR

 

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

I have added the following labels to this PR based on the title: **[ $\\color{#FEF1BE}{\\textsf{enhancement}}$ ]**.
I would have added the following labels to this PR based on the changes made: **[ $\\color{#0B1D38}{\\textsf{datasets}}$ ]**, however some package labels are already present.

The [Checks](https://github.com/aeon-toolkit/aeon/pull/1006/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!
    

Patch:
diff --git a/aeon/classification/_delegate.py b/aeon/classification/_delegate.py
index 752a5a8d..1211ea73 100644
--- a/aeon/classification/_delegate.py
+++ b/aeon/classification/_delegate.py
@@ -47,7 +47,7 @@ class _DelegatedClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -81,7 +81,7 @@ class _DelegatedClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -112,7 +112,7 @@ class _DelegatedClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
diff --git a/aeon/classification/_dummy.py b/aeon/classification/_dummy.py
index 045d23de..0c7d9d95 100644
--- a/aeon/classification/_dummy.py
+++ b/aeon/classification/_dummy.py
@@ -83,7 +83,7 @@ class DummyClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
         y : array-like, shape = [n_instances] - the class labels
 
         Returns
@@ -111,7 +111,7 @@ class DummyClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
 
         Returns
         -------
diff --git a/aeon/classification/base.py b/aeon/classification/base.py
index cb6558cc..e0dfafaf 100644
--- a/aeon/classification/base.py
+++ b/aeon/classification/base.py
@@ -118,7 +118,7 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -195,7 +195,7 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -227,7 +227,7 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or 2D np.array (univariate, equal length series)
                 of shape (n_instances, n_timepoints)
diff --git a/aeon/classification/compose/_channel_ensemble.py b/aeon/classification/compose/_channel_ensemble.py
index 6ab4b732..cf2b960e 100644
--- a/aeon/classification/compose/_channel_ensemble.py
+++ b/aeon/classification/compose/_channel_ensemble.py
@@ -129,7 +129,7 @@ class _BaseChannelEnsembleClassifier(_HeterogenousMetaEstimator, BaseClassifier)
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
 
         y : array-like, shape = [n_instances]
             The class labels.
diff --git a/aeon/classification/compose/_ensemble.py b/aeon/classification/compose/_ensemble.py
index b7343498..06e066d7 100644
--- a/aeon/classification/compose/_ensemble.py
+++ b/aeon/classification/compose/_ensemble.py
@@ -203,7 +203,7 @@ class WeightedEnsembleClassifier(_HeterogenousMetaEstimator, BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/convolution_based/_arsenal.py b/aeon/classification/convolution_based/_arsenal.py
index afafba64..80543d45 100644
--- a/aeon/classification/convolution_based/_arsenal.py
+++ b/aeon/classification/convolution_based/_arsenal.py
@@ -160,7 +160,7 @@ class Arsenal(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -274,7 +274,7 @@ class Arsenal(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -295,7 +295,7 @@ class Arsenal(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/convolution_based/_rocket_classifier.py b/aeon/classification/convolution_based/_rocket_classifier.py
index 51ba3c58..e7d515fc 100644
--- a/aeon/classification/convolution_based/_rocket_classifier.py
+++ b/aeon/classification/convolution_based/_rocket_classifier.py
@@ -130,9 +130,9 @@ class RocketClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             The training data of shape = (n_instances, n_channels, n_timepoints).
-        y : 3D np.array
+        y : 3D np.ndarray
             The class labels shape = (n_instances,).
 
         Returns
@@ -207,7 +207,7 @@ class RocketClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -222,7 +222,7 @@ class RocketClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/dictionary_based/_boss.py b/aeon/classification/dictionary_based/_boss.py
index 40de1ccd..b164fbc3 100644
--- a/aeon/classification/dictionary_based/_boss.py
+++ b/aeon/classification/dictionary_based/_boss.py
@@ -587,7 +587,7 @@ class IndividualBOSS(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -625,7 +625,7 @@ class IndividualBOSS(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
diff --git a/aeon/classification/dictionary_based/_redcomets.py b/aeon/classification/dictionary_based/_redcomets.py
index 8a0b12fb..ab58f347 100644
--- a/aeon/classification/dictionary_based/_redcomets.py
+++ b/aeon/classification/dictionary_based/_redcomets.py
@@ -397,7 +397,7 @@ class REDCOMETS(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
@@ -439,7 +439,7 @@ class REDCOMETS(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
             ``n_channels > 1``
 
diff --git a/aeon/classification/dictionary_based/_weasel_v2.py b/aeon/classification/dictionary_based/_weasel_v2.py
index 14ff6e92..99b849a4 100644
--- a/aeon/classification/dictionary_based/_weasel_v2.py
+++ b/aeon/classification/dictionary_based/_weasel_v2.py
@@ -205,7 +205,7 @@ class WEASEL_V2(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
@@ -333,7 +333,7 @@ class WEASELTransformerV2:
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -418,7 +418,7 @@ class WEASELTransformerV2:
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
            The data to make predictions for.
         y : ignored argument for interface compatibility
 
diff --git a/aeon/classification/distance_based/_elastic_ensemble.py b/aeon/classification/distance_based/_elastic_ensemble.py
index a748f25d..3294e655 100644
--- a/aeon/classification/distance_based/_elastic_ensemble.py
+++ b/aeon/classification/distance_based/_elastic_ensemble.py
@@ -334,7 +334,7 @@ class ElasticEnsemble(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = (n_cases, n_channels, n_timepoints)
+        X : 3D np.ndarray of shape = (n_cases, n_channels, n_timepoints)
             or list of [n_cases] numpy arrays size n_channels, n_timepoints_i)
 
         Returns
diff --git a/aeon/classification/distance_based/_shape_dtw.py b/aeon/classification/distance_based/_shape_dtw.py
index 0fafb6a4..d7f8a855 100644
--- a/aeon/classification/distance_based/_shape_dtw.py
+++ b/aeon/classification/distance_based/_shape_dtw.py
@@ -115,7 +115,7 @@ class ShapeDTW(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -164,7 +164,7 @@ class ShapeDTW(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
         y - training data classes of shape [n_instances].
         """
         self._metric_params = {k.lower(): v for k, v in self._metric_params.items()}
diff --git a/aeon/classification/early_classification/base.py b/aeon/classification/early_classification/base.py
index bf75876e..9ac732d1 100644
--- a/aeon/classification/early_classification/base.py
+++ b/aeon/classification/early_classification/base.py
@@ -93,7 +93,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -178,7 +178,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -219,7 +219,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -261,7 +261,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -294,7 +294,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -366,7 +366,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -403,7 +403,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -438,7 +438,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -473,7 +473,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -519,7 +519,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -558,7 +558,7 @@ class BaseEarlyClassifier(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
diff --git a/aeon/classification/feature_based/_catch22.py b/aeon/classification/feature_based/_catch22.py
index 59cf52d7..b5d2ea26 100644
--- a/aeon/classification/feature_based/_catch22.py
+++ b/aeon/classification/feature_based/_catch22.py
@@ -148,7 +148,7 @@ class Catch22Classifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or list of numpy arrays (any number of channels, unequal length series)
                 of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
@@ -192,7 +192,7 @@ class Catch22Classifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or list of numpy arrays (any number of channels, unequal length series)
                 of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
@@ -210,7 +210,7 @@ class Catch22Classifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or list of numpy arrays (any number of channels, unequal length series)
                 of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
diff --git a/aeon/classification/feature_based/_summary_classifier.py b/aeon/classification/feature_based/_summary_classifier.py
index 433457ac..c62d0587 100644
--- a/aeon/classification/feature_based/_summary_classifier.py
+++ b/aeon/classification/feature_based/_summary_classifier.py
@@ -95,7 +95,7 @@ class SummaryClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -140,7 +140,7 @@ class SummaryClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -160,7 +160,7 @@ class SummaryClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/feature_based/_tsfresh_classifier.py b/aeon/classification/feature_based/_tsfresh_classifier.py
index e0ae7544..53c289ae 100644
--- a/aeon/classification/feature_based/_tsfresh_classifier.py
+++ b/aeon/classification/feature_based/_tsfresh_classifier.py
@@ -104,7 +104,7 @@ class TSFreshClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -171,7 +171,7 @@ class TSFreshClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -189,7 +189,7 @@ class TSFreshClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/hybrid/_hivecote_v1.py b/aeon/classification/hybrid/_hivecote_v1.py
index 05d8d644..9cc9fc7a 100644
--- a/aeon/classification/hybrid/_hivecote_v1.py
+++ b/aeon/classification/hybrid/_hivecote_v1.py
@@ -156,7 +156,7 @@ class HIVECOTEV1(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, n_timepoints]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, n_timepoints]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -297,7 +297,7 @@ class HIVECOTEV1(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, n_timepoints]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, n_timepoints]
             The data to make predictions for.
 
         Returns
@@ -318,7 +318,7 @@ class HIVECOTEV1(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/hybrid/_hivecote_v2.py b/aeon/classification/hybrid/_hivecote_v2.py
index 41dfca2b..afed3578 100644
--- a/aeon/classification/hybrid/_hivecote_v2.py
+++ b/aeon/classification/hybrid/_hivecote_v2.py
@@ -160,7 +160,7 @@ class HIVECOTEV2(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -303,7 +303,7 @@ class HIVECOTEV2(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -324,7 +324,7 @@ class HIVECOTEV2(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/classification/interval_based/_interval_pipelines.py b/aeon/classification/interval_based/_interval_pipelines.py
index b509fcb5..1ba9203a 100644
--- a/aeon/classification/interval_based/_interval_pipelines.py
+++ b/aeon/classification/interval_based/_interval_pipelines.py
@@ -132,7 +132,7 @@ class RandomIntervalClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
         y : 1D np.array, of shape [n_instances] - class labels for fitting
             indices correspond to instance indices in X
@@ -176,7 +176,7 @@ class RandomIntervalClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
 
         Returns
@@ -191,7 +191,7 @@ class RandomIntervalClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
 
         Returns
@@ -369,7 +369,7 @@ class SupervisedIntervalClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
         y : 1D np.array, of shape [n_instances] - class labels for fitting
             indices correspond to instance indices in X
@@ -414,7 +414,7 @@ class SupervisedIntervalClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
 
         Returns
@@ -429,7 +429,7 @@ class SupervisedIntervalClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
 
         Returns
diff --git a/aeon/classification/ordinal_classification/_ordinal_tde.py b/aeon/classification/ordinal_classification/_ordinal_tde.py
index 97766c0c..a3c9f3c4 100644
--- a/aeon/classification/ordinal_classification/_ordinal_tde.py
+++ b/aeon/classification/ordinal_classification/_ordinal_tde.py
@@ -211,7 +211,7 @@ class OrdinalTDE(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -368,7 +368,7 @@ class OrdinalTDE(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -389,7 +389,7 @@ class OrdinalTDE(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
@@ -811,7 +811,7 @@ class IndividualOrdinalTDE(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -891,7 +891,7 @@ class IndividualOrdinalTDE(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
diff --git a/aeon/classification/shapelet_based/_stc.py b/aeon/classification/shapelet_based/_stc.py
index 9cc0977e..c414d935 100644
--- a/aeon/classification/shapelet_based/_stc.py
+++ b/aeon/classification/shapelet_based/_stc.py
@@ -180,7 +180,7 @@ class ShapeletTransformClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -247,7 +247,7 @@ class ShapeletTransformClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
@@ -264,7 +264,7 @@ class ShapeletTransformClassifier(BaseClassifier):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predict probabilities for.
 
         Returns
diff --git a/aeon/clustering/base.py b/aeon/clustering/base.py
index aa592ea0..caa0f49f 100644
--- a/aeon/clustering/base.py
+++ b/aeon/clustering/base.py
@@ -35,7 +35,7 @@ class BaseClusterer(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or 2D np.array (univariate, equal length series)
                 of shape (n_instances, n_timepoints)
@@ -64,7 +64,7 @@ class BaseClusterer(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -116,7 +116,7 @@ class BaseClusterer(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -166,7 +166,7 @@ class BaseClusterer(BaseCollectionEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
diff --git a/aeon/datasets/_data_loaders.py b/aeon/datasets/_data_loaders.py
index c6da70f2..6656842c 100644
--- a/aeon/datasets/_data_loaders.py
+++ b/aeon/datasets/_data_loaders.py
@@ -16,6 +16,7 @@ from aeon.datasets.dataset_collections import (
     list_downloaded_tsc_tsr_datasets,
     list_downloaded_tsf_datasets,
 )
+from aeon.datasets.tser_data_lists import tser_monash, tser_soton
 from aeon.utils.validation.collection import convert_collection
 
 DIRNAME = "data"
@@ -279,6 +280,7 @@ def _load_saved_dataset(
     local_module=MODULE,
     local_dirname=DIRNAME,
     return_meta=False,
+    dir_name=None,
 ):
     """Load baked in time series classification datasets (helper function).
 
@@ -286,20 +288,28 @@ def _load_saved_dataset(
 
     Parameters
     ----------
-    name : string, file name to load from
-    split: None or one of "TRAIN", "TEST", optional (default=None)
+    name : str
+        Base problem file name.
+    split: None or {"TRAIN", "TEST"}, default=None
         Whether to load the train or test instances of the problem.
-        By default it loads both train and test instances (in a single container).
-    return_X_y: bool, optional (default=True)
-        If True, returns (features, target) separately instead of a single
-        dataframe with columns for features and the target.
-    return_data_type : str, optional, default = None
-        "numpy3D"/"numpy3d"/"np3D": recommended for equal length series
+        By default it loads both train and test instances into a single data structure.
+    return_X_y: bool, default=True
+        If True, returns (time series, target) separately. If False it returns (X,
+        y) as a tuple.
+    return_data_type : str, default = None
+        "numpy3D"/"numpy3d"/"np3D": recommended for equal length series, "np-list"
+        for unequal length series that cannot be stored in numpy arrays.
         "numpy2D"/"numpy2d"/"np2d": can be used for univariate equal length series,
         although we recommend numpy3d, because some transformers do not work with
-        numpy2d. If None will load 3D numpy or list of numpy
+        numpy2d. If None will load 3D numpy or list of numpy.
     local_module: default = os.path.dirname(__file__),
-    local_dirname: default = "data"
+    local_dirname: str, default = "data"
+    return_meta: bool, default = False
+        Dictionary of characteristics "problemname" (string), booleans: "timestamps",
+        "missing", "univariate", "equallength", "classlabel", "targetlabel" and
+        "class_values": [].
+    dir_name: str, default = None
+        Directory in local_dirname containing the problem file. If None, dir_name = name
 
     Raises
     ------
@@ -312,25 +322,27 @@ def _load_saved_dataset(
     y: 1D numpy array of length n, only returned if return_X_y if True
         The class labels for each time series instance in X
         If return_X_y is False, y is appended to X instead.
+    meta: meta data dictionary, only returned if return_meta is True
     """
     if isinstance(split, str):
         split = split.upper()
     # This is also called in load_from_tsfile, but we need the value here and it
     # is required in load_from_tsfile since it is public
     return_type = _alias_datatype_check(return_type)
-
+    if dir_name is None:
+        dir_name = name
     if split in ("TRAIN", "TEST"):
         fname = name + "_" + split + ".ts"
-        abspath = os.path.join(local_module, local_dirname, name, fname)
+        abspath = os.path.join(local_module, local_dirname, dir_name, fname)
         X, y, meta_data = load_from_tsfile(abspath, return_meta_data=True)
     # if split is None, load both train and test set
     elif split is None:
         fname = name + "_TRAIN.ts"
-        abspath = os.path.join(local_module, local_dirname, name, fname)
+        abspath = os.path.join(local_module, local_dirname, dir_name, fname)
         X_train, y_train, meta_data = load_from_tsfile(abspath, return_meta_data=True)
 
         fname = name + "_TEST.ts"
-        abspath = os.path.join(local_module, local_dirname, name, fname)
+        abspath = os.path.join(local_module, local_dirname, dir_name, fname)
         X_test, y_test, meta_data_test = load_from_tsfile(
             abspath, return_meta_data=True
         )
@@ -984,8 +996,18 @@ def load_forecasting(name, extract_path=None, return_metadata=False):
     return data
 
 
-def load_regression(name, split=None, extract_path=None, return_metadata=False):
-    """Download/load regression problem from http://tseregression.org/.
+def load_regression(
+    name,
+    split=None,
+    extract_path=None,
+    return_metadata=False,
+    load_equal_length=True,
+    load_no_missing=True,
+):
+    """Download/load regression problem.
+
+    Download from either https://timeseriesclassification.com or, if that fails,
+    http://tseregression.org/.
 
     If you want to load a problem from a local file, specify the
     location in ``extract_path``. This function assumes the data is stored in format
@@ -993,36 +1015,63 @@ def load_regression(name, split=None, extract_path=None, return_metadata=False):
     If you want to load a file directly from a full path, use the function
     `load_from_tsfile`` directly. If you do not specify ``extract_path``, or if the
     problem is not present in ``extract_path`` it will attempt to download the data
-    from http://tseregression.org/.
+    from https://timeseriesclassification.com or, if that fails,
+    http://tseregression.org/.
 
     The list of problems this function can download from the website is in
-    ``datasets/tser_lists.py``.  This function can load timestamped data, but it does
-    not store the time stamps. The time stamp loading is fragile, it will only work
-    if all data are floats.
+    ``datasets/tser_lists.py`` called ``tser_soton``. This function can load timestamped
+    data, but it does not store the time stamps. The time stamp loading is fragile,
+    it will only work if all data are floats.
 
     Data is assumed to be in the standard .ts format: each row is a (possibly
     multivariate) time series. Each dimension is separated by a colon, each value in
-    a series is comma separated. For examples see aeon.datasets.data. ArrowHead
-    is an example of a univariate equal length problem, BasicMotions an equal length
-    multivariate problem.
+    a series is comma separated. For an example TSER problem see
+    aeon.datasets.data.Covid3Month. Some of the original problems are unequal length
+    and have missing values. By default, this function loads equal length no
+    missing value versions of the files that have been used in experimental studies.
+    These have suffixes `_eq` or `_nmv` after the name.
+    If you want to load a different version, set the flags load_equal_length and/or
+    load_no_missing to true. If present, the function will then load these versions
+    if it can. aeon supports loading series with missing values and or unequal
+    length between series, but it does not support loading multivariate series where
+    lengths differ between channels. The original PGDALIA is in this format. The data
+    PGDALIA_eq has length normalised series. If a problem has unequal length series
+    and missing values, it is assumed to be of the form <name>_eq_nmv_TRAIN.ts and
+    <name>_eq_nmv_TEST.ts. There are currently no problems in the archive with
+    missing and unequal length.
 
 
     Parameters
     ----------
-    name : string, file name to load from
-    extract_path : optional (default = None)
-        Path of the location for the data file. If none, data is written to
-        os.path.dirname(__file__)/data/<name>/
+    name : string
+        Name of the problem to load or download.
+    extract_path : None or str, default = None
+        Path of the location for the data file. If None, data is written to
+        os.path.dirname(__file__)/local_data/<name>/.
     split : None or str{"train", "test"}, default=None
         Whether to load the train or test partition of the problem. By default it
         loads both into a single dataset, otherwise it looks only for files of the
         format <name>_TRAIN.ts or <name>_TEST.ts.
-    return_metadata : boolean, default = True
+    return_metadata : boolean, default = False
         If True, returns a tuple (X, y, metadata)
+    load_equal_length : boolean, default=True
+        This is for the case when the standard release has unequal length series. The
+        downloaded zip for these contain a version made equal length through
+        truncation. These versions all have the suffix _eq after the name. If this
+        flag is set to True, the function first attempts to load files called
+        <name>_eq_TRAIN.ts/TEST.ts. If these are not present, it will load the normal
+        version.
+    load_no_missing : boolean, default=True
+        This is for the case when the standard release has missing values. The
+        downloaded zip for these contain a version with imputed missing values. These
+        versions all have the suffix _nmv after the name. If this
+        flag is set to True, the function first attempts to load files called
+        <name>_nmv_TRAIN.ts/TEST.ts. If these are not present, it will load the normal
+        version.
 
     Raises
     ------
-    Raise ValueException if the requested return type is not supported
+    Raise ValueException if the requested return type is not supported.
 
     Returns
     -------
@@ -1039,59 +1088,75 @@ def load_regression(name, split=None, extract_path=None, return_metadata=False):
     >>> from aeon.datasets import load_regression
     >>> X, y=load_regression("FloodModeling1") # doctest: +SKIP
     """
-    from aeon.datasets.tser_data_lists import tser_all
-
     if extract_path is not None:
         local_module = extract_path
         local_dirname = ""
     else:
         local_module = MODULE
         local_dirname = "data"
-
+    error_str = (
+        f"File name {name} is not in the list of valid files to download,"
+        f"see aeon.datasets.tser_data_lists.tser_soton for the list. "
+        f"If it is one tsc.com but not on the list, it means it may not "
+        f"have been fully validated. Download it from the website."
+    )
     if not os.path.exists(os.path.join(local_module, local_dirname)):
         os.makedirs(os.path.join(local_module, local_dirname))
+    path = os.path.join(local_module, local_dirname)
     if name not in list_downloaded_tsc_tsr_datasets(extract_path):
-        if name in tser_all.keys():
-            id = tser_all[name]
+        if name in tser_soton:
             if extract_path is None:
                 local_dirname = "local_data"
-            if not os.path.exists(os.path.join(local_module, local_dirname)):
-                os.makedirs(os.path.join(local_module, local_dirname))
+                if not os.path.exists(os.path.join(local_module, local_dirname)):
+                    os.makedirs(os.path.join(local_module, local_dirname))
+                path = os.path.join(local_module, local_dirname)
         else:
-            raise ValueError(
-                f"File name {name} is not in the list of valid files to download"
-            )
+            raise ValueError(error_str)
         if name not in list_downloaded_tsc_tsr_datasets(
             os.path.join(local_module, local_dirname)
         ):
-            if name in tser_all.keys():
-                id = tser_all[name]
-            else:
-                raise ValueError(
-                    f"File name {name} is not in the list of valid files to download"
-                )
-            # Dataset is not already present in the datasets directory provided.
-            # If it is not there, download and install it.
-            url_train = f"https://zenodo.org/record/{id}/files/{name}_TRAIN.ts"
-            url_test = f"https://zenodo.org/record/{id}/files/{name}_TEST.ts"
-            if not os.path.exists(f"{local_module}/{local_dirname}/{name}"):
-                os.makedirs(f"{local_module}/{local_dirname}/{name}")
-
-            train_save = f"{local_module}/{local_dirname}/{name}/{name}_TRAIN.ts"
-            test_save = f"{local_module}/{local_dirname}/{name}/{name}_TEST.ts"
+            # Check if on timeseriesclassification.com
+            url = f"https://timeseriesclassification.com/aeon-toolkit/{name}.zip"
+            # This also tests the validitiy of the URL, can't rely on the html
             try:
-                urllib.request.urlretrieve(url_train, train_save)
-                urllib.request.urlretrieve(url_test, test_save)
-            except Exception:
-                raise ValueError(
-                    f"Invalid dataset name ={name} one or both of TRAIN and TEST is "
-                    f"not available on path ={local_module}/{local_dirname}/{name}.\n "
-                    f"Nor is it available on tseregression.org via path {url_train} "
-                    f"or {url_test}"
+                _download_and_extract(
+                    url,
+                    extract_path=extract_path,
                 )
-    #            zipfile.ZipFile(file_save, "r").extractall(f"{extract_path}/{name}/")
+            except zipfile.BadZipFile:
+                # Try on monash
+                if name in tser_monash.keys():
+                    id = tser_monash[name]
+                    url_train = f"https://zenodo.org/record/{id}/files/{name}_TRAIN.ts"
+                    url_test = f"https://zenodo.org/record/{id}/files/{name}_TEST.ts"
+                    full_path = os.path.join(path, name)
+                    if not os.path.exists(full_path):
+                        os.makedirs(full_path)
+
+                    train_save = f"{full_path}/{name}_TRAIN.ts"
+                    test_save = f"{full_path}/{name}_TEST.ts"
+                    try:
+                        urllib.request.urlretrieve(url_train, train_save)
+                        urllib.request.urlretrieve(url_test, test_save)
+                    except Exception:
+                        raise ValueError(error_str)
+    # Test for non missing or equal length versions
+    dir_name = name
+    if load_equal_length:
+        # If there exists a version with equal length, load that
+        train = os.path.join(path, f"{name}/{name}_eq_TRAIN.ts")
+        test = os.path.join(path, f"{name}/{name}_eq_TRAIN.ts")
+        if os.path.exists(train) and os.path.exists(test):
+            name = name + "_eq"
+    if load_no_missing:
+        train = os.path.join(path, f"{name}/{name}_nmv_TRAIN.ts")
+        test = os.path.join(path, f"{name}/{name}_nmv_TRAIN.ts")
+        if os.path.exists(train) and os.path.exists(test):
+            name = name + "_nmv"
+
     return _load_saved_dataset(
         name=name,
+        dir_name=dir_name,
         split=split,
         local_module=local_module,
         local_dirname=local_dirname,
diff --git a/aeon/datasets/dataset_collections.py b/aeon/datasets/dataset_collections.py
index b6d3a377..7edcdb13 100644
--- a/aeon/datasets/dataset_collections.py
+++ b/aeon/datasets/dataset_collections.py
@@ -37,7 +37,7 @@ import os
 
 import aeon
 from aeon.datasets.tsc_data_lists import multivariate, univariate
-from aeon.datasets.tser_data_lists import tser_all
+from aeon.datasets.tser_data_lists import tser_monash
 from aeon.datasets.tsf_data_lists import tsf_all
 
 MODULE = os.path.join(os.path.dirname(aeon.__file__), "datasets")
@@ -46,8 +46,8 @@ MODULE = os.path.join(os.path.dirname(aeon.__file__), "datasets")
 def list_available_tser_datasets(name=None):
     """List available tser data."""
     if name is None:  # List them all
-        return sorted(list(tser_all))
-    if name in tser_all:
+        return sorted(list(tser_monash))
+    if name in tser_monash:
         return True
     return False
 
diff --git a/aeon/datasets/tser_data_lists.py b/aeon/datasets/tser_data_lists.py
index 591b1a32..e8c4cc64 100644
--- a/aeon/datasets/tser_data_lists.py
+++ b/aeon/datasets/tser_data_lists.py
@@ -1,7 +1,7 @@
 """Datasets in the Monash tser data archives."""
 
 
-tser_all = {
+tser_monash = {
     "AppliancesEnergy": 3902637,
     "HouseholdPowerConsumption1": 3902704,
     "HouseholdPowerConsumption2": 3902706,
@@ -23,3 +23,85 @@ tser_all = {
     "NewsTitleSentiment": 3902726,
     "Covid3Month": 3902690,
 }
+
+tser_soton = {
+    "AcousticContaminationMadrid",
+    "AluminiumConcentration",
+    "AppliancesEnergy",
+    "AustraliaRainfall",
+    "BarCrawl6min",
+    "BeijingIntAirportPM25Quality",
+    "BeijingPM10Quality",
+    "BeijingPM25Quality",
+    "BenzeneConcentration",
+    "BIDMC32HR",
+    "BIDMC32RR",
+    "BIDMC32SpO2",
+    "BinanceCoinSentiment",
+    "BitcoinSentiment",
+    "BoronConcentration",
+    "CalciumConcentration",
+    "CardanoSentiment",
+    "ChilledWaterPredictor",
+    "CopperConcentration",
+    "Covid19Andalusia",
+    "Covid3Month",
+    "DailyOilGasPrices",
+    "DailyTemperatureLatitude",
+    "DhakaHourlyAirQuality",
+    "ElectricityPredictor",
+    "ElectricMotorTemperature",
+    "EthereumSentiment",
+    "FloodModeling1",
+    "FloodModeling2",
+    "FloodModeling3",
+    "GasSensorArrayAcetone",
+    "GasSensorArrayEthanol",
+    "HotwaterPredictor",
+    "HouseholdPowerConsumption1",
+    "HouseholdPowerConsumption2",
+    "IEEEPPG",
+    "IronConcentration",
+    "LiveFuelMoistureContent",
+    "LPGasMonitoringHomeActivity",
+    "MadridPM10Quality",
+    "MagnesiumConcentration",
+    "ManganeseConcentration",
+    "MethaneMonitoringHomeActivity",
+    "MetroInterstateTrafficVolume",
+    "NaturalGasPricesSentiment",
+    "NewsHeadlineSentiment",
+    "NewsTitleSentiment",
+    "OccupancyDetectionLight",
+    "ParkingBirmingham",
+    "PhosphorusConcentration",
+    "PotassiumConcentration",
+    "PPGDalia",
+    "PrecipitationAndalusia",
+    "SierraNevadaMountainsSnow",
+    "SodiumConcentration",
+    "SolarRadiationAndalusia",
+    "SteamPredictor",
+    "SulphurConcentration",
+    "TetuanEnergyConsumption",
+    "VentilatorPressure",
+    "WaveDataTension",
+    "WindTurbinePower",
+    "ZincConcentration",
+}
+
+tser_soton_unequal_length = {
+    "ParkingBirmingham",
+    "PPGDalia",
+}
+tser_soton_missing_values = {
+    "AcousticContaminationMadrid",
+    "BeijingPM10Quality",
+    "BeijingPM25Quality",
+    "BenzeneConcentration",
+    "HouseholdPowerConsumption1",
+    "HouseholdPowerConsumption2",
+    "MadridPM10Quality",
+    "PrecipitationAndalusia",
+    "SolarRadiationAndalusia",
+}
diff --git a/aeon/registry/_tags.py b/aeon/registry/_tags.py
index 91200302..e5c345ac 100644
--- a/aeon/registry/_tags.py
+++ b/aeon/registry/_tags.py
@@ -119,9 +119,10 @@ ESTIMATOR_TAG_REGISTER = [
             [
                 "pd.Series",
                 "pd.DataFrame",
-                "np.array",
+                "np.ndarray",
                 "nested_univ",
                 "pd-multiindex",
+                "pd_multiindex_hier",
                 "numpy3D",
                 "df-list",
             ],
@@ -130,13 +131,13 @@ ESTIMATOR_TAG_REGISTER = [
     ),
     (
         "X_inner_type",
-        ["forecaster"],
+        ["forecaster", "classifier", "regressor", "transformer", "clusterer"],
         (
             "list",
             [
                 "pd.Series",
                 "pd.DataFrame",
-                "np.array",
+                "np.ndarray",
                 "nested_univ",
                 "pd-multiindex",
                 "numpy3D",
diff --git a/aeon/regression/_delegate.py b/aeon/regression/_delegate.py
index e0522246..3cd9a57c 100644
--- a/aeon/regression/_delegate.py
+++ b/aeon/regression/_delegate.py
@@ -46,7 +46,7 @@ class _DelegatedRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
@@ -79,7 +79,7 @@ class _DelegatedRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array
+        X : 3D np.ndarray
             Input data, any number of channels, equal length series of shape ``(
             n_instances, n_channels, n_timepoints)``
             or 2D np.array (univariate, equal length series) of shape
diff --git a/aeon/regression/_dummy.py b/aeon/regression/_dummy.py
index d829a6b9..758f92cc 100644
--- a/aeon/regression/_dummy.py
+++ b/aeon/regression/_dummy.py
@@ -68,7 +68,7 @@ class DummyRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array of shape [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape [n_instances, n_channels, series_length]
         y : array-like, shape = [n_instances] - the target values
 
         Returns
@@ -83,7 +83,7 @@ class DummyRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array of shape [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape [n_instances, n_channels, series_length]
 
         Returns
         -------
diff --git a/aeon/regression/convolution_based/_rocket_regressor.py b/aeon/regression/convolution_based/_rocket_regressor.py
index 90139ea5..a6f0d768 100644
--- a/aeon/regression/convolution_based/_rocket_regressor.py
+++ b/aeon/regression/convolution_based/_rocket_regressor.py
@@ -195,7 +195,7 @@ class RocketRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
diff --git a/aeon/regression/feature_based/_catch22.py b/aeon/regression/feature_based/_catch22.py
index b2081dc0..86842981 100644
--- a/aeon/regression/feature_based/_catch22.py
+++ b/aeon/regression/feature_based/_catch22.py
@@ -135,7 +135,7 @@ class Catch22Regressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or list of numpy arrays (any number of channels, unequal length series)
                 of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
@@ -179,7 +179,7 @@ class Catch22Regressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or list of numpy arrays (any number of channels, unequal length series)
                 of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
diff --git a/aeon/regression/feature_based/_fresh_prince.py b/aeon/regression/feature_based/_fresh_prince.py
index 3036b8f4..f1350e1e 100644
--- a/aeon/regression/feature_based/_fresh_prince.py
+++ b/aeon/regression/feature_based/_fresh_prince.py
@@ -105,7 +105,7 @@ class FreshPRINCERegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The training data.
         y : array-like, shape = [n_instances]
             The class labels.
@@ -149,7 +149,7 @@ class FreshPRINCERegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array of shape = [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
             The data to make predictions for.
 
         Returns
diff --git a/aeon/regression/interval_based/_interval_pipelines.py b/aeon/regression/interval_based/_interval_pipelines.py
index 9de321ee..960ac753 100644
--- a/aeon/regression/interval_based/_interval_pipelines.py
+++ b/aeon/regression/interval_based/_interval_pipelines.py
@@ -126,7 +126,7 @@ class RandomIntervalRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
         y : 1D np.array, of shape [n_instances] - target labels for fitting
             indices correspond to instance indices in X
@@ -170,7 +170,7 @@ class RandomIntervalRegressor(BaseRegressor):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
 
         Returns
diff --git a/aeon/transformations/collection/convolution_based/_minirocket_mv.py b/aeon/transformations/collection/convolution_based/_minirocket_mv.py
index 26896104..fdefdb6f 100644
--- a/aeon/transformations/collection/convolution_based/_minirocket_mv.py
+++ b/aeon/transformations/collection/convolution_based/_minirocket_mv.py
@@ -247,10 +247,10 @@ def _np_list_transposed2D_array_and_len_list(
 
     Returns
     -------
-    np.array: 2D array of shape =
+    np.ndarray: 2D array of shape =
         [n_channels, sum(length_series(i) for i in n_instances)],
         np.float32
-    np.array: 1D array of shape = [n_instances]
+    np.ndarray: 1D array of shape = [n_instances]
         with length of each series, np.int32
 
     Raises
diff --git a/aeon/transformations/collection/feature_based/_catch22.py b/aeon/transformations/collection/feature_based/_catch22.py
index 6d7e2d58..61d023d3 100644
--- a/aeon/transformations/collection/feature_based/_catch22.py
+++ b/aeon/transformations/collection/feature_based/_catch22.py
@@ -166,7 +166,7 @@ class Catch22(BaseCollectionTransformer):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
+        X : 3D np.ndarray (any number of channels, equal length series)
                 of shape (n_instances, n_channels, n_timepoints)
             or list of numpy arrays (any number of channels, unequal length series)
                 of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
diff --git a/aeon/transformations/collection/scaler.py b/aeon/transformations/collection/scaler.py
index 631c17a3..ef11cb79 100644
--- a/aeon/transformations/collection/scaler.py
+++ b/aeon/transformations/collection/scaler.py
@@ -86,11 +86,12 @@ class TimeSeriesScaler(BaseCollectionTransformer):
 
         Parameters
         ----------
-        X : 3D np.array (any number of channels, equal length series)
-                of shape (n_instances, n_channels, n_timepoints)
+        X : 3D np.ndarray (any number of channels, equal length series)
+                of shape ``(n_instances, n_channels, n_timepoints)``
             or list of numpy arrays (any number of channels, unequal length series)
-                of shape [n_instances], 2D np.array (n_channels, n_timepoints_i), where
-                n_timepoints_i is length of series i
+                of shape ``[n_instances]``, 2D np.ndarray `
+                `(n_channels, n_timepoints_i)``, where ``n_timepoints_i`` is length
+                of series i.
 
         Returns
         -------
diff --git a/aeon/utils/datetime.py b/aeon/utils/datetime.py
index 5d0afe6b..5bab465a 100644
--- a/aeon/utils/datetime.py
+++ b/aeon/utils/datetime.py
@@ -4,14 +4,12 @@ __author__ = ["mloning", "xiaobenbenecho", "khrapovs"]
 __all__ = []
 
 import warnings
-from functools import singledispatch
-from typing import Optional, Tuple, Union
+from typing import Tuple, Union
 
 import numpy as np
 import pandas as pd
 from pandas.api.types import is_integer_dtype
 
-from aeon.datatypes import VectorizedDF
 from aeon.utils.validation.series import check_time_index, is_integer_index
 
 
@@ -175,41 +173,12 @@ def set_hier_freq(x):
     return x
 
 
-@singledispatch
-def infer_freq(y=None) -> Optional[str]:
+def infer_freq(y):
     """Infer frequency string from the time series object.
 
     Parameters
     ----------
-    y : Series, Panel, or Hierarchical object, or VectorizedDF, optional (default=None)
-
-    Returns
-    -------
-    str
-        Frequency string inferred from the pandas index,
-        or `None`, if inference fails.
-    """
-    return None
-
-
-@infer_freq.register(pd.DataFrame)
-@infer_freq.register(pd.Series)
-@infer_freq.register(np.ndarray)
-def _(y) -> Optional[str]:
-    return _infer_freq_from_index(get_time_index(y))
-
-
-@infer_freq.register(VectorizedDF)
-def _(y) -> Optional[str]:
-    return _infer_freq_from_index(get_time_index(y.as_list()[0]))
-
-
-def _infer_freq_from_index(index: pd.Index) -> Optional[str]:
-    """Infer frequency string from the pandas index.
-
-    Parameters
-    ----------
-    index : pd.Index
+    y : pd.DataFrame, pd.Series, or np.ndarray object
 
     Returns
     -------
@@ -217,6 +186,7 @@ def _infer_freq_from_index(index: pd.Index) -> Optional[str]:
         Frequency string inferred from the pandas index,
         or `None`, if inference fails.
     """
+    index = get_time_index(y)
     if hasattr(index, "freqstr"):
         return index.freqstr
     else:
diff --git a/aeon/utils/validation/forecasting.py b/aeon/utils/validation/forecasting.py
index 0746f8f3..b674bc06 100644
--- a/aeon/utils/validation/forecasting.py
+++ b/aeon/utils/validation/forecasting.py
@@ -20,6 +20,7 @@ from typing import Optional, Union
 
 import numpy as np
 import pandas as pd
+from pandas.api.types import is_numeric_dtype
 from sklearn.base import clone, is_regressor
 from sklearn.ensemble import GradientBoostingRegressor
 
@@ -467,20 +468,57 @@ def check_regressor(regressor=None, random_state=None):
     return regressor
 
 
+def _check_valid_prediction_intervals(obj):
+    """Check whether obj is a valid representation of probablity prediction intervals.
+
+    Parameters
+    ----------
+    obj : pd.DataFrame
+
+    Returns
+    -------
+    bool
+        True if obj is a valid representation of prediction intervals
+    """
+    # check if the input is a dataframe
+    if not isinstance(obj, pd.DataFrame):
+        return False
+    # check that column indices are unique
+    if not len(set(obj.columns)) == len(obj.columns):
+        return False
+    # check that all cols are numeric
+    if not np.all([is_numeric_dtype(obj[c]) for c in obj.columns]):
+        return False
+    # Check time index is ordered in time
+    if not obj.index.is_monotonic_increasing:
+        return False
+    # check column multiindex
+    colidx = obj.columns
+    if not isinstance(colidx, pd.MultiIndex) or not colidx.nlevels == 3:
+        return False
+    coverages = colidx.get_level_values(1)
+    if not is_numeric_dtype(coverages):
+        return False
+    if not (coverages <= 1).all() or not (coverages >= 0).all():
+        return False
+    upper_lower = colidx.get_level_values(2)
+    if not upper_lower.isin(["upper", "lower"]).all():
+        return False
+    return True
+
+
 def check_interval_df(interval_df, index_to_match):
     """
     Verify that a predicted interval DataFrame is formatted correctly.
 
     Parameters
     ----------
-    interval_df : pandas DataFrame outputted from forecaster.predict_interval()
+    interval_df : pandas DataFrame output from forecaster.predict_interval()
     index_to_match : Index object that must match interval_df.index
     """
-    from aeon.datatypes import check_is_mtype
-
-    checked = check_is_mtype(interval_df, "pred_interval", return_metadata=True)
-    if not checked[0]:
-        raise ValueError(checked[1])
+    valid = _check_valid_prediction_intervals(interval_df)
+    if not valid:
+        raise ValueError("Aregument interval_df is not a valid prediction interval.")
     df_idx = interval_df.index
     if len(index_to_match) != len(df_idx) or not (index_to_match == df_idx).all():
         raise ValueError("Prediction interval index must match the final Series index.")
diff --git a/aeon/utils/validation/panel.py b/aeon/utils/validation/panel.py
index cb429de7..3de365d8 100644
--- a/aeon/utils/validation/panel.py
+++ b/aeon/utils/validation/panel.py
@@ -14,7 +14,7 @@ from sklearn.utils.validation import check_consistent_length
 from aeon.utils.validation import is_nested_univ_dataframe
 from aeon.utils.validation.collection import convert_collection
 
-VALID_X_TYPES = (pd.DataFrame, np.ndarray)  # nested pd.DataFrame, 2d or 3d np.array
+VALID_X_TYPES = (pd.DataFrame, np.ndarray)  # nested pd.DataFrame, 2d or 3D np.ndarray
 VALID_Y_TYPES = (pd.Series, np.ndarray)  # 1-d vector
 
 
diff --git a/aeon/visualisation/results_plotting.py b/aeon/visualisation/results_plotting.py
index 359c6419..701b2641 100644
--- a/aeon/visualisation/results_plotting.py
+++ b/aeon/visualisation/results_plotting.py
@@ -321,8 +321,8 @@ def plot_scatter(
         zip(*sorted(zip(differences, first, second), key=lambda x: -abs(x[0]))),
     )
 
-    first_avg = first.mean()
-    second_avg = second.mean()
+    first_median = np.median(first)
+    second_median = np.median(second)
 
     plot = sns.scatterplot(
         x=second,
@@ -333,23 +333,32 @@ def plot_scatter(
         zorder=2,
     )
 
-    # Draw the average value per method as a dashed line from 0 to the mean value.
+    # Draw the median value per method as a dashed line from 0 to the median value.
     plt.plot(
-        [first_avg, min_value] if not lower_better else [first_avg, max_value],
-        [first_avg, first_avg],
+        [first_median, min_value] if not lower_better else [first_median, max_value],
+        [first_median, first_median],
         linestyle="--",
         color=palette[2],
         zorder=3,
     )
 
     plt.plot(
-        [second_avg, second_avg],
-        [second_avg, min_value] if not lower_better else [second_avg, max_value],
+        [second_median, second_median],
+        [second_median, min_value] if not lower_better else [second_median, max_value],
         linestyle="--",
         color=palette[0],
         zorder=3,
     )
 
+    legend_median = AnchoredText(
+        "*Dashed lines represent the median",
+        loc="lower right" if lower_better else "upper right",
+        prop=dict(size=8),
+        bbox_to_anchor=(1.01, 1.07 if lower_better else -0.07),
+        bbox_transform=ax.transAxes,
+    )
+    ax.add_artist(legend_median)
+
     # Compute the W, T, and L per methods
     if lower_better:
         differences = [-i for i in differences]
@@ -362,8 +371,10 @@ def plot_scatter(
         ax.spines["right"].set_visible(True)
 
     # Setting labels for x and y axis
-    plot.set_ylabel(f"{first_method} {metric}\n(avg.: {first_avg:.4f})", fontsize=13)
-    plot.set_xlabel(f"{second_method} {metric}\n(avg.: {second_avg:.4f})", fontsize=13)
+    plot.set_ylabel(f"{first_method} {metric}\n(mean: {first.mean():.4f})", fontsize=13)
+    plot.set_xlabel(
+        f"{second_method} {metric}\n(mean: {second.mean():.4f})", fontsize=13
+    )
 
     wins_A = losses_B = sum(i == 1 for i in differences)
     ties_A = ties_B = sum(i == 0 for i in differences)


Test patch:
diff --git a/aeon/datasets/tests/test_data_loaders.py b/aeon/datasets/tests/test_data_loaders.py
index 230dbc15..3fc82b09 100644
--- a/aeon/datasets/tests/test_data_loaders.py
+++ b/aeon/datasets/tests/test_data_loaders.py
@@ -91,6 +91,8 @@ def test_load_regression_from_repo():
     ):
         load_regression(name)
     name = "FloodModeling1"
+    name2 = "ParkingBirmingham"
+    name3 = "AcousticContaminationMadrid"
     with tempfile.TemporaryDirectory() as tmp:
         X, y, meta = load_regression(name, extract_path=tmp, return_metadata=True)
         assert isinstance(X, np.ndarray)
@@ -102,9 +104,28 @@ def test_load_regression_from_repo():
         assert not meta["timestamps"]
         assert meta["univariate"]
         assert meta["equallength"]
+        assert not meta["missing"]
         assert not meta["classlabel"]
         assert meta["targetlabel"]
         assert meta["class_values"] == []
+        # Test load equal length
+        X, y, meta = load_regression(
+            name2, extract_path=tmp, return_metadata=True, load_equal_length=True
+        )
+        assert meta["equallength"]
+        X, y, meta = load_regression(
+            name2, extract_path=tmp, return_metadata=True, load_equal_length=False
+        )
+        assert not meta["equallength"]
+        # Test load no missing values
+        X, y, meta = load_regression(
+            name3, extract_path=tmp, return_metadata=True, load_no_missing=True
+        )
+        assert not meta["missing"]
+        X, y, meta = load_regression(
+            name3, extract_path=tmp, return_metadata=True, load_no_missing=False
+        )
+        assert meta["missing"]
 
 
 @pytest.mark.skipif(
diff --git a/aeon/utils/_testing/scenarios_forecasting.py b/aeon/utils/_testing/scenarios_forecasting.py
index c086ec52..e05aec9b 100644
--- a/aeon/utils/_testing/scenarios_forecasting.py
+++ b/aeon/utils/_testing/scenarios_forecasting.py
@@ -17,7 +17,6 @@ from inspect import isclass
 import pandas as pd
 
 from aeon.base import BaseObject
-from aeon.datatypes import mtype_to_scitype
 from aeon.forecasting.base import BaseForecaster
 from aeon.tests.test_config import PR_TESTING
 from aeon.utils._testing.collection import _make_collection_X
@@ -70,12 +69,15 @@ class ForecasterTestScenario(TestScenario, BaseObject):
         # run Panel/Hierarchical scenarios for genuinely Panel/Hierarchical forecasters
         y_type = self.get_tag("y_type", "Series", raise_error=False)
         scenario_is_hierarchical = y_type in ["Panel", "Hierarchical"]
-
-        obj_y_inner_types = get_tag(obj, "y_inner_type")
-        obj_types = mtype_to_scitype(obj_y_inner_types)
-        obj_is_hierarchical = "Panel" in obj_types or "Hierarchical" in obj_types
-
-        # if scenario is hierarchical and obj is not genuinely hierarchical,
+        series_types = {"pd.Series", "pd.DataFrame", "np.ndarray"}
+        y_inner_types = get_tag(obj, "y_inner_type")
+        if isinstance(y_inner_types, str):
+            y_inner_types = {y_inner_types}
+        else:
+            y_inner_types = set(y_inner_types)
+        obj_is_hierarchical = not y_inner_types.issubset(series_types)
+
+        # if scenario is hierarchical and obj is not capable of hierarchical y,
         # this would trigger generic vectorization, which is tested in test_base
         if scenario_is_hierarchical and not obj_is_hierarchical:
             return False
diff --git a/aeon/utils/_testing/scenarios_transformers.py b/aeon/utils/_testing/scenarios_transformers.py
index 2d2ab833..85141796 100644
--- a/aeon/utils/_testing/scenarios_transformers.py
+++ b/aeon/utils/_testing/scenarios_transformers.py
@@ -47,6 +47,15 @@ def get_tag(obj, tag_name):
         return obj.get_tag(tag_name)
 
 
+def _internal_abstract_type(obj, inner_tag, series_types):
+    inner_types = get_tag(obj, inner_tag)
+    if isinstance(inner_types, str):
+        inner_types = {inner_types}
+    else:
+        inner_types = set(inner_types)
+    return not inner_types.issubset(series_types)
+
+
 class TransformerTestScenario(TestScenario, BaseObject):
     """Generic test scenario for transformers."""
 
diff --git a/aeon/utils/tests/test_datetime.py b/aeon/utils/tests/test_datetime.py
index 594cbab1..97fc1e97 100644
--- a/aeon/utils/tests/test_datetime.py
+++ b/aeon/utils/tests/test_datetime.py
@@ -8,8 +8,6 @@ import numpy as np
 import pandas as pd
 
 from aeon.datasets import load_airline
-from aeon.datatypes import VectorizedDF
-from aeon.utils._testing.hierarchical import _bottom_hier_datagen
 from aeon.utils.datetime import (
     _coerce_duration_to_int,
     _get_freq,
@@ -70,7 +68,6 @@ def test_coerce_duration_to_int() -> None:
 
 def test_infer_freq() -> None:
     """Test frequency inference."""
-    assert infer_freq(None) is None
 
     y = pd.Series(dtype=int)
     assert infer_freq(y) is None
@@ -91,38 +88,6 @@ def test_infer_freq() -> None:
     )
     assert infer_freq(y) == "M"
 
-    y = _bottom_hier_datagen(no_levels=2)
-    y = VectorizedDF(X=y, iterate_as="Series", is_scitype="Hierarchical")
-    assert infer_freq(y) == "M"
-
-
-def test_set_freq() -> None:
-    """Test frequency inference."""
-    assert infer_freq(None) is None
-
-    y = pd.Series(dtype=int)
-    assert infer_freq(y) is None
-
-    index = pd.date_range(start="2021-01-01", periods=1)
-    y = pd.Series(index=index, dtype=int)
-    assert infer_freq(y) == "D"
-
-    index = pd.date_range(start="2021-01-01", periods=1, freq="M")
-    y = pd.Series(index=index, dtype=int)
-    assert infer_freq(y) == "M"
-
-    y = pd.DataFrame({"a": 1}, index=pd.date_range(start="2021-01-01", periods=1))
-    assert infer_freq(y) == "D"
-
-    y = pd.DataFrame(
-        {"a": 1}, index=pd.date_range(start="2021-01-01", periods=1, freq="M")
-    )
-    assert infer_freq(y) == "M"
-
-    y = _bottom_hier_datagen(no_levels=2)
-    y = VectorizedDF(X=y, iterate_as="Series", is_scitype="Hierarchical")
-    assert infer_freq(y) == "M"
-
 
 def test_set_freq_hier():
     """Test that setting frequency on a DatetimeIndex MultiIndex works."""