Repository: aeon-toolkit/aeon (python)

Issue:
[ENH] make fit_predict_default configurable
### Describe the feature or idea you want to propose

currently fit_predict makes estimates on train data by default through cross validation. It hard codes the number of folds to 10 or the minimum number of cases in one class . I would like be able to set this to something other than 10, not immediately sure the best way of configuring this. 

It also always fits the whole model. I'd like to be able to turn that off.

The context is using fit_predict to score channels for channel selection. Would like it to be fast, so want 3x CV and not to build the whole model

### Describe your proposed solution

mocked up fit.
```python
        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)
        # Select the top n_keep channels
        sorted_indices = np.argsort(-scores)
        n_keep = math.ceil(n_channels * self.proportion)
        self.channels_selected_=sorted_indices[:n_keep]
```
Currently this builds 11 models per channel, assuming each class has at least 10 cases
```
    def _fit_predict_default(self, X, y, method):
        # fit the classifier
        self._fit(X, y)

        # predict using cross-validation
        cv_size = 10
        _, counts = np.unique(y, return_counts=True)
        min_class = np.min(counts)
        if min_class < cv_size:
            cv_size = min_class
            if cv_size < 2:
                raise ValueError(
                    f"All classes must have at least 2 values to run the "
                    f"_fit_{method} cross-validation."
                )

        random_state = getattr(self, "random_state", None)
        estimator = _clone_estimator(self, random_state)

        return cross_val_predict(
            estimator,
            X=X,
            y=y,
            cv=cv_size,
            method=method,
            n_jobs=self._n_jobs,
        )
```

could do it with kwargs for fit_predict maybe?

```
        for i in range(n_channels):
            preds=self.classifier.fit_predict(X[:,i,:],y, **{"cv_size":3,"full_model":False})
```



### Describe alternatives you've considered, if relevant
could I set it in the constructor or pass as an explicit parameter with default 10


Pull request description:
[ENH] at kwargs to fit_predict to allow setting of the cv_size parameter in default
adds `kwarg` parameter to `BaseClassifier fit_predict` to allow setting the number of folds in a cross validation.

This is important for channel selection algorithm `ChannelScorer`, where we want to reduce the CV size to speed things up. 

Also added tests to `score` for coverage

Wrorte the docs as I see it, feel free to suggest different text. 

resolves #1503 

Patch:
diff --git a/aeon/classification/base.py b/aeon/classification/base.py
index cf3316a2..ba3ac85c 100644
--- a/aeon/classification/base.py
+++ b/aeon/classification/base.py
@@ -21,7 +21,7 @@ State:
 __all__ = [
     "BaseClassifier",
 ]
-__maintainer__ = []
+__maintainer__ = ["TonyBagnall", "MatthewMiddlehurst"]
 
 import time
 from abc import ABC, abstractmethod
@@ -225,7 +225,7 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
         return self._predict_proba(X)
 
     @final
-    def fit_predict(self, X, y) -> np.ndarray:
+    def fit_predict(self, X, y, **kwargs) -> np.ndarray:
         """Fits the classifier and predicts class labels for X.
 
         fit_predict produces prediction estimates using just the train data.
@@ -262,6 +262,15 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
         y : np.ndarray
             1D np.array of float or str, of shape ``(n_cases)`` - class labels
             (ground truth) for fitting indices corresponding to instance indices in X.
+        kwargs : dict
+            key word arguments to configure the default cross validation if the base
+            class default fit_predict is used (i.e. if function ``_fit_predict`` is
+            not overridden. If ``_fit_predict`` is overridden, kwargs may not
+            function as expected. If ``_fit_predict`` is not overridden, valid input is
+            ``cv_size`` integer, which is the number of cross validation folds to use to
+            estimate train data. If ``cv_size`` is not passed, the default is 10.
+            If ``cv_size`` is greater than the minimum number of samples in any
+            class, it is set to this minimum.
 
         Returns
         -------
@@ -270,19 +279,18 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
             instance indices in
         """
         X, y, single_class = self._fit_setup(X, y)
-
         if single_class:
             n_cases = get_n_cases(X)
             y_pred = np.repeat(list(self._class_dictionary.keys()), n_cases)
         else:
-            y_pred = self._fit_predict(X, y)
+            y_pred = self._fit_predict(X, y, **kwargs)
 
         # this should happen last
         self._is_fitted = True
         return y_pred
 
     @final
-    def fit_predict_proba(self, X, y) -> np.ndarray:
+    def fit_predict_proba(self, X, y, **kwargs) -> np.ndarray:
         """Fits the classifier and predicts class label probabilities for X.
 
         fit_predict_proba produces probability estimates using just the train data.
@@ -320,6 +328,15 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
         y : np.ndarray
             1D np.array of float or str, of shape ``(n_cases)`` - class labels
             (ground truth) for fitting indices corresponding to instance indices in X.
+        kwargs : dict
+            key word arguments to configure the default cross validation if the base
+            class default fit_predict is used (i.e. if function ``_fit_predict`` is
+            not overridden. If ``_fit_predict`` is overridden, kwargs may not
+            function as expected. If ``_fit_predict`` is not overridden, valid input is
+            ``cv_size`` integer, which is the number of cross validation folds to use to
+            estimate train data. If ``cv_size`` is not passed, the default is 10.
+            If ``cv_size`` is greater than the minimum number of samples in any
+            class, it is set to this minimum.
 
         Returns
         -------
@@ -335,7 +352,7 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
             n_cases = get_n_cases(X)
             y_proba = np.repeat([[1]], n_cases, axis=0)
         else:
-            y_proba = self._fit_predict_proba(X, y)
+            y_proba = self._fit_predict_proba(X, y, **kwargs)
 
         # this should happen last
         self._is_fitted = True
@@ -495,7 +512,7 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
 
         return dists
 
-    def _fit_predict(self, X, y) -> np.ndarray:
+    def _fit_predict(self, X, y, **kwargs) -> np.ndarray:
         """Fits and predicts labels for sequences in X.
 
         Parameters
@@ -516,9 +533,10 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
             shape ``[n_cases]`` - predicted class labels indices correspond to
             instance indices in
         """
-        return self._fit_predict_default(X, y, "predict")
+        cv_size = BaseClassifier._get_folds(kwargs)
+        return self._fit_predict_default(X, y, "predict", cv_size)
 
-    def _fit_predict_proba(self, X, y) -> np.ndarray:
+    def _fit_predict_proba(self, X, y, **kwargs) -> np.ndarray:
         """Fits and predicts labels probabilities for sequences in X.
 
         Parameters
@@ -541,7 +559,8 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
             second dimension indices correspond to class labels, (i, j)-th entry is
             estimated probability that i-th instance is of class j
         """
-        return self._fit_predict_default(X, y, "predict_proba")
+        cv_size = BaseClassifier._get_folds(kwargs)
+        return self._fit_predict_default(X, y, "predict_proba", cv_size)
 
     def _fit_setup(self, X, y):
         # reset estimator at the start of fit
@@ -590,12 +609,11 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
 
         return y
 
-    def _fit_predict_default(self, X, y, method):
-        # fit the classifier
+    def _fit_predict_default(self, X, y, method, cv_size=10):
+        # fit the classifier to all the data
         self._fit(X, y)
 
-        # predict using cross-validation
-        cv_size = 10
+        # predict on training data using cross-validation
         _, counts = np.unique(y, return_counts=True)
         min_class = np.min(counts)
         if min_class < cv_size:
@@ -637,3 +655,16 @@ class BaseClassifier(BaseCollectionEstimator, ABC):
                     "but in predict it is ",
                     get_n_timepoints(X),
                 )
+
+    @staticmethod
+    def _get_folds(dict):
+        """Get the number of CV folds from kwargs dict."""
+        cv_size = 10
+        if "cv_size" in dict:
+            if not isinstance(dict["cv_size"], int) or dict["cv_size"] < 1:
+                raise ValueError(
+                    "cv_size must be an integer greater than 0, but found "
+                    f"{dict['cv_size']}"
+                )
+            cv_size = dict["cv_size"]
+        return cv_size
diff --git a/aeon/classification/dictionary_based/_boss.py b/aeon/classification/dictionary_based/_boss.py
index 365a4359..2aca7895 100644
--- a/aeon/classification/dictionary_based/_boss.py
+++ b/aeon/classification/dictionary_based/_boss.py
@@ -14,7 +14,7 @@ from joblib import Parallel, effective_n_jobs
 from sklearn.metrics import pairwise
 from sklearn.utils import check_random_state, gen_even_slices
 from sklearn.utils.extmath import safe_sparse_dot
-from sklearn.utils.fixes import delayed
+from sklearn.utils.parallel import delayed
 from sklearn.utils.sparsefuncs_fast import csr_row_norms
 from sklearn.utils.validation import _num_samples
 
diff --git a/aeon/classification/feature_based/_fresh_prince.py b/aeon/classification/feature_based/_fresh_prince.py
index 2b6ab132..2760c226 100644
--- a/aeon/classification/feature_based/_fresh_prince.py
+++ b/aeon/classification/feature_based/_fresh_prince.py
@@ -8,6 +8,7 @@ __maintainer__ = ["MatthewMiddlehurst"]
 __all__ = ["FreshPRINCEClassifier"]
 
 import numpy as np
+from sklearn.tree import DecisionTreeClassifier
 
 from aeon.classification.base import BaseClassifier
 from aeon.classification.sklearn import RotationForestClassifier
@@ -29,6 +30,12 @@ class FreshPRINCEClassifier(BaseClassifier):
         "comprehensive".
     n_estimators : int, default=200
         Number of estimators for the RotationForestClassifier ensemble.
+    base_estimator : BaseEstimator or None, default="None"
+        Base estimator for the ensemble. By default, uses the sklearn
+        `DecisionTreeClassifier` using entropy as a splitting measure.
+    pca_solver : str, default="auto"
+        Solver to use for the PCA ``svd_solver`` parameter in rotation forest. See the
+        scikit-learn PCA implementation for options.
     verbose : int, default=0
         Level of output printed to the console (for information only).
     n_jobs : int, default=1
@@ -81,6 +88,8 @@ class FreshPRINCEClassifier(BaseClassifier):
         self,
         default_fc_parameters="comprehensive",
         n_estimators=200,
+        base_estimator=None,
+        pca_solver="auto",
         verbose=0,
         n_jobs=1,
         chunksize=None,
@@ -88,6 +97,8 @@ class FreshPRINCEClassifier(BaseClassifier):
     ):
         self.default_fc_parameters = default_fc_parameters
         self.n_estimators = n_estimators
+        self.base_estimator = base_estimator
+        self.pca_solver = pca_solver
 
         self.verbose = verbose
         self.n_jobs = n_jobs
@@ -173,6 +184,8 @@ class FreshPRINCEClassifier(BaseClassifier):
 
         self._rotf = RotationForestClassifier(
             n_estimators=self.n_estimators,
+            base_estimator=self.base_estimator,
+            pca_solver=self.pca_solver,
             n_jobs=self._n_jobs,
             random_state=self.random_state,
         )
@@ -214,6 +227,10 @@ class FreshPRINCEClassifier(BaseClassifier):
         if parameter_set == "results_comparison":
             return {
                 "n_estimators": 10,
+                "base_estimator": DecisionTreeClassifier(
+                    criterion="entropy", max_depth=3
+                ),
+                "pca_solver": "full",
                 "default_fc_parameters": "minimal",
             }
         else:
diff --git a/aeon/classification/sklearn/_rotation_forest_classifier.py b/aeon/classification/sklearn/_rotation_forest_classifier.py
index 8a00d98d..37142c72 100644
--- a/aeon/classification/sklearn/_rotation_forest_classifier.py
+++ b/aeon/classification/sklearn/_rotation_forest_classifier.py
@@ -48,6 +48,9 @@ class RotationForestClassifier(ClassifierMixin, BaseEstimator):
     base_estimator : BaseEstimator or None, default="None"
         Base estimator for the ensemble. By default, uses the sklearn
         `DecisionTreeClassifier` using entropy as a splitting measure.
+    pca_solver : str, default="auto"
+        Solver to use for the PCA ``svd_solver`` parameter. See the scikit-learn PCA
+        implementation for options.
     time_limit_in_minutes : int, default=0
         Time contract to limit build time in minutes, overriding ``n_estimators``.
         Default of `0` means ``n_estimators`` is used.
@@ -104,6 +107,7 @@ class RotationForestClassifier(ClassifierMixin, BaseEstimator):
         max_group: int = 3,
         remove_proportion: float = 0.5,
         base_estimator: Optional[Type[BaseEstimator]] = None,
+        pca_solver: str = "auto",
         time_limit_in_minutes: float = 0.0,
         contract_max_n_estimators: int = 500,
         n_jobs: int = 1,
@@ -114,6 +118,7 @@ class RotationForestClassifier(ClassifierMixin, BaseEstimator):
         self.max_group = max_group
         self.remove_proportion = remove_proportion
         self.base_estimator = base_estimator
+        self.pca_solver = pca_solver
         self.time_limit_in_minutes = time_limit_in_minutes
         self.contract_max_n_estimators = contract_max_n_estimators
         self.n_jobs = n_jobs
@@ -315,6 +320,7 @@ class RotationForestClassifier(ClassifierMixin, BaseEstimator):
         start_time = time.time()
         train_time = 0
 
+        self._base_estimator = self.base_estimator
         if self.base_estimator is None:
             self._base_estimator = DecisionTreeClassifier(criterion="entropy")
 
@@ -422,7 +428,7 @@ class RotationForestClassifier(ClassifierMixin, BaseEstimator):
                 with np.errstate(divide="ignore", invalid="ignore"):
                     # differences between os occasionally. seems to happen when there
                     # are low amounts of cases in the fit
-                    pca = PCA(random_state=rng).fit(X_t)
+                    pca = PCA(random_state=rng, svd_solver=self.pca_solver).fit(X_t)
 
                 if not np.isnan(pca.explained_variance_ratio_).all():
                     break
diff --git a/aeon/regression/feature_based/_fresh_prince.py b/aeon/regression/feature_based/_fresh_prince.py
index 26a6db34..029e7535 100644
--- a/aeon/regression/feature_based/_fresh_prince.py
+++ b/aeon/regression/feature_based/_fresh_prince.py
@@ -8,6 +8,7 @@ __maintainer__ = ["MatthewMiddlehurst"]
 __all__ = ["FreshPRINCERegressor"]
 
 import numpy as np
+from sklearn.tree import DecisionTreeRegressor
 
 from aeon.regression.base import BaseRegressor
 from aeon.regression.sklearn import RotationForestRegressor
@@ -29,6 +30,12 @@ class FreshPRINCERegressor(BaseRegressor):
         "comprehensive".
     n_estimators : int, default=200
         Number of estimators for the RotationForestRegressor ensemble.
+    base_estimator : BaseEstimator or None, default="None"
+        Base estimator for the ensemble. By default, uses the sklearn
+        `DecisionTreeRegressor` using MSE as a splitting measure.
+    pca_solver : str, default="auto"
+        Solver to use for the PCA ``svd_solver`` parameter in rotation forest. See the
+        scikit-learn PCA implementation for options.
     verbose : int, default=0
         Level of output printed to the console (for information only)
     n_jobs : int, default=1
@@ -83,6 +90,8 @@ class FreshPRINCERegressor(BaseRegressor):
         self,
         default_fc_parameters="comprehensive",
         n_estimators=200,
+        base_estimator=None,
+        pca_solver="auto",
         verbose=0,
         n_jobs=1,
         chunksize=None,
@@ -90,6 +99,8 @@ class FreshPRINCERegressor(BaseRegressor):
     ):
         self.default_fc_parameters = default_fc_parameters
         self.n_estimators = n_estimators
+        self.base_estimator = base_estimator
+        self.pca_solver = pca_solver
 
         self.verbose = verbose
         self.n_jobs = n_jobs
@@ -153,6 +164,8 @@ class FreshPRINCERegressor(BaseRegressor):
 
         self._rotf = RotationForestRegressor(
             n_estimators=self.n_estimators,
+            base_estimator=self.base_estimator,
+            pca_solver=self.pca_solver,
             n_jobs=self._n_jobs,
             random_state=self.random_state,
         )
@@ -194,6 +207,8 @@ class FreshPRINCERegressor(BaseRegressor):
         if parameter_set == "results_comparison":
             return {
                 "n_estimators": 10,
+                "base_estimator": DecisionTreeRegressor(max_depth=3),
+                "pca_solver": "randomized",
                 "default_fc_parameters": "minimal",
             }
         else:
diff --git a/aeon/regression/sklearn/_rotation_forest_regressor.py b/aeon/regression/sklearn/_rotation_forest_regressor.py
index 527d54bb..8e8c60f7 100644
--- a/aeon/regression/sklearn/_rotation_forest_regressor.py
+++ b/aeon/regression/sklearn/_rotation_forest_regressor.py
@@ -49,6 +49,9 @@ class RotationForestRegressor(RegressorMixin, BaseEstimator):
     base_estimator : BaseEstimator or None, default="None"
         Base estimator for the ensemble. By default, uses the sklearn
         `DecisionTreeRegressor` using MSE as a splitting measure.
+    pca_solver : str, default="auto"
+        Solver to use for the PCA ``svd_solver`` parameter. See the scikit-learn PCA
+        implementation for options.
     time_limit_in_minutes : int, default=0
         Time contract to limit build time in minutes, overriding ``n_estimators``.
         Default of `0` means ``n_estimators`` is used.
@@ -102,6 +105,7 @@ class RotationForestRegressor(RegressorMixin, BaseEstimator):
         max_group: int = 3,
         remove_proportion: float = 0.5,
         base_estimator: Optional[Type[BaseEstimator]] = None,
+        pca_solver: str = "auto",
         time_limit_in_minutes: float = 0.0,
         contract_max_n_estimators: int = 500,
         n_jobs: int = 1,
@@ -112,6 +116,7 @@ class RotationForestRegressor(RegressorMixin, BaseEstimator):
         self.max_group = max_group
         self.remove_proportion = remove_proportion
         self.base_estimator = base_estimator
+        self.pca_solver = pca_solver
         self.time_limit_in_minutes = time_limit_in_minutes
         self.contract_max_n_estimators = contract_max_n_estimators
         self.n_jobs = n_jobs
@@ -229,6 +234,7 @@ class RotationForestRegressor(RegressorMixin, BaseEstimator):
         start_time = time.time()
         train_time = 0
 
+        self._base_estimator = self.base_estimator
         if self.base_estimator is None:
             self._base_estimator = DecisionTreeRegressor(criterion="squared_error")
 
@@ -321,7 +327,7 @@ class RotationForestRegressor(RegressorMixin, BaseEstimator):
                 with np.errstate(divide="ignore", invalid="ignore"):
                     # differences between os occasionally. seems to happen when there
                     # are low amounts of cases in the fit
-                    pca = PCA(random_state=rng).fit(X_t)
+                    pca = PCA(random_state=rng, svd_solver=self.pca_solver).fit(X_t)
 
                 if not np.isnan(pca.explained_variance_ratio_).all():
                     break
diff --git a/examples/distances/sklearn_distances.ipynb b/examples/distances/sklearn_distances.ipynb
index 0ba0dc63..48f32bee 100644
--- a/examples/distances/sklearn_distances.ipynb
+++ b/examples/distances/sklearn_distances.ipynb
@@ -29,19 +29,22 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
-   "outputs": [],
    "source": [
     "from sklearn.neighbors import (\n",
     "    KNeighborsClassifier,\n",
     "    KNeighborsRegressor,\n",
     "    KNeighborsTransformer,\n",
-    "    NearestCentroid,\n",
     ")"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:23.882870Z",
+     "start_time": "2024-06-17T23:21:23.055803Z"
+    }
+   },
+   "outputs": [],
+   "execution_count": 1
   },
   {
    "cell_type": "markdown",
@@ -60,18 +63,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "KNN with manhatttan distance on 2D time series data first five  ['1' '2' '2' '1' '1']\n",
-      "Shape of train =  (50, 1, 150) sklearn will crash\n",
-      "raises an ValueError: Found array with dim 3.KNeighborsClassifier expected <= 2.\n"
-     ]
-    }
-   ],
    "source": [
     "from aeon.datasets import load_gunpoint\n",
     "\n",
@@ -96,8 +87,24 @@
     "    )"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:24.416318Z",
+     "start_time": "2024-06-17T23:21:23.884866Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "KNN with manhatttan distance on 2D time series data first five  ['1' '2' '2' '1' '1']\n",
+      "Shape of train =  (50, 1, 150) sklearn will crash\n",
+      "raises an ValueError: Found array with dim 3.KNeighborsClassifier expected <= 2.\n"
+     ]
+    }
+   ],
+   "execution_count": 2
   },
   {
    "cell_type": "markdown",
@@ -112,41 +119,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "KNN with DTW on 2D time series data first five predictions=  ['1' '2' '2' '1' '2']\n",
-      "raises an ValueError: Found array with dim 3. KNeighborsClassifier expected <= 2.\n",
-      "nc with ERP on 2D time series data first five predictions=  ['1' '1' '2' '1' '1']\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "C:\\Code\\aeon\\venv\\lib\\site-packages\\sklearn\\neighbors\\_nearest_centroid.py:179: UserWarning: Averaging for metrics other than euclidean and manhattan not supported. The average is set to be the mean.\n",
-      "  warnings.warn(\n"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "nc with ERP on 2D time series data transform shape =  (150, 50)\n"
-     ]
-    }
-   ],
    "source": [
-    "from aeon.distances import (\n",
-    "    dtw_distance,\n",
-    "    edr_distance,\n",
-    "    erp_distance,\n",
-    "    msm_distance,\n",
-    "    twe_distance,\n",
-    ")\n",
+    "from aeon.distances import dtw_distance, edr_distance, msm_distance, twe_distance\n",
     "\n",
     "# Use directly on TSC data with aeon distance function\n",
     "knn = KNeighborsClassifier(metric=dtw_distance)\n",
@@ -162,12 +136,6 @@
     "        \"raises a ValueError: Found array with dim 3. \"\n",
     "        \"KNeighborsClassifier expected <= 2.\"\n",
     "    )\n",
-    "nc = NearestCentroid(metric=erp_distance)\n",
-    "nc.fit(trainx1, trainy1)\n",
-    "print(\n",
-    "    \"nc with ERP on 2D time series data first five predictions= \",\n",
-    "    nc.predict(testx1)[:5],\n",
-    ")\n",
     "kt = KNeighborsTransformer(metric=edr_distance)\n",
     "kt.fit(trainx1, trainy1)\n",
     "print(\n",
@@ -175,8 +143,24 @@
     ")"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:26.806946Z",
+     "start_time": "2024-06-17T23:21:24.417316Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "KNN with DTW on 2D time series data first five predictions=  ['1' '2' '2' '1' '2']\n",
+      "raises a ValueError: Found array with dim 3. KNeighborsClassifier expected <= 2.\n",
+      "nc with ERP on 2D time series data transform shape =  (150, 50)\n"
+     ]
+    }
+   ],
+   "execution_count": 3
   },
   {
    "cell_type": "markdown",
@@ -192,18 +176,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "KNN classification with MSM 3D time series data first five predictions= ['1' '2' '2' '1' '1']\n",
-      "aeon KNN regression with TWE first five predictions= [0.02558824 0.00877193 0.01960784 0.03533314 0.00805611]\n",
-      "sklearn KNN regression with TWE first five predictions= [0.02558824 0.00877193 0.01960784 0.03533314 0.00805611]\n"
-     ]
-    }
-   ],
    "source": [
     "from aeon.classification.distance_based import KNeighborsTimeSeriesClassifier\n",
     "from aeon.datasets import load_covid_3month  # Regression problem\n",
@@ -233,8 +205,24 @@
     ")"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:31.418483Z",
+     "start_time": "2024-06-17T23:21:26.808941Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "KNN classification with MSM 3D time series data first five predictions= ['1' '2' '2' '1' '1']\n",
+      "aeon KNN regression with TWE first five predictions= [0.02558824 0.00877193 0.01960784 0.03533314 0.00805611]\n",
+      "sklearn KNN regression with TWE first five predictions= [0.02558824 0.00877193 0.01960784 0.03533314 0.00805611]\n"
+     ]
+    }
+   ],
+   "execution_count": 4
   },
   {
    "cell_type": "markdown",
@@ -249,16 +237,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "KNN precomputed= ['1' '2' '2' '1' '1']\n"
-     ]
-    }
-   ],
    "source": [
     "from aeon.distances import euclidean_pairwise_distance\n",
     "\n",
@@ -269,8 +247,22 @@
     "print(\"KNN precomputed=\", knn.predict(test_dists)[:5])"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:32.354262Z",
+     "start_time": "2024-06-17T23:21:31.419481Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "KNN precomputed= ['1' '2' '2' '1' '1']\n"
+     ]
+    }
+   ],
+   "execution_count": 5
   },
   {
    "cell_type": "markdown",
@@ -283,14 +275,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
-   "outputs": [],
    "source": [
     "from sklearn.svm import SVC, SVR, NuSVC, NuSVR"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:32.360245Z",
+     "start_time": "2024-06-17T23:21:32.355286Z"
+    }
+   },
+   "outputs": [],
+   "execution_count": 6
   },
   {
    "cell_type": "markdown",
@@ -306,18 +302,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "SVC with TWE first five predictions=  ['1' '2' '1' '2' '2']\n",
-      "NuSVC with MSM first five predictions=  ['1' '2' '2' '1' '2']\n",
-      "SVR with DTW first five predictions=  [0.08823529 0.08823529 0.08823529 0.08823529 0.08823529]\n"
-     ]
-    }
-   ],
    "source": [
     "from aeon.distances import (\n",
     "    dtw_pairwise_distance,\n",
@@ -344,8 +328,24 @@
     "print(\"SVR with DTW first five predictions= \", svr.predict(test_m3)[:5])"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:39.997149Z",
+     "start_time": "2024-06-17T23:21:32.361244Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "SVC with TWE first five predictions=  ['1' '2' '1' '2' '2']\n",
+      "NuSVC with MSM first five predictions=  ['1' '2' '2' '1' '2']\n",
+      "SVR with DTW first five predictions=  [0.08823529 0.08823529 0.08823529 0.08823529 0.08823529]\n"
+     ]
+    }
+   ],
+   "execution_count": 7
   },
   {
    "cell_type": "markdown",
@@ -358,14 +358,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
-   "outputs": [],
    "source": [
     "from sklearn.cluster import DBSCAN"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:40.488336Z",
+     "start_time": "2024-06-17T23:21:39.998146Z"
+    }
+   },
+   "outputs": [],
+   "execution_count": 8
   },
   {
    "cell_type": "markdown",
@@ -382,18 +386,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[-1  0  0  0  0]\n",
-      "[-1  0  0  0  0]\n",
-      "[-1  0  0  0  0]\n"
-     ]
-    }
-   ],
    "source": [
     "db1 = DBSCAN(eps=2.5)\n",
     "preds1 = db1.fit_predict(trainx1)\n",
@@ -406,8 +398,24 @@
     "print(preds1[:5])"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:41.393371Z",
+     "start_time": "2024-06-17T23:21:40.489328Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[-1  0  0  0  0]\n",
+      "[-1  0  0  0  0]\n",
+      "[-1  0  0  0  0]\n"
+     ]
+    }
+   ],
+   "execution_count": 9
   },
   {
    "cell_type": "markdown",
@@ -421,17 +429,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      " Shape =  (67, 67)\n",
-      "These should be the same 7.595223506000001 and 7.595223506000001\n"
-     ]
-    }
-   ],
    "source": [
     "from sklearn.preprocessing import FunctionTransformer\n",
     "\n",
@@ -446,8 +443,23 @@
     "print(f\"These should be the same {d} and {X2[0][1]}\")"
    ],
    "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:41.472132Z",
+     "start_time": "2024-06-17T23:21:41.395337Z"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " Shape =  (67, 67)\n",
+      "These should be the same 7.595223506000001 and 7.595223506000001\n"
+     ]
+    }
+   ],
+   "execution_count": 10
   },
   {
    "cell_type": "markdown",
@@ -461,17 +473,6 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
-   "outputs": [
-    {
-     "data": {
-      "text/plain": "array(['1', '1', '2', '2', '1', '2', '2', '1', '1', '2', '2', '1', '1',\n       '2', '1', '2', '1', '1', '2', '1', '1', '2', '1', '1', '1', '1',\n       '1', '2', '2', '1', '1', '2', '2', '1', '2', '2', '1', '2', '1',\n       '2', '1', '1', '2', '2', '1', '2', '2', '2', '2', '1', '1', '2',\n       '2', '2', '1', '2', '2', '1', '1', '2', '2', '1', '1', '2', '1',\n       '2', '2'], dtype='<U1')"
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
    "source": [
     "from sklearn.pipeline import Pipeline\n",
     "\n",
@@ -482,17 +483,30 @@
     "pipe.predict(X)"
    ],
    "metadata": {
-    "collapsed": false
-   }
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "outputs": [],
-   "source": [],
-   "metadata": {
-    "collapsed": false
-   }
+    "collapsed": false,
+    "ExecuteTime": {
+     "end_time": "2024-06-17T23:21:41.503125Z",
+     "start_time": "2024-06-17T23:21:41.473128Z"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array(['1', '1', '2', '2', '1', '2', '2', '1', '1', '2', '2', '1', '1',\n",
+       "       '2', '1', '2', '1', '1', '2', '1', '1', '2', '1', '1', '1', '1',\n",
+       "       '1', '2', '2', '1', '1', '2', '2', '1', '2', '2', '1', '2', '1',\n",
+       "       '2', '1', '1', '2', '2', '1', '2', '2', '2', '2', '1', '1', '2',\n",
+       "       '2', '2', '1', '2', '2', '1', '1', '2', '2', '1', '1', '2', '1',\n",
+       "       '2', '2'], dtype='<U1')"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "execution_count": 11
   }
  ],
  "metadata": {
diff --git a/pyproject.toml b/pyproject.toml
index 1ebe7a58..7d9ea9db 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -50,7 +50,7 @@ dependencies = [
     "numpy>=1.21.0,<1.27.0",
     "packaging>=20.0",
     "pandas>=1.5.3,<2.1.0",
-    "scikit-learn>=1.0.0,<1.5.0",
+    "scikit-learn>=1.0.0,<1.6.0",
     "scipy>=1.9.0,<1.13.0",
     "typing-extensions>=4.6.0",
 ]


Test patch:
diff --git a/aeon/classification/sklearn/tests/test_continuous_interval_tree.py b/aeon/classification/sklearn/tests/test_continuous_interval_tree.py
index fbef4fe1..cd794aa8 100644
--- a/aeon/classification/sklearn/tests/test_continuous_interval_tree.py
+++ b/aeon/classification/sklearn/tests/test_continuous_interval_tree.py
@@ -6,14 +6,48 @@ from sklearn.exceptions import NotFittedError
 
 from aeon.classification.sklearn import ContinuousIntervalTree
 from aeon.classification.sklearn._continuous_interval_tree import _TreeNode
+from aeon.datasets import load_unit_test
 from aeon.testing.data_generation import (
     make_example_2d_numpy_collection,
     make_example_3d_numpy,
 )
 
 
-def test_predict_proba():
-    """Test ContinuousIntervalTree predict_proba."""
+def test_cit_output():
+    """Test of RotF contracting and train estimate on test data."""
+    X_train, y_train = load_unit_test(split="train", return_type="numpy2d")
+    X_test, y_test = load_unit_test(split="test", return_type="numpy2d")
+
+    cit = ContinuousIntervalTree(
+        random_state=0,
+    )
+    cit.fit(X_train, y_train)
+
+    expected = [
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [0.0, 1.0],
+        [0.0, 1.0],
+        [1.0, 0.0],
+    ]
+
+    np.testing.assert_array_almost_equal(
+        expected, cit.predict_proba(X_test[:15]), decimal=4
+    )
+
+
+def test_cit_edge_cases():
+    """Test ContinuousIntervalTree edge cases."""
     X, y = make_example_2d_numpy_collection(n_cases=5, n_labels=1)
     cit = ContinuousIntervalTree(max_depth=1)
     with pytest.raises(NotFittedError, match="please call `fit` first"):
@@ -21,14 +55,16 @@ def test_predict_proba():
     model = cit.fit(X, y)
     assert isinstance(model, ContinuousIntervalTree)
     preds = cit.predict(X)
-    assert np.all(preds == 0)
     # Should predict all 0
+    assert np.all(preds == 0)
+
     X, y = make_example_2d_numpy_collection(n_cases=5)
     cit = ContinuousIntervalTree(max_depth=1)
     cit.fit(X, y)
     node = cit._root
     assert isinstance(node, _TreeNode)
     assert len(node.children[0].children) == 0
+
     X, y = make_example_3d_numpy(n_channels=3)
     with pytest.raises(
         ValueError, match="ContinuousIntervalTree is not a time series classifier"
@@ -36,13 +72,10 @@ def test_predict_proba():
         cit.fit(X, y)
 
 
-def test_nan_values():
+def test_cit_nan_values():
     """Test that ContinuousIntervalTree can handle NaN values."""
-    rng = np.random.RandomState(0)
-    X = rng.uniform(size=(10, 3))
+    X, y = make_example_2d_numpy_collection()
     X[0:3, 0] = np.nan
-    y = np.zeros(10)
-    y[:5] = 1
 
     clf = ContinuousIntervalTree()
     clf.fit(X, y)
diff --git a/aeon/classification/sklearn/tests/test_rotation_forest_classifier.py b/aeon/classification/sklearn/tests/test_rotation_forest_classifier.py
index 94d9c3ab..605203d6 100644
--- a/aeon/classification/sklearn/tests/test_rotation_forest_classifier.py
+++ b/aeon/classification/sklearn/tests/test_rotation_forest_classifier.py
@@ -7,6 +7,41 @@ from aeon.classification.sklearn import RotationForestClassifier
 from aeon.datasets import load_unit_test
 
 
+def test_rotf_output():
+    """Test of RotF contracting and train estimate on test data."""
+    X_train, y_train = load_unit_test(split="train", return_type="numpy2d")
+    X_test, y_test = load_unit_test(split="test", return_type="numpy2d")
+
+    rotf = RotationForestClassifier(
+        n_estimators=10,
+        pca_solver="randomized",
+        random_state=0,
+    )
+    rotf.fit(X_train, y_train)
+
+    expected = [
+        [0.8, 0.2],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [0.8, 0.2],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [0.9, 0.1],
+        [0.9, 0.1],
+        [1.0, 0.0],
+        [1.0, 0.0],
+        [0.8, 0.2],
+        [0.9, 0.1],
+        [0.0, 1.0],
+        [0.1, 0.9],
+        [0.4, 0.6],
+    ]
+
+    np.testing.assert_array_almost_equal(
+        expected, rotf.predict_proba(X_test[:15]), decimal=4
+    )
+
+
 def test_contracted_rotf():
     """Test of RotF contracting and train estimate on test data."""
     X_train, y_train = load_unit_test(split="train", return_type="numpy2d")
diff --git a/aeon/classification/tests/test_base.py b/aeon/classification/tests/test_base.py
index dcc8a171..200e19d4 100644
--- a/aeon/classification/tests/test_base.py
+++ b/aeon/classification/tests/test_base.py
@@ -262,6 +262,61 @@ def test_fit_predict():
     assert p.shape == (5,)
 
 
+def test_fit_predict_kwargs():
+    """Test fit_predict with cross validation kwargs."""
+    X = np.random.random(size=(5, 1, 10))
+    y = np.array([1, 0, 1, 0, 1])
+    cls = MockClassifier()
+    p = cls.fit_predict(X, y)
+    assert p.shape == (5,)
+
+    p = cls.fit_predict(X, y, cv_size=2)
+    assert p.shape == (5,)
+    with pytest.raises(ValueError, match="cv_size must be an integer greater than 0"):
+        cls.fit_predict(X, y, cv_size=0)
+    with pytest.raises(ValueError, match="cv_size must be an integer greater than 0"):
+        cls.fit_predict(X, y, cv_size="FOO")
+    y = np.array([0, 0, 0, 0, 1])
+    with pytest.raises(ValueError, match="All classes must have at least 2 values"):
+        cls.fit_predict(X, y, cv_size=20)
+    y = np.array([1, 0, 1, 0, 1])
+    p = cls.fit_predict_proba(X, y, cv_size=2)
+    assert p.shape == (5, 2)
+
+
+def test_score():
+    """Test base classifier scorer."""
+    X = np.random.random(size=(5, 1, 10))
+    y = np.array([1, 0, 1, 0, 1])
+    cls = MockClassifier()
+    cls.fit(X, y)
+    score = cls.score(X, y)
+    assert isinstance(score, float)
+    with pytest.raises(
+        ValueError,
+        match="can't handle a mix of binary and multilabel-indicator targets",
+    ):
+        score = cls.score(X, y, use_proba=True)
+    with pytest.raises(
+        ValueError,
+        match="can't handle a mix of binary and multilabel-indicator targets",
+    ):
+        score = cls.score(X, y, use_proba=True)
+    score = cls.score(X, y, metric="neg_log_loss")
+    assert isinstance(score, float)
+    score = cls.score(X, y, use_proba=True, metric="neg_log_loss")
+    with pytest.raises(
+        ValueError, match="The metric parameter should be either a string or a callable"
+    ):
+        score = cls.score(X, y, metric=42)
+
+    def dummy_metric(y_true, y_pred):
+        return 42.0
+
+    score = cls.score(X, y, metric=dummy_metric)
+    assert score == 42.0
+
+
 def test_fit_predict_single_class():
     """Test return of fit_predict/fit_predict_proba in case only single class."""
     X = np.ones(shape=(10, 20))
diff --git a/aeon/classification/tests/test_sklearn_compatability.py b/aeon/classification/tests/test_sklearn_compatability.py
index a949764d..11e3c3a3 100644
--- a/aeon/classification/tests/test_sklearn_compatability.py
+++ b/aeon/classification/tests/test_sklearn_compatability.py
@@ -73,7 +73,7 @@ COMPOSITE_ESTIMATORS = [
     ),
     CalibratedClassifierCV(
         estimator=CanonicalIntervalForestClassifier.create_test_instance(),
-        cv=3,
+        cv=2,
     ),
 ]
 
@@ -104,7 +104,7 @@ def test_sklearn_parameter_tuning(parameter_tuning_method):
     X, y = make_example_3d_numpy(n_cases=20, n_channels=2, n_timepoints=30)
 
     parameter_tuning_method = parameter_tuning_method(
-        clf, param_grid, cv=KFold(n_splits=3)
+        clf, param_grid, cv=KFold(n_splits=2)
     )
     parameter_tuning_method.fit(X, y)
     assert isinstance(
diff --git a/aeon/regression/sklearn/tests/test_all_regressors.py b/aeon/regression/sklearn/tests/test_all_regressors.py
index fdd23d92..f23b11c7 100644
--- a/aeon/regression/sklearn/tests/test_all_regressors.py
+++ b/aeon/regression/sklearn/tests/test_all_regressors.py
@@ -1,4 +1,4 @@
-"""Unit tests for sklearn classifiers."""
+"""Unit tests for sklearn regressors."""
 
 __maintainer__ = ["MatthewMiddlehurst"]
 
diff --git a/aeon/regression/sklearn/tests/test_rotation_forest_regressor.py b/aeon/regression/sklearn/tests/test_rotation_forest_regressor.py
index 7e95090e..ef26b760 100644
--- a/aeon/regression/sklearn/tests/test_rotation_forest_regressor.py
+++ b/aeon/regression/sklearn/tests/test_rotation_forest_regressor.py
@@ -4,16 +4,51 @@ __maintainer__ = ["MatthewMiddlehurst"]
 
 import numpy as np
 from sklearn.metrics import mean_squared_error
+from sklearn.tree import DecisionTreeRegressor
 
 from aeon.datasets import load_covid_3month
 from aeon.regression.sklearn import RotationForestRegressor
 
 
-def test_contracted_rotf():
+def test_rotf_output():
     """Test of RotF contracting and train estimate on test data."""
     X_train, y_train = load_covid_3month(split="train", return_type="numpy2d")
     X_test, y_test = load_covid_3month(split="test", return_type="numpy2d")
 
+    rotf = RotationForestRegressor(
+        n_estimators=10,
+        base_estimator=DecisionTreeRegressor(max_depth=3),
+        pca_solver="randomized",
+        random_state=0,
+    )
+    rotf.fit(X_train, y_train)
+
+    expected = [
+        0.02694297,
+        0.02694297,
+        0.01997832,
+        0.04276962,
+        0.09027588,
+        0.02706564,
+        0.02553648,
+        0.04075808,
+        0.02900289,
+        0.04248546,
+        0.02694297,
+        0.03667328,
+        0.0235855,
+        0.03444119,
+        0.0235855,
+    ]
+
+    np.testing.assert_array_almost_equal(expected, rotf.predict(X_test[:15]), decimal=4)
+
+
+def test_contracted_rotf():
+    """Test of RotF contracting on testing data."""
+    X_train, y_train = load_covid_3month(split="train", return_type="numpy2d")
+    X_test, y_test = load_covid_3month(split="test", return_type="numpy2d")
+
     rotf = RotationForestRegressor(
         time_limit_in_minutes=5,
         contract_max_n_estimators=5,
@@ -32,7 +67,7 @@ def test_contracted_rotf():
 
 
 def test_rotf_fit_predict():
-    """Test of RotF fit_predict on test data."""
+    """Test of RotF fit_predict on testing data."""
     X_train, y_train = load_covid_3month(split="train", return_type="numpy2d")
 
     rotf = RotationForestRegressor(
diff --git a/aeon/testing/expected_results/expected_classifier_outputs.py b/aeon/testing/expected_results/expected_classifier_outputs.py
index 1a1432f5..d27f2d18 100644
--- a/aeon/testing/expected_results/expected_classifier_outputs.py
+++ b/aeon/testing/expected_results/expected_classifier_outputs.py
@@ -686,14 +686,14 @@ basic_motions_proba["FreshPRINCEClassifier"] = np.array(
     [
         [0.0, 0.0, 0.1, 0.9],
         [0.9, 0.1, 0.0, 0.0],
-        [0.0, 0.0, 0.9, 0.1],
-        [0.2, 0.8, 0.0, 0.0],
+        [0.0, 0.0, 0.8, 0.2],
+        [0.1, 0.9, 0.0, 0.0],
         [0.1, 0.0, 0.0, 0.9],
         [0.0, 0.0, 0.1, 0.9],
-        [0.6, 0.4, 0.0, 0.0],
-        [0.0, 0.0, 0.9, 0.1],
+        [0.7, 0.3, 0.0, 0.0],
+        [0.0, 0.0, 1.0, 0.0],
         [0.0, 1.0, 0.0, 0.0],
-        [0.2, 0.7, 0.0, 0.1],
+        [0.1, 0.8, 0.0, 0.1],
     ]
 )
 basic_motions_proba["RandomIntervalClassifier"] = np.array(
diff --git a/aeon/testing/expected_results/expected_regressor_outputs.py b/aeon/testing/expected_results/expected_regressor_outputs.py
index ea6b6c3d..c9fb4420 100644
--- a/aeon/testing/expected_results/expected_regressor_outputs.py
+++ b/aeon/testing/expected_results/expected_regressor_outputs.py
@@ -11,30 +11,30 @@ cardano_sentiment_preds = dict()
 
 covid_3month_preds["FreshPRINCERegressor"] = np.array(
     [
-        0.0491,
-        0.0781,
-        0.0409,
-        0.0412,
-        0.0612,
-        0.0337,
-        0.0492,
+        0.0545,
+        0.0767,
+        0.0347,
+        0.0505,
+        0.0683,
+        0.0434,
+        0.0674,
         0.1111,
-        0.1029,
-        0.0344,
+        0.0946,
+        0.0366,
     ]
 )
 
 cardano_sentiment_preds["FreshPRINCERegressor"] = np.array(
     [
-        0.3672,
-        0.108,
-        0.3813,
-        0.0617,
-        0.5077,
-        0.3122,
-        0.1335,
-        0.3857,
-        0.0954,
-        0.4744,
+        0.3484,
+        0.1438,
+        0.3705,
+        0.0842,
+        0.3892,
+        0.3705,
+        0.1342,
+        0.3476,
+        0.0959,
+        0.409,
     ]
 )