Repository: aeon-toolkit/aeon
Pull request #270 (2023-04-06 14:12:07)

[ENH] convert SlopeTransformer to use numpy internally
there is a theme here :)

This is the last of the ShapeDTW transformers that need conversion. As with others, there is a lot of scope for speeding up with vectorisation or numba, but we can leave that for another day, too many of these to do first! It pleases me each time I delete import pandas as pd though :)

Note I have also decreased the test problem size as this PR was failing because of it, see #272 

#### Reference Issues/PRs
#197 #98



Patch:
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 9d8e96e2..809d1985 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,12 +1,12 @@
 # These are supported funding model platforms
 
-github: scikit-time
-patreon: # replace with username
-open_collective: # replace with username
-ko_fi: # replace with username
-tidelift: # replace with username
-community_bridge: # replace with username
-liberapay: # replace with username
-issuehunt: # replace with username
-otechie: # replace with username
-custom: # replace with custom URLs e.g., ['link1', 'link2']
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with custom URLs e.g., ['link1', 'link2']
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index b7d8da7b..c9bf2f11 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,18 +1,18 @@
 ---
 name: "\U0001F41B Bug report"
-about: Create a report to help us improve
+about: Report a bug currently impacting aeon.
 title: "[BUG]"
 labels: bug
-assignees: ''
-
 ---
 
-**Describe the bug**
+#### Describe the bug
+
 <!--
 A clear and concise description of what the bug is.
 -->
 
-**To Reproduce**
+#### To Reproduce
+
 <!--
 Add a Minimal, Complete, and Verifiable example (for more details, see e.g. https://stackoverflow.com/help/mcve
 
@@ -23,17 +23,19 @@ If the code is too long, feel free to put it in a public gist and link it in the
 <Paste your code here>
 ```
 
-**Expected behavior**
+#### Expected behavior
+
 <!--
 A clear and concise description of what you expected to happen.
 -->
 
-**Additional context**
+#### Additional context
+
 <!--
 Add any other context about the problem here.
 -->
 
-**Versions**
+#### Versions
 <details>
 
 <!--
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 82dc1218..5930ab1e 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,7 +1,7 @@
 contact_links:
   - name: "\U0001F4AC All other questions and general chat"
-    url: https://join.slack.com/t/scikit-timeworkspace/shared_invite/zt-1pkhua342-W_W24XuAZt2JZU1GniK2YA
+    url: https://join.slack.com/t/aeon-toolkit/shared_invite/zt-1plkevy4x-vAg1dAUXcuoR38FjY9nxzg
     about: Chat with the aeon community on Slack
   - name: "\u2709\uFE0F Code of Conduct incident reporting"
     url: https://www.aeon-toolkit.org/en/latest/get_involved/code_of_conduct.html#incident-reporting-guidelines
-    about: Report an incident to the Code of Conduct committee
+    about: Report an incident to the Code of Conduct Committee
diff --git a/.github/ISSUE_TEMPLATE/doc_improvement.md b/.github/ISSUE_TEMPLATE/doc_improvement.md
index 63d56fae..e9a4b3ff 100644
--- a/.github/ISSUE_TEMPLATE/doc_improvement.md
+++ b/.github/ISSUE_TEMPLATE/doc_improvement.md
@@ -1,10 +1,8 @@
 ---
 name: "\U0001F4D6 Documentation improvement"
-about: Create a report to help us improve the documentation. Alternatively you can just open a pull request with the suggested change.
+about: Create a report to help us improve the aeon documentation.
 title: "[DOC]"
 labels: documentation
-assignees: ''
-
 ---
 
 #### Describe the issue linked to the documentation
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index f89493f5..943a8216 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,20 +1,30 @@
 ---
 name: "\u2728 Feature request"
-about: Suggest an idea for this project
-title: '[ENH]'
+about: Suggest a new algorithm or idea for aeon.
+title: "[ENH]"
 labels: enhancement
-assignees: ''
-
 ---
 
-**Is your feature request related to a problem? Please describe.**
+#### Is your feature request related to a problem? Please describe.
+
+<!--
 A clear and concise description of what the problem is.
+-->
+
+#### Describe the solution you'd like
 
-**Describe the solution you'd like**
+<!--
 A clear and concise description of what you want to happen, ideally taking into consideration the existing toolbox design, classes and methods.
+-->
 
-**Describe alternatives you've considered**
+#### Describe alternatives you've considered, if relevant
+
+<!--
 A clear and concise description of any alternative solutions or features you've considered.
+-->
+
+#### Additional context
 
-**Additional context**
-Add any other context or screenshots about the feature request here.
+<!--
+Add any other context about the problem here.
+-->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8edabf51..8faace28 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,19 +1,19 @@
 <!--
-Thanks for contributing a pull request! Please ensure you have taken a look
-at our contribution guide: https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTING.md
+Thanks for contributing a pull request! Please ensure you have taken a look at our contribution guide: https://github.com/aeon-toolkit/aeon/blob/main/CONTRIBUTING.md
+
+Feel free to delete sections of this template if they do not apply to your PR, avoid submitting a blank template or empty sections.
 -->
 
 #### Reference Issues/PRs
+
 <!--
 Example: Fixes #1234. See also #3456.
 
-Please use keywords (e.g., Fixes) to create link to the issues or pull requests
-you resolved, so that they will automatically be closed when your pull request
-is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests
+Please use keywords (e.g., Fixes) to create link to the issues or pull requests you resolved, so that they will automatically be closed when your pull request is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests
 -->
 
-
 #### What does this implement/fix? Explain your changes.
+
 <!--
 A clear and concise description of what you have implemented.
 -->
@@ -21,24 +21,29 @@ A clear and concise description of what you have implemented.
 #### Does your contribution introduce a new dependency? If yes, which one?
 
 <!--
-If your contribution does add a new hard dependency, we may suggest to initially develop your contribution in a separate companion package in https://github.com/aeon/ to keep external dependencies of the core aeon package to a minimum.
+If your contribution does add a dependency, we may suggest adding it as an optional/soft dependency to keep external dependencies of the core aeon package to a minimum.
 -->
 
 #### What should a reviewer concentrate their feedback on?
 
-<!-- This section is particularly useful if you have a pull request that is still in development. You can guide the reviews to focus on the parts that are ready for their comments. We suggest using bullets (indicated by * or -) and filled checkboxes [x] here -->
+<!--
+This section is particularly useful if you have a pull request that is still in development. You can guide the reviews to focus on the parts that are ready for their comments. We suggest using bullets (indicated by * or -) and filled checkboxes [x] here
+-->
 
 #### Did you add any tests for the change?
 
-<!-- This section is useful if you have added a test in addition to the existing ones. This will ensure that further changes to these files won't introduce the same kind of bug. It is considered good practice to add tests with newly added code to enforce the fact that the code actually works. This will reduce the chance of introducing logical bugs.
+<!--
+This section is useful if you have added a test in addition to the existing ones. This will ensure that further changes to these files won't introduce the same kind of bug. It is considered good practice to add tests with newly added code to enforce the fact that the code actually works. This will reduce the chance of introducing logical bugs.
 -->
 
 #### Any other comments?
+
 <!--
 Please be aware that we are a loose team of volunteers so patience is necessary; assistance handling other issues is very welcome. We value all user contributions, no matter how minor they are. If we are slow to review, either the pull request needs some benchmarking, tinkering, convincing, etc. or more likely the reviewers are simply busy. In either case, we ask for your understanding during the review process.
 -->
 
-#### PR checklist
+### PR checklist
+
 <!--
 Please go through the checklist below. Please feel free to remove points if they are not applicable.
 -->
@@ -47,10 +52,7 @@ Please go through the checklist below. Please feel free to remove points if they
 - [ ] I've added myself to the [list of contributors](https://github.com/aeon-toolkit/aeon/blob/main/.all-contributorsrc).
 - [ ] Optionally, I've updated aeon's [CODEOWNERS](https://github.com/aeon-toolkit/aeon/blob/main/CODEOWNERS) to receive notifications about future changes to these files.
 - [ ] The PR title starts with either [ENH], [MNT], [DOC], or [BUG] indicating whether the PR topic is related to enhancement, maintenance, documentation, or bug.
-
-##### For new estimators
-- [ ] I've added the estimator to the online documentation.
-- [ ] I've updated the existing example notebooks or provided a new one to showcase how my estimator works.
+- [ ] For new estimators, I've added the estimator to the online documentation.
 
 
 <!--
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
deleted file mode 100644
index 425dbbe7..00000000
--- a/.github/release-drafter.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name-template: 'v$NEXT_PATCH_VERSION'
-tag-template: 'v$NEXT_PATCH_VERSION'
-categories:
-  - title: '🚀 Features'
-    labels:
-      - 'feature'
-      - 'enhancement'
-  - title: '🐛 Bug Fixes'
-    labels:
-      - 'fix'
-      - 'bugfix'
-      - 'bug'
-  - title: '🧰 Maintenance'
-    label:
-      - 'chore'
-      - 'maintenance'
-      - 'refactor'
-      - 'documentation'
-
-template: |
-  ## What's New
-
-  $CHANGES
-
-  All contributors: $CONTRIBUTORS
diff --git a/.github/release.yml b/.github/release.yml
index c013f46e..12fc604f 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -1,23 +1,21 @@
 changelog:
-  exclude:
-    authors:
-      - octocat
-      - dependabot
   categories:
     - title: '🚀 Features'
       labels:
-        - 'feature'
         - 'enhancement'
+        - 'implementing algorithms'
+        - 'implementing framework'
+        - 'interfacing algorithms'
     - title: '🐛 Bug Fixes'
       labels:
-        - 'fix'
-        - 'bugfix'
         - 'bug'
     - title: '🧰 Maintenance'
       label:
-        - 'chore'
         - 'maintenance'
         - 'refactor'
+        - 'testing'
+    - title: '📖 Documentation'
+      label:
         - 'documentation'
     - title: Other Changes
       labels:
diff --git a/.github/workflows/wheels.yml b/.github/workflows/release.yml
similarity index 67%
rename from .github/workflows/wheels.yml
rename to .github/workflows/release.yml
index bb9dde63..dc33395e 100644
--- a/.github/workflows/wheels.yml
+++ b/.github/workflows/release.yml
@@ -1,13 +1,28 @@
-name: Build wheels and publish to PyPI
+name: Release
 
 on:
   release:
-    types: [published]
+    types:
+      - published
 
 jobs:
+  check_manifest:
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.10"
+
+      - uses: pre-commit/action@v3.0.0
+        with:
+          extra_args: check-manifest --hook-stage manual
+
   build_wheels:
-    name: Build wheels
-    runs-on: ubuntu-20.04
+    needs: check_manifest
+    runs-on: ubuntu-22.04
 
     steps:
       - uses: actions/checkout@v3
@@ -16,39 +31,41 @@ jobs:
         with:
           python-version: '3.10'
 
-      - name: Build wheel
+      - name: Build project
         run: |
           python -m pip install build
-          python -m build --wheel --sdist --outdir wheelhouse
+          python -m build
 
-      - name: Store wheels
-        uses: actions/upload-artifact@v2
+      - name: Store build files
+        uses: actions/upload-artifact@v3
         with:
-          name: wheels
-          path: wheelhouse/*
+          name: dist
+          path: dist/*
 
   test_unix_wheels:
     needs: build_wheels
     name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }}
     runs-on: ${{ matrix.os }}
+
     strategy:
       matrix:
-        os: [ubuntu-20.04, macOS-11]
+        os: [ubuntu-22.04, macOS-12]
         python-version: ['3.8', '3.9', '3.10']
 
     steps:
       - uses: actions/checkout@v3
+
       - uses: actions/setup-python@v4
         with:
           python-version: ${{ matrix.python-version }}
 
-      - uses: actions/download-artifact@v2
+      - uses: actions/download-artifact@v3
         with:
-          name: wheels
-          path: wheelhouse
+          name: dist
+          path: dist
 
       - name: Get wheel filename
-        run: echo "WHEELNAME=$(ls ./wheelhouse/aeon-*none-any.whl)" >> $GITHUB_ENV
+        run: echo "WHEELNAME=$(ls ./dist/aeon-*none-any.whl)" >> $GITHUB_ENV
 
       - name: Install wheel and extras
         run: python -m pip install "${{ env.WHEELNAME }}[all_extras,dev]"
@@ -59,31 +76,26 @@ jobs:
   test_windows_wheels:
     needs: build_wheels
     name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }}
-    runs-on: windows-2019
+    runs-on: windows-2022
     strategy:
       matrix:
         include:
           # Window 64 bit
-          - os: windows-2019
+          - os: windows-2022
             python: 38
             python-version: '3.8'
             bitness: 64
             platform_id: win_amd64
-          - os: windows-2019
+          - os: windows-2022
             python: 39
             python-version: '3.9'
             bitness: 64
             platform_id: win_amd64
-          - os: windows-2019
+          - os: windows-2022
             python: 310
             python-version: '3.10'
             bitness: 64
             platform_id: win_amd64
-          - os: windows-2019
-            python: 39
-            python-version: 3.9
-            bitness: 64
-            platform_id: win_amd64
 
     steps:
       - uses: actions/checkout@v3
@@ -97,19 +109,19 @@ jobs:
       - run: conda --version
       - run: which python
 
-      - uses: actions/download-artifact@v2
+      - uses: actions/download-artifact@v3
         with:
-          name: wheels
-          path: wheelhouse
+          name: dist
+          path: dist
 
       - name: Install conda libpython
         run: conda install -c anaconda -n test -y libpython
 
       - name: Display downloaded artifacts
-        run: ls -l wheelhouse
+        run: ls -l dist
 
       - name: Get wheel filename
-        run: echo "WHEELNAME=$(ls ./wheelhouse/aeon-*none-any.whl)" >> $env:GITHUB_ENV
+        run: echo "WHEELNAME=$(ls ./dist/aeon-*none-any.whl)" >> $env:GITHUB_ENV
 
       - name: Activate conda env
         run: conda activate test
@@ -128,19 +140,16 @@ jobs:
           python -m pytest
 
   upload_wheels:
-    name: Upload wheels to PyPI
     runs-on: ubuntu-latest
     needs: [build_wheels,test_unix_wheels,test_windows_wheels]
 
     steps:
-      - uses: actions/download-artifact@v2
+      - uses: actions/download-artifact@v3
         with:
-          name: wheels
-          path: wheelhouse
+          name: dist
+          path: dist
 
       - name: Publish package to PyPI
         uses: pypa/gh-action-pypi-publish@release/v1
         with:
-          user: ${{ secrets.PYPI_USERNAME }}
-          password: ${{ secrets.PYPI_PASSWORD }}
-          packages_dir: wheelhouse/
+          password: ${{ secrets.PYPI_TOKEN }}
diff --git a/Makefile b/Makefile
index 84aa582d..4a5a5b8f 100644
--- a/Makefile
+++ b/Makefile
@@ -17,12 +17,6 @@ help:
 		 awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m\
 		 %s\n", $$1, $$2}'
 
-release: ## Make a release
-	python3 $(BUILD_TOOLS)/make_release.py
-
-install: ## Install for the current user using the default python command
-	python3 setup.py build_ext --inplace && python setup.py install --user
-
 test: ## Run unit tests
 	-rm -rf ${TEST_DIR}
 	mkdir -p ${TEST_DIR}
@@ -30,6 +24,8 @@ test: ## Run unit tests
 	cp setup.cfg ${TEST_DIR}
 	python -m pytest
 
+tests: test
+
 test_softdeps: ## Run unit tests to check soft dependency handling in estimators
 	-rm -rf ${TEST_DIR}
 	mkdir -p ${TEST_DIR}
@@ -45,35 +41,3 @@ test_softdeps_full: ## Run all non-suite unit tests without soft dependencies
 	cp setup.cfg ${TEST_DIR}
 	cd ${TEST_DIR}
 	python -m pytest -v --showlocals --durations=20 -k 'not TestAll' $(PYTESTOPTIONS) --ignore=aeon/utils/tests/test_mlflow_aeon_model_export.py
-
-tests: test
-
-clean: ## Clean build dist and egg directories left after install
-	rm -rf ./dist
-	rm -rf ./build
-	rm -rf ./pytest_cache
-	rm -rf ./htmlcov
-	rm -rf ./junit
-	rm -rf ./$(PACKAGE).egg-info
-	rm -rf coverage.xml
-	rm -f MANIFEST
-	rm -rf ./wheelhouse/*
-	find . -type f -iname "*.so" -delete
-	find . -type f -iname '*.pyc' -delete
-	find . -type d -name '__pycache__' -empty -delete
-
-dist: ## Make Python source distribution
-	python3 setup.py sdist bdist_wheel
-
-build:
-	python -m build --sdist --wheel --outdir wheelhouse
-
-docs: doc
-
-doc: ## Build documentation with Sphinx
-	$(MAKE) -C $(DOC_DIR) html
-
-nb: clean
-	rm -rf .venv || true
-	python3 -m venv .venv
-	. .venv/bin/activate && python -m pip install .[all_extras,binder] && ./build_tools/run_examples.sh
diff --git a/aeon/classification/_delegate.py b/aeon/classification/_delegate.py
index 62cbd2c6..6926bdab 100644
--- a/aeon/classification/_delegate.py
+++ b/aeon/classification/_delegate.py
@@ -50,9 +50,7 @@ class _DelegatedClassifier(BaseClassifier):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_instances, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
             for specifications, see examples/AA_datatypes_and_datasets.ipynb
         y : 1D np.array of int, of shape [n_instances] - class labels for fitting
@@ -82,8 +80,6 @@ class _DelegatedClassifier(BaseClassifier):
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
                 3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
             for specifications, see examples/AA_datatypes_and_datasets.ipynb
 
@@ -110,9 +106,7 @@ class _DelegatedClassifier(BaseClassifier):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_instances, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
             for specifications, see examples/AA_datatypes_and_datasets.ipynb
 
diff --git a/aeon/classification/base.py b/aeon/classification/base.py
index 7d4a6e8d..d51da2b6 100644
--- a/aeon/classification/base.py
+++ b/aeon/classification/base.py
@@ -59,9 +59,7 @@ class BaseClassifier(BaseEstimator, ABC):
     """
 
     _tags = {
-        "X_inner_mtype": "numpy3D",  # Currently, it should be "numpy3D". Unequal
-        # length series through "nested_univ" is no longer supported, will be
-        # reintroduced with lists of numpy.
+        "X_inner_mtype": "numpy3D",
         "capability:multivariate": False,
         "capability:unequal_length": False,
         "capability:missing_values": False,
@@ -230,15 +228,13 @@ class BaseClassifier(BaseEstimator, ABC):
         Parameters
         ----------
         X : 3D np.array (any number of channels, equal length series)
-                of shape [n_instances, n_channels, series_length]
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of channels, equal or unequal length series)
+                of shape [n_cases, series_length]
 
         Returns
         -------
-        y : 2D array of shape [n_instances, n_classes] - predicted class probabilities
+        y : 2D array of shape [n_cases, n_classes] - predicted class probabilities
             First dimension indices correspond to instance indices in X,
             second dimension indices correspond to class labels, (i, j)-th entry is
             estimated probability that i-th instance is of class j
diff --git a/aeon/classification/deep_learning/inception_time.py b/aeon/classification/deep_learning/inception_time.py
index 78260948..3cd2acd3 100644
--- a/aeon/classification/deep_learning/inception_time.py
+++ b/aeon/classification/deep_learning/inception_time.py
@@ -309,7 +309,7 @@ class InceptionTimeClassifier(BaseClassifier):
             `create_test_instance` uses the first (or only) dictionary in `params`.
         """
         param1 = {
-            "n_classifiers": 2,
+            "n_classifiers": 1,
             "n_epochs": 10,
             "batch_size": 4,
             "kernel_size": 4,
@@ -317,14 +317,7 @@ class InceptionTimeClassifier(BaseClassifier):
             "use_bottleneck": True,
         }
 
-        param2 = {
-            "n_classifiers": 3,
-            "n_epochs": 12,
-            "batch_size": 6,
-            "use_bias": True,
-        }
-
-        return [param1, param2]
+        return [param1]
 
 
 class IndividualInceptionClassifier(BaseDeepClassifier):
diff --git a/aeon/classification/distance_based/_shape_dtw.py b/aeon/classification/distance_based/_shape_dtw.py
index 96a25e62..2c7b4a75 100644
--- a/aeon/classification/distance_based/_shape_dtw.py
+++ b/aeon/classification/distance_based/_shape_dtw.py
@@ -19,13 +19,16 @@ from aeon.datatypes import convert
 from aeon.datatypes._panel._convert import from_3d_numpy_to_nested
 from aeon.transformations.panel.dictionary_based._paa import PAA
 from aeon.transformations.panel.dwt import DWTTransformer
-from aeon.transformations.panel.hog1d import HOG1DTransformer
 
-# Transforms
+# Done
+from aeon.transformations.panel.hog1d import HOG1DTransformer
 from aeon.transformations.panel.segment import SlidingWindowSegmenter
 from aeon.transformations.panel.slope import SlopeTransformer
 from aeon.transformations.panel.summarize._extract import DerivativeSlopeTransformer
 
+# Transformers: To Do
+
+
 __author__ = ["vincent-nich12"]
 
 
diff --git a/aeon/classification/early_classification/base.py b/aeon/classification/early_classification/base.py
index f9054170..bfaa1bda 100644
--- a/aeon/classification/early_classification/base.py
+++ b/aeon/classification/early_classification/base.py
@@ -55,8 +55,7 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
     """
 
     _tags = {
-        "X_inner_mtype": "numpy3D",  # which type do _fit/_predict, support for X?
-        #    it should be either "numpy3D" or "nested_univ" (nested pd.DataFrame)
+        "X_inner_mtype": "numpy3D",
         "capability:multivariate": False,
         "capability:unequal_length": False,
         "capability:missing_values": False,
@@ -88,16 +87,13 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of dimensions, equal length series)
-                of shape [n_instances, n_dimensions, series_length]
+        X : 3D np.array (any number of channels, equal length series)
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of dimensions, equal or unequal length series)
-            or of any other supported Panel mtype
-                for list of mtypes, see datatypes.SCITYPE_REGISTER
-                for specifications, see examples/AA_datatypes_and_datasets.ipynb
-        y : 1D np.array of int, of shape [n_instances] - class labels for fitting
+                of shape [n_cases, series_length]
+            or of any other supported collection data type
+                for list of types, see datatypes.SCITYPE_REGISTER
+        y : 1D np.array of int, of shape [n_cases] - class labels for fitting
             indices correspond to instance indices in X
 
         Returns
@@ -124,19 +120,18 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of dimensions, equal length series)
-                of shape [n_instances, n_dimensions, series_length]
+        X : 3D np.array (any number of channels, equal length series)
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of dimensions, equal or unequal length series)
-            or of any other supported Panel mtype
-                for list of mtypes, see datatypes.SCITYPE_REGISTER
-                for specifications, see examples/AA_datatypes_and_datasets.ipynb
+                of shape [n_cases, series_length]
+            or of any other supported collection data type
+                for list of types, see datatypes.SCITYPE_REGISTER
+        y : 1D np.array of int, of shape [n_cases] - class labels for fitting
+            indices correspond to instance indices in X
 
         Returns
         -------
-        y : 1D np.array of int, of shape [n_instances] - predicted class labels
+        y : 1D np.array of int, of shape [n_cases] - predicted class labels
             indices correspond to instance indices in X
         decisions : 1D bool array
             An array of booleans, containing the decision of whether a prediction is
@@ -166,19 +161,16 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of dimensions, equal length series)
-                of shape [n_instances, n_dimensions, series_length]
+        X : 3D np.array (any number of channels, equal length series)
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of dimensions, equal or unequal length series)
-            or of any other supported Panel mtype
-                for list of mtypes, see datatypes.SCITYPE_REGISTER
-                for specifications, see examples/AA_datatypes_and_datasets.ipynb
+                of shape [n_cases, series_length]
+            or of any other supported collection data type
+                for list of types, see datatypes.SCITYPE_REGISTER
 
         Returns
         -------
-        y : 1D np.array of int, of shape [n_instances] - predicted class labels
+        y : 1D np.array of int, of shape [n_cases] - predicted class labels
             indices correspond to instance indices in X
         decisions : 1D bool array
             An array of booleans, containing the decision of whether a prediction is
@@ -207,19 +199,16 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of dimensions, equal length series)
-                of shape [n_instances, n_dimensions, series_length]
+        X : 3D np.array (any number of channels, equal length series)
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of dimensions, equal or unequal length series)
-            or of any other supported Panel mtype
-                for list of mtypes, see datatypes.SCITYPE_REGISTER
-                for specifications, see examples/AA_datatypes_and_datasets.ipynb
+                of shape [n_cases, series_length]
+            or of any other supported collection data type
+                for list of types, see datatypes.SCITYPE_REGISTER
 
         Returns
         -------
-        y : 2D array of shape [n_instances, n_classes] - predicted class probabilities
+        y : 2D array of shape [n_cases, n_classes] - predicted class probabilities
             1st dimension indices correspond to instance indices in X
             2nd dimension indices correspond to possible labels (integers)
             (i, j)-th entry is predictive probability that i-th instance is of class j
@@ -251,19 +240,16 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of dimensions, equal length series)
-                of shape [n_instances, n_dimensions, series_length]
+        X : 3D np.array (any number of channels, equal length series)
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of dimensions, equal or unequal length series)
-            or of any other supported Panel mtype
-                for list of mtypes, see datatypes.SCITYPE_REGISTER
-                for specifications, see examples/AA_datatypes_and_datasets.ipynb
+                of shape [n_cases, series_length]
+            or of any other supported collection data type
+                for list of types, see datatypes.SCITYPE_REGISTER
 
         Returns
         -------
-        y : 2D array of shape [n_instances, n_classes] - predicted class probabilities
+        y : 2D array of shape [n_cases, n_classes] - predicted class probabilities
             1st dimension indices correspond to instance indices in X
             2nd dimension indices correspond to possible labels (integers)
             (i, j)-th entry is predictive probability that i-th instance is of class j
@@ -287,16 +273,13 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
 
         Parameters
         ----------
-        X : 3D np.array (any number of dimensions, equal length series)
-                of shape [n_instances, n_dimensions, series_length]
+        X : 3D np.array (any number of channels, equal length series)
+                of shape [n_cases, n_channels, series_length]
             or 2D np.array (univariate, equal length series)
-                of shape [n_instances, series_length]
-            or pd.DataFrame with each column a dimension, each cell a pd.Series
-                (any number of dimensions, equal or unequal length series)
-            or of any other supported Panel mtype
-                for list of mtypes, see datatypes.SCITYPE_REGISTER
-                for specifications, see examples/AA_datatypes_and_datasets.ipynb
-        y : 1D np.ndarray of int, of shape [n_instances] - class labels (ground truth)
+                of shape [n_cases, series_length]
+            or of any other supported collection data type
+                for list of types, see datatypes.SCITYPE_REGISTER
+        y : 1D np.ndarray of int, of shape [n_cases] - class labels (ground truth)
             indices correspond to instance indices in X
 
         Returns
@@ -363,12 +346,9 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_cases, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
-            for specifications, see examples/AA_datatypes_and_datasets.ipynb
-        y : 1D np.array of int, of shape [n_instances] - class labels for fitting
+        y : 1D np.array of int, of shape [n_cases] - class labels for fitting
             indices correspond to instance indices in X
 
         Returns
@@ -398,15 +378,12 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_cases, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
-            for specifications, see examples/AA_datatypes_and_datasets.ipynb
 
         Returns
         -------
-        y : 1D np.array of int, of shape [n_instances] - predicted class labels
+        y : 1D np.array of int, of shape [n_cases] - predicted class labels
             indices correspond to instance indices in X
         decisions : 1D bool array
             An array of booleans, containing the decision of whether a prediction is
@@ -431,15 +408,12 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_cases, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
-            for specifications, see examples/AA_datatypes_and_datasets.ipynb
 
         Returns
         -------
-        y : 1D np.array of int, of shape [n_instances] - predicted class labels
+        y : 1D np.array of int, of shape [n_cases] - predicted class labels
             indices correspond to instance indices in X
         decisions : 1D bool array
             An array of booleans, containing the decision of whether a prediction is
@@ -464,15 +438,12 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_cases, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
-            for specifications, see examples/AA_datatypes_and_datasets.ipynb
 
         Returns
         -------
-        y : 2D array of shape [n_instances, n_classes] - predicted class probabilities
+        y : 2D array of shape [n_cases, n_classes] - predicted class probabilities
             1st dimension indices correspond to instance indices in X
             2nd dimension indices correspond to possible labels (integers)
             (i, j)-th entry is predictive probability that i-th instance is of class j
@@ -508,15 +479,12 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_cases, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
-            for specifications, see examples/AA_datatypes_and_datasets.ipynb
 
         Returns
         -------
-        y : 2D array of shape [n_instances, n_classes] - predicted class probabilities
+        y : 2D array of shape [n_cases, n_classes] - predicted class probabilities
             1st dimension indices correspond to instance indices in X
             2nd dimension indices correspond to possible labels (integers)
             (i, j)-th entry is predictive probability that i-th instance is of class j
@@ -545,12 +513,9 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             if self.get_tag("X_inner_mtype") = "numpy3D":
-                3D np.ndarray of shape = [n_instances, n_dimensions, series_length]
-            if self.get_tag("X_inner_mtype") = "nested_univ":
-                pd.DataFrame with each column a dimension, each cell a pd.Series
+                3D np.ndarray of shape = [n_cases, n_channels, series_length]
             for list of other mtypes, see datatypes.SCITYPE_REGISTER
-            for specifications, see examples/AA_datatypes_and_datasets.ipynb
-        y : 1D np.array of int, of shape [n_instances] - class labels for fitting
+        y : 1D np.array of int, of shape [n_cases] - class labels for fitting
             indices correspond to instance indices in X
 
         Returns
@@ -612,7 +577,7 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         _convert_X = BaseClassifier._convert_X
         return _convert_X(self, X)
 
-    def _check_classifier_input(self, X, y=None, enforce_min_instances=1):
+    def _check_classifier_input(self, X, y=None, enforce_min_cases=1):
         """Check whether input X and y are valid formats with minimum data.
 
         Raises a ValueError if the input is not valid.
@@ -621,7 +586,7 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
         ----------
         X : check whether conformant with any aeon Panel mtype specification
         y : check whether a pd.Series or np.array
-        enforce_min_instances : int, optional (default=1)
+        enforce_min_cases : int, optional (default=1)
             check there are a minimum number of instances.
 
         Returns
@@ -634,7 +599,7 @@ class BaseEarlyClassifier(BaseEstimator, ABC):
             If y or X is invalid input data type, or there is not enough data
         """
         _check_classifier_input = BaseClassifier._check_classifier_input
-        return _check_classifier_input(self, X, y, enforce_min_instances)
+        return _check_classifier_input(self, X, y, enforce_min_cases)
 
     def _internal_convert(self, X, y=None):
         """Convert X and y if necessary as a user convenience.
diff --git a/aeon/forecasting/varmax.py b/aeon/forecasting/varmax.py
index 12c42754..ae80d709 100644
--- a/aeon/forecasting/varmax.py
+++ b/aeon/forecasting/varmax.py
@@ -8,6 +8,9 @@ import warnings
 import pandas as pd
 
 from aeon.forecasting.base.adapters import _StatsModelsAdapter
+from aeon.utils.validation._dependencies import _check_soft_dependencies
+
+_check_soft_dependencies("pandas<2.0.0", severity="warning")
 
 
 class VARMAX(_StatsModelsAdapter):
@@ -216,6 +219,7 @@ class VARMAX(_StatsModelsAdapter):
         "X-y-must-have-same-index": True,
         "enforce_index_type": None,
         "capability:pred_int": False,
+        "python_dependencies": "pandas<2.0.0",  # needs to be fixed with pandas==2.0.0
     }
 
     def __init__(
diff --git a/aeon/transformations/panel/dictionary_based/_paa.py b/aeon/transformations/panel/dictionary_based/_paa.py
index bb9164c7..cef62b4b 100644
--- a/aeon/transformations/panel/dictionary_based/_paa.py
+++ b/aeon/transformations/panel/dictionary_based/_paa.py
@@ -1,8 +1,7 @@
 # -*- coding: utf-8 -*-
 """Piecewise Aggregate Approximation Transformer (PAA)."""
-import pandas as pd
+import numpy as np
 
-from aeon.datatypes._panel._convert import from_nested_to_2d_array
 from aeon.transformations.base import BaseTransformer
 
 __author__ = "MatthewMiddlehurst"
@@ -11,93 +10,89 @@ __author__ = "MatthewMiddlehurst"
 class PAA(BaseTransformer):
     """Piecewise Aggregate Approximation Transformer (PAA).
 
-    (PAA) Piecewise Aggregate Approximation Transformer, as described in
-    Eamonn Keogh, Kaushik Chakrabarti, Michael Pazzani, and Sharad Mehrotra.
-    Dimensionality reduction for fast similarity search in large time series
-    databases.
-    Knowledge and information Systems, 3(3), 263-286, 2001.
-    For each series reduce the dimensionality to num_intervals, where each
-    value is the mean of values in
-    the interval.
-
-    TO DO: pythonise it to make it more efficient. Maybe check vs this version
-            http://vigne.sh/posts/piecewise-aggregate-approx/
-    Could have: Tune the interval size in fit somehow?
+    (PAA) Piecewise Aggregate Approximation Transformer, as described in [1]. For
+    each series reduce the dimensionality to num_intervals, where each value is the
+    mean of values in the interval.
 
     Parameters
     ----------
-    num_intervals   : int, dimension of the transformed data (default 8)
+    n_intervals   : int, dimension of the transformed data (default 8)
+
+    Notes
+    -----
+    [1]  Eamonn Keogh, Kaushik Chakrabarti, Michael Pazzani, and Sharad Mehrotra.
+    Dimensionality reduction for fast similarity search in large time series
+    databases. Knowledge and information Systems, 3(3), 263-286, 2001.
+
+    Examples
+    --------
+    >>> from aeon.transformations.panel.dictionary_based import PAA
+    >>> import numpy as np
+    >>> data = np.array([[[1,2,3,4,5,6,7,8,9,10]],[[5,5,5,5,5,5,5,5,5,5]]])
+    >>> paa = PAA(n_intervals=2)
+    >>> data2 = paa.fit_transform(data)
     """
 
     _tags = {
-        "scitype:transform-input": "Series",
-        # what is the scitype of X: Series, or Panel
         "scitype:transform-output": "Series",
-        # what scitype is returned: Primitives, Series, Panel
-        "scitype:instancewise": True,  # is this an instance-wise transform?
-        "X_inner_mtype": "nested_univ",  # which mtypes do _fit/_predict support for X?
-        "y_inner_mtype": "None",  # which mtypes do _fit/_predict support for X?
+        "scitype:instancewise": True,
+        "X_inner_mtype": "numpy3D",
+        "y_inner_mtype": "None",
     }
 
-    def __init__(self, num_intervals=8):
-        self.num_intervals = num_intervals
+    def __init__(self, n_intervals=8):
+        self.n_intervals = n_intervals
         super(PAA, self).__init__()
 
     def set_num_intervals(self, n):
         """Set self.num_intervals to n."""
-        self.num_intervals = n
+        self.n_intervals = n
 
-    # todo: looks like this just loops over series instances
-    # so should be refactored to work on Series directly
     def _transform(self, X, y=None):
         """Transform data.
 
         Parameters
         ----------
-        X : nested pandas DataFrame of shape [n_instances, n_dims]
-            Nested dataframe with multivariate time-series in cells.
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
+            collection of time series to transform
+        y : ignored argument for interface compatibility
 
         Returns
         -------
-        dims: Pandas data frame with first dimension in column zero,
-              second in column one etc.
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
+            collection of transformed time series
         """
         # Get information about the dataframe
-        num_atts = len(X.iloc[0, 0])
-        col_names = X.columns
+        # PAA acts on each dimension, so its best to reshape prior to transform
+        n_cases, n_channels, series_length = X.shape
+        _X = np.swapaxes(X, 0, 1)
 
         # Check the parameters are appropriate
-        self._check_parameters(num_atts)
+        self._check_parameters(series_length)
 
         # On each dimension, perform PAA
-        dataFrames = []
-        for x in col_names:
-            dataFrames.append(self._perform_paa_along_dim(pd.DataFrame(X[x])))
-
-        # Combine the dimensions together
-        result = pd.concat(dataFrames, axis=1, sort=False)
-        result.columns = col_names
+        channels = []
+        for i in range(n_channels):
+            channels.append(self._perform_paa_along_dim(_X[i]))
+        result = np.array(channels)
+        result = np.swapaxes(result, 0, 1)
 
         return result
 
     def _perform_paa_along_dim(self, X):
-        X = from_nested_to_2d_array(X, return_numpy=True)
-
-        num_atts = X.shape[1]
-        num_insts = X.shape[0]
-        dims = pd.DataFrame()
+        n_cases, series_length = X.shape
         data = []
 
-        for i in range(num_insts):
+        for i in range(n_cases):
             series = X[i, :]
 
             frames = []
             current_frame = 0
             current_frame_size = 0
-            frame_length = num_atts / self.num_intervals
+            frame_length = series_length / self.n_intervals
             frame_sum = 0
 
-            for n in range(num_atts):
+            for n in range(series_length):
                 remaining = frame_length - current_frame_size
 
                 if remaining > 1:
@@ -115,16 +110,14 @@ class PAA(BaseTransformer):
                     current_frame_size = 1 - remaining
 
             # if the last frame was lost due to double imprecision
-            if current_frame == self.num_intervals - 1:
+            if current_frame == self.n_intervals - 1:
                 frames.append(frame_sum / frame_length)
 
-            data.append(pd.Series(frames))
-
-        dims[0] = data
+            data.append(frames)
 
-        return dims
+        return data
 
-    def _check_parameters(self, num_atts):
+    def _check_parameters(self, series_length):
         """Check parameters of PAA.
 
         Function for checking the values of parameters inserted into PAA.
@@ -135,20 +128,15 @@ class PAA(BaseTransformer):
         ------
         ValueError or TypeError if a parameters input is invalid.
         """
-        if isinstance(self.num_intervals, int):
-            if self.num_intervals <= 0:
-                raise ValueError(
-                    "num_intervals must have the \
-                                  value of at least 1"
-                )
-            if self.num_intervals > num_atts:
+        if isinstance(self.n_intervals, int):
+            if self.n_intervals <= 0:
+                raise ValueError("num_intervals must have the value of at least 1")
+            if self.n_intervals > series_length:
                 raise ValueError(
-                    "num_intervals cannot be higher \
-                                  than the time series length."
+                    "num_intervals cannot be higher than the time series length."
                 )
         else:
             raise TypeError(
-                "num_intervals must be an 'int'. Found '"
-                + type(self.num_intervals).__name__
-                + "' instead."
+                f"num_intervals must be an 'int'. Found"
+                f" {type(self.n_intervals).__name__} instead."
             )
diff --git a/aeon/transformations/panel/dictionary_based/_sax.py b/aeon/transformations/panel/dictionary_based/_sax.py
index 27a006e6..fcc3f1b7 100644
--- a/aeon/transformations/panel/dictionary_based/_sax.py
+++ b/aeon/transformations/panel/dictionary_based/_sax.py
@@ -135,7 +135,7 @@ class SAX(BaseTransformer):
 
             split = scipy.stats.zscore(split, axis=1)
 
-            paa = PAA(num_intervals=self.word_length)
+            paa = PAA(n_intervals=self.word_length)
             data = pd.DataFrame()
             data[0] = [pd.Series(x, dtype=np.float32) for x in split]
             patterns = paa.fit_transform(data)
diff --git a/aeon/transformations/panel/slope.py b/aeon/transformations/panel/slope.py
index 6a3789c0..fd568386 100644
--- a/aeon/transformations/panel/slope.py
+++ b/aeon/transformations/panel/slope.py
@@ -23,21 +23,27 @@ class SlopeTransformer(BaseTransformer):
     ----------
     n_intervals : int, number of approx equal segments
                     to split the time series into.
+
+    Examples
+    --------
+    >>> import numpy as np
+    >>> from aeon.transformations.panel.slope import SlopeTransformer
+    >>> X = np.array([[[4, 6, 10, 12, 8, 6, 5, 5]]])
+    >>> s = SlopeTransformer(n_intervals=2)
+    >>> res = s.fit_transform(X)
     """
 
     _tags = {
         "scitype:transform-output": "Series",
-        "scitype:instancewise": False,  # is this an instance-wise transform?
-        "X_inner_mtype": "numpy3D",  # which mtypes do _fit/_predict support for X?
-        "y_inner_mtype": "None",  # which mtypes do _fit/_predict support for X?
+        "scitype:instancewise": False,
+        "X_inner_mtype": "numpy3D",
+        "y_inner_mtype": "None",
         "fit_is_empty": True,
-        "capability:unequal_length:removes": True,
-        # is transform result always guaranteed to be equal length (and series)?
     }
 
     def __init__(self, n_intervals=8):
         self.n_intervals = n_intervals
-        super(SlopeTransformer, self).__init__()
+        super(SlopeTransformer, self).__init__(_output_convert=False)
 
     def _transform(self, X, y=None):
         """Transform X and return a transformed version.
@@ -46,24 +52,28 @@ class SlopeTransformer(BaseTransformer):
 
         Parameters
         ----------
-        X : numpy array of shape [n_instances, n_channels, series_length]
+        X : 3D np.ndarray of shape = [n_instances, n_channels, series_length]
+        collection of time series to transform
         y : ignored argument for interface compatibility
-            Additional data, e.g., labels for transformation
 
         Returns
         -------
-        Xt : numpy array of shape [n_instances, n_channels, n_intervals]
-            transformed version of X, each value is the slope over an interval
+        3D np.ndarray of shape = [n_instances, n_channels, series_length] collection
+        of time series to transform
         """
         # Get information about the dataframe
-        n_instances, n_channels, n_timepoints = X.shape
-        self._check_parameters(n_timepoints)
-
-        x_trans = np.zeros(shape=(n_instances, n_channels, self.n_intervals))
-        for j in range(n_channels):
-            for i in range(n_instances):
-                x_trans[i][j] = self._get_gradients_of_lines(X[i][j])
-        return x_trans
+        n_cases, n_channels, series_length = X.shape
+        self._check_parameters(series_length)
+        full_data = []
+        for i in range(n_cases):
+            case_data = []
+            for j in range(n_channels):
+                # Calculate gradients
+                res = self._get_gradients_of_lines(X[i][j])
+                case_data.append(res)
+            full_data.append(np.asarray(case_data))
+
+        return np.array(full_data)
 
     def _get_gradients_of_lines(self, X):
         """Get gradients of lines.
diff --git a/build_tools/make_release.py b/build_tools/make_release.py
deleted file mode 100644
index ec957d76..00000000
--- a/build_tools/make_release.py
+++ /dev/null
@@ -1,280 +0,0 @@
-#!/usr/bin/env python3 -u
-# -*- coding: utf-8 -*-
-# copyright: aeon developers, BSD-3-Clause License (see LICENSE file)
-"""
-Do-nothing script for making a release
-
-This idea comes from here:
-- https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-the-key-to
--gradual-automation/
-
-The script is adapted from:
-- https://github.com/alan-turing-institute/CleverCSV/blob/master
-/make_release.py
-"""
-
-__author__ = ["Markus Löning"]
-
-import codecs
-import os
-import re
-import webbrowser
-
-import colorama
-
-ROOT_DIR = os.path.abspath(os.path.dirname(__file__)).replace("build_tools", "")
-PACKAGE_NAME = "aeon"
-
-
-class URLs:
-    DOCS_LOCAL = "file://" + os.path.realpath(
-        os.path.join(ROOT_DIR, "docs/_build/html/index.html")
-    )
-    DOCS_ONLINE = "https://www.aeon-toolkit.org"
-    PYPI = f"https://pypi.org/simple/{PACKAGE_NAME}/"
-
-
-def read(*parts):
-    # intentionally *not* adding an encoding option to open, See:
-    #   https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690
-    with codecs.open(os.path.join(ROOT_DIR, *parts), "r") as fp:
-        return fp.read()
-
-
-def find_version(*file_paths):
-    version_file = read(*file_paths)
-    version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
-    if version_match:
-        return version_match.group(1)
-    else:
-        raise RuntimeError("Unable to find version string.")
-
-
-def open_website(website):
-    webbrowser.open(website, new=1)
-
-
-def colored(msg, color=None, style=None):
-    colors = {
-        "red": colorama.Fore.RED,
-        "green": colorama.Fore.GREEN,
-        "cyan": colorama.Fore.CYAN,
-        "yellow": colorama.Fore.YELLOW,
-        "magenta": colorama.Fore.MAGENTA,
-        None: "",
-    }
-    styles = {
-        "bright": colorama.Style.BRIGHT,
-        "dim": colorama.Style.DIM,
-        None: "",
-    }
-    pre = colors[color] + styles[style]
-    post = colorama.Style.RESET_ALL
-    return f"{pre}{msg}{post}"
-
-
-def cprint(msg, color=None, style=None):
-    """Coloured printing"""
-    print(colored(msg, color=color, style=style))  # noqa
-
-
-def wait_for_enter():
-    input(colored("\nPress Enter to continue", style="dim"))
-    print()  # noqa
-
-
-class Step:
-    def pre(self, context):
-        pass
-
-    def post(self, context):
-        wait_for_enter()
-
-    def run(self, context):
-        try:
-            self.pre(context)
-            self.action(context)
-            self.post(context)
-        except KeyboardInterrupt:
-            cprint("\nInterrupted.", color="red")
-            raise SystemExit(1)
-
-    @staticmethod
-    def instruct(msg):
-        cprint(msg, color="green")
-
-    def print_run(self, msg):
-        cprint("Run:", color="cyan", style="bright")
-        self.print_cmd(msg)
-
-    @staticmethod
-    def print_cmd(msg):
-        cprint("\t" + msg, color="cyan", style="bright")
-
-    @staticmethod
-    def do_cmd(cmd):
-        cprint(f"Going to run: {cmd}", color="magenta", style="bright")
-        wait_for_enter()
-        os.system(cmd)
-
-    def action(self, context):
-        raise NotImplementedError("abstract method")
-
-
-class ConfirmGitStatus(Step):
-    def __init__(self, branch):
-        self.branch = branch
-
-    def action(self, context):
-        self.instruct(
-            f"Make sure you're on: {self.branch}, you're local "
-            f"branch is up-to-date, and all new changes are merged "
-            f"in."
-        )
-        self.do_cmd(f"git checkout {self.branch}")
-        self.do_cmd("git pull")
-
-
-class UpdateChangelog(Step):
-    def action(self, context):
-        self.instruct(f"Update CHANGELOG for version: {context['version']}")
-
-
-class UpdateReadme(Step):
-    def action(self, context):
-        self.instruct(f"Update README for version: {context['version']}")
-
-
-class UpdateVersion(Step):
-    def action(self, context):
-        self.instruct("Update __init__.py with new version")
-
-    def post(self, context):
-        wait_for_enter()
-        context["version"] = find_version(context["package_name"], "__init__.py")
-
-
-class MakeClean(Step):
-    def action(self, context):
-        self.do_cmd("make clean")
-
-
-class MakeDocs(Step):
-    def action(self, context):
-        self.do_cmd("make docs")
-
-
-class MakeDist(Step):
-    def action(self, context):
-        self.do_cmd("make dist")
-
-
-class UploadToTestPyPI(Step):
-    def action(self, context):
-        self.instruct("Upload to TestPyPI")
-        cmd = "twine upload --repository-url https://test.pypi.org/legacy/ dist/*"
-        self.do_cmd(cmd)
-
-
-class InstallFromTestPyPI(Step):
-    def action(self, context):
-        self.instruct("Check installation from TestPyPI")
-        self.do_cmd(
-            f"sh build_tools/check_install_from_test_pypi.sh {context['version']}"
-        )
-
-
-class CheckVersionNumber(Step):
-    def action(self, context):
-        self.instruct(
-            f"Ensure that the following command gives version: {context['version']}"
-        )
-        self.do_cmd(
-            f"python -c 'import {context['package_name']}; print("
-            f"{context['package_name']}.__version__)'"
-        )
-
-
-class GitTagRelease(Step):
-    def action(self, context):
-        self.instruct("Tag version as a release")
-        self.do_cmd(f"git tag v{context['version']}")
-
-
-class GitTagPreRelease(Step):
-    def action(self, context):
-        self.instruct("Tag version as a pre-release (increment as needed)")
-        self.print_run(f"git tag v{context['version']}-rc.1")
-
-
-class PushToGitHub(Step):
-    def action(self, context):
-        self.instruct("Add and commit to git, then push to GitHub")
-
-
-class CheckCIStatus(Step):
-    def action(self, context):
-        self.instruct("Wait for CI to complete and check status")
-
-
-class CheckOnlineDocs(Step):
-    def action(self, context):
-        self.instruct("Check online docs")
-        open_website(URLs.DOCS_ONLINE)
-
-
-class CheckLocalDocs(Step):
-    def action(self, context):
-        self.instruct("Check local docs")
-        open_website(URLs.DOCS_LOCAL)
-
-
-class CheckPyPIFiles(Step):
-    def action(self, context):
-        self.instruct("Check PyPI files")
-        open_website(URLs.PYPI)
-
-
-class PushTagToGitHub(Step):
-    def action(self, context):
-        self.do_cmd(f"git push origin v{context['version']}")
-
-
-def main():
-    colorama.init()
-    steps = [
-        # prepare and run final checks
-        ConfirmGitStatus(branch="main"),
-        MakeClean(),
-        UpdateVersion(),
-        CheckVersionNumber(),
-        UpdateReadme(),
-        UpdateChangelog(),
-        MakeDocs(),
-        CheckLocalDocs(),
-        MakeDist(),
-        UploadToTestPyPI(),
-        InstallFromTestPyPI(),
-        PushToGitHub(),
-        CheckCIStatus(),
-        # check pre-release online
-        # GitTagPreRelease(),
-        # PushTagToGitHub(),
-        # make release
-        GitTagRelease(),
-        PushTagToGitHub(),
-        CheckCIStatus(),
-        CheckOnlineDocs(),
-        CheckPyPIFiles(),
-    ]
-    context = dict()
-    context["package_name"] = PACKAGE_NAME
-    context["version"] = find_version(context["package_name"], "__init__.py")
-    context["testdir"] = "temp/"
-    for step in steps:
-        step.run(context)
-    cprint("\nDone!", color="yellow", style="bright")
-
-
-if __name__ == "__main__":
-    main()
diff --git a/docs/developer_guide/release.rst b/docs/developer_guide/release.rst
index 8daf5c1f..dfdcdc29 100644
--- a/docs/developer_guide/release.rst
+++ b/docs/developer_guide/release.rst
@@ -131,21 +131,3 @@ Generally, release notes should follow the general pattern of previous release n
 * maintenance
 * bugfixes
 * all contributor credits
-
-
-Legacy build tools
-------------------
-
-We no longer use the legacy build tools using the make file.
-
-To run the legacy release workflow, e.g., for development purposes, run
-
-.. code:: bash
-
-   make release
-
-This calls
-`build_tools/make_release.py <https://github.com/aeon-toolkit/aeon/blob/main/build_tools/make_release.py>`__
-that will guide you through the release process.
-
-IMPORTANT: when running the legacy build tools, ensure to not accidentally push a release tag to the ``aeon`` repo.
diff --git a/examples/01_forecasting.ipynb b/examples/01_forecasting.ipynb
index ae7dcf04..3676ae5d 100644
--- a/examples/01_forecasting.ipynb
+++ b/examples/01_forecasting.ipynb
@@ -5132,58 +5132,6 @@
     "mean_absolute_percentage_error(y_test, y_pred, symmetric=False)"
    ]
   },
-  {
-   "cell_type": "markdown",
-   "metadata": {
-    "tags": []
-   },
-   "source": [
-    "### 2.3.6 AutoARIMA from [StatsForecast](https://github.com/Nixtla/statsforecast)<a class=\"anchor\" id=\"section_2_3_6\"></a>\n",
-    "\n",
-    "`aeon` interfaces `StatsForecast` for its `AutoARIMA` class models. `AutoARIMA` is an automatically tuned `ARIMA` variant that obtains the optimal pdq parameters automatically:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 243,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "0.04093539044441262"
-      ]
-     },
-     "execution_count": 243,
-     "metadata": {},
-     "output_type": "execute_result"
-    },
-    {
-     "data": {
-      "image/png": "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",
-      "text/plain": [
-       "<Figure size 1152x288 with 1 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "# StatsForecastAutoARIMA does not work with pandas 2.0.0 (yet). If so, pls make a PR.\n",
-    "if pd.__version__ < \"2.0.0\":\n",
-    "    from aeon.forecasting.statsforecast import StatsForecastAutoARIMA\n",
-    "\n",
-    "    forecaster = StatsForecastAutoARIMA(sp=12)\n",
-    "\n",
-    "    forecaster.fit(y_train)\n",
-    "    y_pred = forecaster.predict(fh)\n",
-    "    plot_series(y_train, y_test, y_pred, labels=[\"y_train\", \"y_test\", \"y_pred\"])\n",
-    "    mean_absolute_percentage_error(y_pred, y_test)"
-   ]
-  },
   {
    "cell_type": "markdown",
    "metadata": {},
diff --git a/pyproject.toml b/pyproject.toml
index e050a6f3..59f862de 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -38,13 +38,13 @@ classifiers = [
 ]
 requires-python = ">=3.8,<3.11"
 dependencies = [
+    "attrs>=19.2.0", # see #282
     "deprecated>=1.2.13",
     "numba>=0.55",
     "numpy>=1.21.0,<1.25",
-    "pandas<2.1.0",
+    "pandas>=1.5.3,<2.1.0",
     "scikit-learn>=1.0.0,<1.3.0",
     "scipy<2.0.0,>=1.2.0",
-    "attrs>=19.2.0", # see #282
     "packaging>=20.0",
 ]
 
@@ -66,7 +66,7 @@ all_extras = [
     "pyod>=0.8.0",
     "scikit_posthocs>=0.6.5",
     "seaborn>=0.11.0",
-    "statsforecast>=0.5.2",
+    # "statsforecast>=0.5.2",
     "statsmodels>=0.12.1",
     "stumpy>=1.5.1",
     "tbats>=1.1.0",


Test patch:
diff --git a/aeon/classification/deep_learning/tests/__init__.py b/aeon/classification/deep_learning/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/aeon/classification/deep_learning/tests/test_deep_base.py b/aeon/classification/deep_learning/tests/test_deep_base.py
new file mode 100644
index 00000000..24464eb9
--- /dev/null
+++ b/aeon/classification/deep_learning/tests/test_deep_base.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+"""Unit tests for classifier deep learning base class functionality."""
+
+import pickle
+
+import pytest
+
+from aeon.classification.deep_learning.base import BaseDeepClassifier
+from aeon.utils.validation._dependencies import _check_soft_dependencies
+
+__author__ = ["achieveordie"]
+
+
+class _DummyDeepClassifierEmpty(BaseDeepClassifier):
+    """Dummy Deep Classifier for testing empty base deep class save utilities."""
+
+    def __init__(self):
+        super(_DummyDeepClassifierEmpty, self).__init__()
+
+    def build_model(self, input_shape, n_classes, **kwargs):
+        return None
+
+    def _fit(self, X, y):
+        return self
+
+
+class _DummyDeepClassifierFull(BaseDeepClassifier):
+    """Dummy Deep Classifier to test serialization capabilities."""
+
+    def __init__(
+        self,
+        optimizer,
+    ):
+        super(_DummyDeepClassifierFull, self).__init__()
+        self.optimizer = optimizer
+
+    def build_model(self, input_shape, n_classes, **kwargs):
+        return None
+
+    def _fit(self, X, y):
+        return self
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies("tensorflow", severity="none"),
+    reason="skip test if required soft dependency not available",
+)
+def test_deep_estimator_empty():
+    """Check if serialization works for empty dummy."""
+    empty_dummy = _DummyDeepClassifierEmpty()
+    serialized_empty = pickle.dumps(empty_dummy)
+    deserialized_empty = pickle.loads(serialized_empty)
+    assert empty_dummy.__dict__ == deserialized_empty.__dict__
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies("tensorflow", severity="none"),
+    reason="skip test if required soft dependency not available",
+)
+@pytest.mark.parametrize("optimizer", [None, "adam", "object-adamax"])
+def test_deep_estimator_full(optimizer):
+    """Check if serialization works for full dummy."""
+    from tensorflow.keras.optimizers import Adamax, Optimizer, serialize
+
+    if optimizer == "object-adamax":
+        optimizer = Adamax()
+
+    full_dummy = _DummyDeepClassifierFull(optimizer)
+    serialized_full = pickle.dumps(full_dummy)
+    deserialized_full = pickle.loads(serialized_full)
+
+    if isinstance(optimizer, Optimizer):
+        # assert same configuration of optimizer
+        assert serialize(full_dummy.__dict__["optimizer"]) == serialize(
+            deserialized_full.__dict__["optimizer"]
+        )
+        assert serialize(full_dummy.optimizer) == serialize(deserialized_full.optimizer)
+
+        # assert weights of optimizers are same
+        assert (
+            full_dummy.optimizer.variables() == deserialized_full.optimizer.variables()
+        )
+
+        # remove optimizers from both to do full dict check,
+        # since two different objects
+        del full_dummy.__dict__["optimizer"]
+        del deserialized_full.__dict__["optimizer"]
+
+    # check if components are same
+    assert full_dummy.__dict__ == deserialized_full.__dict__
diff --git a/aeon/classification/tests/test_base.py b/aeon/classification/tests/test_base.py
index 5ad5d51e..29903756 100644
--- a/aeon/classification/tests/test_base.py
+++ b/aeon/classification/tests/test_base.py
@@ -3,22 +3,13 @@
 
 __author__ = ["mloning", "fkiraly", "TonyBagnall", "MatthewMiddlehurst", "achieveordie"]
 
-import pickle
-
 import numpy as np
 import pandas as pd
 import pytest
 
+from aeon.classification import DummyClassifier
 from aeon.classification.base import BaseClassifier
-from aeon.classification.deep_learning.base import BaseDeepClassifier
-from aeon.classification.distance_based import KNeighborsTimeSeriesClassifier
-from aeon.classification.feature_based import Catch22Classifier
-from aeon.utils._testing.panel import (
-    _make_classification_y,
-    _make_panel,
-    make_classification_problem,
-)
-from aeon.utils.validation._dependencies import _check_soft_dependencies
+from aeon.utils._testing.panel import _make_classification_y, _make_panel
 
 
 class _DummyClassifier(BaseClassifier):
@@ -44,36 +35,6 @@ class _DummyComposite(_DummyClassifier):
         self.foo = foo
 
 
-class _DummyDeepClassifierEmpty(BaseDeepClassifier):
-    """Dummy Deep Classifier for testing empty base deep class save utilities."""
-
-    def __init__(self):
-        super(_DummyDeepClassifierEmpty, self).__init__()
-
-    def build_model(self, input_shape, n_classes, **kwargs):
-        return None
-
-    def _fit(self, X, y):
-        return self
-
-
-class _DummyDeepClassifierFull(BaseDeepClassifier):
-    """Dummy Deep Classifier to test serialization capabilities."""
-
-    def __init__(
-        self,
-        optimizer,
-    ):
-        super(_DummyDeepClassifierFull, self).__init__()
-        self.optimizer = optimizer
-
-    def build_model(self, input_shape, n_classes, **kwargs):
-        return None
-
-    def _fit(self, X, y):
-        return self
-
-
 class _DummyHandlesAllInput(BaseClassifier):
     """Dummy classifier for testing base class fit/predict/predict_proba."""
 
@@ -96,26 +57,6 @@ class _DummyHandlesAllInput(BaseClassifier):
         return self
 
 
-class _DummyConvertPandas(BaseClassifier):
-    """Dummy classifier for testing base class fit/predict/predict_proba."""
-
-    _tags = {
-        "X_inner_mtype": "nested_univ",  # which type do _fit/_predict, support for X?
-    }
-
-    def _fit(self, X, y):
-        """Fit dummy."""
-        return self
-
-    def _predict(self, X):
-        """Predict dummy."""
-        return self
-
-    def _predict_proba(self, X):
-        """Predict proba dummy."""
-        return self
-
-
 multivariate_message = r"multivariate series"
 missing_message = r"missing values"
 unequal_message = r"unequal length series"
@@ -164,19 +105,11 @@ def test_base_classifier_fit():
         result = dummy.fit(test_X1, test_X3)
 
 
-TF = [True, False]
-
-
-@pytest.mark.parametrize("missing", TF)
-@pytest.mark.parametrize("multivariate", TF)
-@pytest.mark.parametrize("unequal", TF)
+@pytest.mark.parametrize("missing", [True, False])
+@pytest.mark.parametrize("multivariate", [True, False])
+@pytest.mark.parametrize("unequal", [True, False])
 def test_check_capabilities(missing, multivariate, unequal):
-    """Test the checking of capabilities.
-
-    There are eight different combinations to be tested with a classifier that can
-    handle it and that cannot. Obvs could loop, but I think its clearer to just
-    explicitly test;
-    """
+    """Test the checking of capabilities."""
     handles_none = _DummyClassifier()
     handles_none_composite = _DummyComposite(_DummyClassifier())
 
@@ -209,48 +142,6 @@ def test_check_capabilities(missing, multivariate, unequal):
     handles_all._check_capabilities(missing, multivariate, unequal)
 
 
-def test_convert_input():
-    """Test the conversions from dataframe to numpy.
-
-    1. Pass a 2D numpy X, get a 3D numpy X
-    2. Pass a 3D numpy X, get a 3D numpy X
-    3. Pass a pandas numpy X, equal length, get a 3D numpy X
-    4. Pass a pd.Series y, get a pd.Series back
-    5. Pass a np.ndarray y, get a pd.Series back
-    """
-
-    def _internal_convert(X, y=None):
-        return BaseClassifier._internal_convert(None, X, y)
-
-    cases = 5
-    length = 10
-    test_X1 = np.random.uniform(-1, 1, size=(cases, length))
-    test_X2 = np.random.uniform(-1, 1, size=(cases, 2, length))
-    tester = _DummyClassifier()
-    tempX = tester._convert_X(test_X2)
-    assert tempX.shape[0] == cases and tempX.shape[1] == 2 and tempX.shape[2] == length
-    instance_list = []
-    for _ in range(0, cases):
-        instance_list.append(pd.Series(np.random.randn(10)))
-    test_X3 = _create_example_dataframe(cases=cases, dimensions=1, length=length)
-    test_X4 = _create_example_dataframe(cases=cases, dimensions=3, length=length)
-    tempX = tester._convert_X(test_X3)
-    assert tempX.shape[0] == cases and tempX.shape[1] == 1 and tempX.shape[2] == length
-    tempX = tester._convert_X(test_X4)
-    assert tempX.shape[0] == cases and tempX.shape[1] == 3 and tempX.shape[2] == length
-    tester = _DummyConvertPandas()
-    tempX = tester._convert_X(test_X2)
-    assert isinstance(tempX, pd.DataFrame)
-    assert tempX.shape[0] == cases
-    assert tempX.shape[1] == 2
-    test_y1 = np.random.randint(0, 1, size=(cases))
-    test_y1 = pd.Series(test_y1)
-    tempX, tempY = _internal_convert(test_X1, test_y1)
-    assert isinstance(tempY, np.ndarray)
-    assert isinstance(tempX, np.ndarray)
-    assert tempX.ndim == 3
-
-
 def test__check_classifier_input():
     """Test for valid estimator format.
 
@@ -260,39 +151,36 @@ def test__check_classifier_input():
     4. Test incorrect: y as a list
     5. Test incorrect: too few cases or too short a series
     """
-
-    def _check_classifier_input(X, y=None, enforce_min_instances=1):
-        return BaseClassifier._check_classifier_input(None, X, y, enforce_min_instances)
-
+    cls = DummyClassifier()
     # 1. Test correct: X: np.array of 2 and 3 dimensions vs y:np.array and np.Series
     test_X1 = np.random.uniform(-1, 1, size=(5, 10))
     test_X2 = np.random.uniform(-1, 1, size=(5, 2, 10))
     test_y1 = np.random.randint(0, 1, size=5)
     test_y2 = pd.Series(np.random.randn(5))
-    _check_classifier_input(test_X2)
-    _check_classifier_input(test_X2, test_y1)
-    _check_classifier_input(test_X2, test_y2)
+    cls._check_classifier_input(test_X2)
+    cls._check_classifier_input(test_X2, test_y1)
+    cls._check_classifier_input(test_X2, test_y2)
     # 2. Test correct: X: pd.DataFrame with 1 (univariate) and 3 cols(multivariate) vs
     # y:np.array and np.Series
     test_X3 = _create_example_dataframe(5, 1, 10)
     test_X4 = _create_example_dataframe(5, 3, 10)
-    _check_classifier_input(test_X3, test_y1)
-    _check_classifier_input(test_X4, test_y1)
-    _check_classifier_input(test_X3, test_y2)
-    _check_classifier_input(test_X4, test_y2)
+    cls._check_classifier_input(test_X3, test_y1)
+    cls._check_classifier_input(test_X4, test_y1)
+    cls._check_classifier_input(test_X3, test_y2)
+    cls._check_classifier_input(test_X4, test_y2)
     # 3. Test incorrect: X with fewer cases than y
     test_X5 = np.random.uniform(-1, 1, size=(3, 4, 10))
     with pytest.raises(ValueError, match=r".*Mismatch in number of cases*."):
-        _check_classifier_input(test_X5, test_y1)
+        cls._check_classifier_input(test_X5, test_y1)
     # 4. Test incorrect data type: y is a List
     test_y3 = [1, 2, 3, 4, 5]
     with pytest.raises(
         TypeError, match=r".*X is not of a supported input data " r"type.*"
     ):
-        _check_classifier_input(test_X1, test_y3)
+        cls._check_classifier_input(test_X1, test_y3)
     # 5. Test incorrect: too few cases or too short a series
     with pytest.raises(ValueError, match=r".*Minimum number of cases required*."):
-        _check_classifier_input(test_X2, test_y1, enforce_min_instances=6)
+        cls._check_classifier_input(test_X2, test_y1, enforce_min_instances=6)
 
 
 def _create_example_dataframe(cases=5, dimensions=1, length=10):
@@ -318,93 +206,36 @@ def _create_unequal_length_nested_dataframe(cases=5, dimensions=1, length=10):
     return testy
 
 
-MTYPES = ["numpy3D", "pd-multiindex", "df-list", "numpyflat", "nested_univ"]
+MTYPES = ["numpy3D", "pd-multiindex", "df-list", "numpyflat"]
 
 
 @pytest.mark.parametrize("mtype", MTYPES)
 def test_input_conversion_fit_predict(mtype):
-    """Test that base class lets all Panel mtypes through."""
+    """Test that base class lets all valid input types through."""
     y = _make_classification_y()
     X = _make_panel(return_mtype=mtype)
 
-    clf = Catch22Classifier()
+    clf = DummyClassifier()
     clf.fit(X, y)
     clf.predict(X)
 
-    clf = _DummyConvertPandas()
+    clf = _DummyClassifier()
     clf.fit(X, y)
     clf.predict(X)
 
 
-@pytest.mark.parametrize("method", ["predict", "predict_proba"])
-def test_predict_single_class(method):
-    """Test return of predict/_proba in case only single class seen in fit."""
-    X, y = make_classification_problem()
-    y[:] = 42
-    n_instances = 10
-    X_test = X[:n_instances]
-
-    clf = KNeighborsTimeSeriesClassifier()
-
-    clf.fit(X, y)
-    y_pred = getattr(clf, method)(X_test)
-
-    if method == "predict":
-        assert isinstance(y_pred, np.ndarray)
-        assert y_pred.ndim == 1
-        assert y_pred.shape == (n_instances,)
-        assert all(list(y_pred == 42))
-    if method == "predict_proba":
-        assert isinstance(y_pred, np.ndarray)
-        assert y_pred.ndim == 2
-        assert y_pred.shape == (n_instances, 1)
-        assert all(list(y_pred == 1))
-
-
-@pytest.mark.skipif(
-    not _check_soft_dependencies("tensorflow", severity="none"),
-    reason="skip test if required soft dependency not available",
-)
-def test_deep_estimator_empty():
-    """Check if serialization works for empty dummy."""
-    empty_dummy = _DummyDeepClassifierEmpty()
-    serialized_empty = pickle.dumps(empty_dummy)
-    deserialized_empty = pickle.loads(serialized_empty)
-    assert empty_dummy.__dict__ == deserialized_empty.__dict__
-
-
-@pytest.mark.skipif(
-    not _check_soft_dependencies("tensorflow", severity="none"),
-    reason="skip test if required soft dependency not available",
-)
-@pytest.mark.parametrize("optimizer", [None, "adam", "object-adamax"])
-def test_deep_estimator_full(optimizer):
-    """Check if serialization works for full dummy."""
-    from tensorflow.keras.optimizers import Adamax, Optimizer, serialize
-
-    if optimizer == "object-adamax":
-        optimizer = Adamax()
-
-    full_dummy = _DummyDeepClassifierFull(optimizer)
-    serialized_full = pickle.dumps(full_dummy)
-    deserialized_full = pickle.loads(serialized_full)
-
-    if isinstance(optimizer, Optimizer):
-        # assert same configuration of optimizer
-        assert serialize(full_dummy.__dict__["optimizer"]) == serialize(
-            deserialized_full.__dict__["optimizer"]
-        )
-        assert serialize(full_dummy.optimizer) == serialize(deserialized_full.optimizer)
-
-        # assert weights of optimizers are same
-        assert (
-            full_dummy.optimizer.variables() == deserialized_full.optimizer.variables()
-        )
-
-        # remove optimizers from both to do full dict check,
-        # since two different objects
-        del full_dummy.__dict__["optimizer"]
-        del deserialized_full.__dict__["optimizer"]
-
-    # check if components are same
-    assert full_dummy.__dict__ == deserialized_full.__dict__
+def test_predict_single_class():
+    """Test return of predict predict_proba in case only single class seen in fit."""
+    trainX = np.ones(shape=(10, 20))
+    y = np.ones(10)
+    testX = np.ones(shape=(10, 20))
+    clf = DummyClassifier()
+    clf.fit(trainX, y)
+    y_pred = clf.predict(testX)
+    y_pred_proba = clf.predict_proba(testX)
+    assert y_pred.ndim == 1
+    assert y_pred.shape == (10,)
+    assert all(list(y_pred == 1))
+    assert y_pred_proba.ndim == 2
+    assert y_pred_proba.shape == (10, 1)
+    assert all(list(y_pred_proba == 1))
diff --git a/aeon/classification/tests/test_dummy.py b/aeon/classification/tests/test_dummy.py
index ab8fb121..fcae273b 100644
--- a/aeon/classification/tests/test_dummy.py
+++ b/aeon/classification/tests/test_dummy.py
@@ -3,16 +3,18 @@
 import numpy as np
 
 from aeon.classification import DummyClassifier
-from aeon.datasets import load_unit_test
 
 
 def test_dummy_classifier():
-    """Test function for DummyClassifier."""
-    X_train, y_train = load_unit_test(split="train")
-    X_test, _ = load_unit_test(split="test")
+    """Test DummyClassifier predicts majority class and prior distribution."""
+    X_train = np.ones(shape=(10, 10))
+    y_train = np.array([1, 1, 1, 1, 1, 1, 1, 1, 0, 0])
+    y_expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
+    X_test = np.ones(shape=(10, 10))
     dummy = DummyClassifier()
     dummy.fit(X_train, y_train)
     pred = dummy.predict(X_test)
-    assert all(i == "1" for i in pred)
+    np.testing.assert_array_equal(y_expected, pred)
     pred_proba = dummy.predict_proba(X_test)
-    assert all(np.array_equal([0.5, 0.5], i) for i in pred_proba)
+
+    assert all(np.array_equal([0.2, 0.8], i) for i in pred_proba)
diff --git a/aeon/forecasting/tests/test_varmax.py b/aeon/forecasting/tests/test_varmax.py
index 61c856f3..dd682092 100644
--- a/aeon/forecasting/tests/test_varmax.py
+++ b/aeon/forecasting/tests/test_varmax.py
@@ -22,7 +22,12 @@ df = pd.DataFrame(
 
 
 @pytest.mark.skipif(
-    not _check_soft_dependencies("statsmodels", severity="none"),
+    not all(
+        (
+            _check_soft_dependencies("statsmodels", severity="none"),
+            _check_soft_dependencies("pandas<2.0.0", severity="none"),
+        )
+    ),
     reason="skip test if required soft dependency not available",
 )
 def test_VARMAX_against_statsmodels():
@@ -50,7 +55,12 @@ def test_VARMAX_against_statsmodels():
 
 
 @pytest.mark.skipif(
-    not _check_soft_dependencies("statsmodels", severity="none"),
+    not all(
+        (
+            _check_soft_dependencies("statsmodels", severity="none"),
+            _check_soft_dependencies("pandas<2.0.0", severity="none"),
+        )
+    ),
     reason="skip test if required soft dependency not available",
 )
 def test_VARMAX_against_statsmodels_with_exog():
diff --git a/aeon/transformations/panel/dictionary_based/tests/test_paa.py b/aeon/transformations/panel/dictionary_based/tests/test_paa.py
index 1e02e6f2..34a12e62 100644
--- a/aeon/transformations/panel/dictionary_based/tests/test_paa.py
+++ b/aeon/transformations/panel/dictionary_based/tests/test_paa.py
@@ -14,16 +14,16 @@ def test_bad_input_args(bad_num_intervals):
     X = np.array([[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]])
     if not isinstance(bad_num_intervals, int):
         with pytest.raises(TypeError):
-            PAA(num_intervals=bad_num_intervals).fit(X).transform(X)
+            PAA(n_intervals=bad_num_intervals).fit(X).transform(X)
     else:
         with pytest.raises(ValueError):
-            PAA(num_intervals=bad_num_intervals).fit(X).transform(X)
+            PAA(n_intervals=bad_num_intervals).fit_transform(X)
 
 
 # Check the transformer has changed the data correctly.
 def test_output_of_transformer():
     X = np.array([[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]])
-    p = PAA(num_intervals=3).fit(X)
+    p = PAA(n_intervals=3).fit(X)
     res = p.transform(X)
     orig = np.array([[[2.2, 5.5, 8.8]]])
     np.testing.assert_array_almost_equal(res, orig)
@@ -32,7 +32,7 @@ def test_output_of_transformer():
 def test_output_dimensions():
     # test with univariate
     X = np.random.rand(10, 1, 12)
-    p = PAA(num_intervals=5).fit(X)
+    p = PAA(n_intervals=5).fit(X)
     res = p.transform(X)
     assert res.shape[0] == 10 and res.shape[1] == 1 and res.shape[2] == 5
 
@@ -40,7 +40,7 @@ def test_output_dimensions():
 # This is to check that PAA produces the same result along each dimension
 def test_paa_performs_correcly_along_each_dim():
     X = np.array([[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]])
-    p = PAA(num_intervals=3).fit(X)
+    p = PAA(n_intervals=3).fit(X)
     res = p.transform(X)
     orig = [[[2.2, 5.5, 8.8], [2.2, 5.5, 8.8]]]
     np.testing.assert_array_almost_equal(res, orig)
diff --git a/aeon/transformations/panel/tests/test_slope_transformer.py b/aeon/transformations/panel/tests/test_slope_transformer.py
index c9beb1ec..4e01e136 100644
--- a/aeon/transformations/panel/tests/test_slope_transformer.py
+++ b/aeon/transformations/panel/tests/test_slope_transformer.py
@@ -12,25 +12,26 @@ from aeon.transformations.panel.slope import SlopeTransformer
 # correct input is meant to be a positive integer of 1 or more.
 @pytest.mark.parametrize("bad_num_intervals", ["str", 1.2, -1.2, -1, {}, 0])
 def test_bad_input_args(bad_num_intervals):
-    X = np.ones(shape=(10, 1, 10))
+    X = np.ones(shape=(10, 10, 1))
     if not isinstance(bad_num_intervals, int):
         with pytest.raises(TypeError):
-            SlopeTransformer(n_intervals=bad_num_intervals).fit_transform(X)
+            SlopeTransformer(n_intervals=bad_num_intervals).fit(X).transform(X)
     else:
         with pytest.raises(ValueError):
-            SlopeTransformer(n_intervals=bad_num_intervals).fit_transform(X)
+            SlopeTransformer(n_intervals=bad_num_intervals).fit(X).transform(X)
 
 
 # Check the transformer has changed the data correctly.
 def test_output_of_transformer():
     X = np.array([[[4, 6, 10, 12, 8, 6, 5, 5]]])
-
     s = SlopeTransformer(n_intervals=2).fit(X)
     res = s.transform(X)
     orig = np.array([[[(5 + math.sqrt(41)) / 4, (1 + math.sqrt(101)) / -10]]])
-    np.testing.assert_array_almost_equal(res, orig, decimal=2)
+    np.testing.assert_array_almost_equal(res, orig, decimal=5)
+
     X = np.array([[[-5, 2.5, 1, 3, 10, -1.5, 6, 12, -3, 0.2]]])
-    res = s.fit_transform(X)
+    s = s.fit(X)
+    res = s.transform(X)
     orig = np.array(
         [
             [
@@ -41,35 +42,32 @@ def test_output_of_transformer():
             ]
         ]
     )
-    np.testing.assert_array_almost_equal(res, orig, decimal=2)
+    np.testing.assert_array_almost_equal(res, orig, decimal=5)
 
 
 @pytest.mark.parametrize("num_intervals,corr_series_length", [(2, 2), (5, 5), (8, 8)])
 def test_output_dimensions(num_intervals, corr_series_length):
-    X = np.ones(shape=(10, 1, 10))
-    s = SlopeTransformer(n_intervals=num_intervals)
-    res = s.fit_transform(X)
-
-    # get the dimension of the generated dataframe.
-    num_rows = res.shape[0]
-    num_cols = res.shape[1]
-    act_time_series_length = res.shape[2]
+    X = np.ones(shape=(10, 1, 13))
+    s = SlopeTransformer(n_intervals=num_intervals).fit(X)
+    res = s.transform(X)
+    n_cases, n_channels, series_length = res.shape
 
-    assert act_time_series_length == corr_series_length
-    assert num_rows == 10
-    assert num_cols == 1
+    assert series_length == corr_series_length
+    assert n_cases == 10
+    assert n_channels == 1
 
 
 # This is to check that Slope produces the same result along each dimension
 def test_slope_performs_correcly_along_each_dim():
-    X = np.array([[[4, 6, 10, 12, 8, 6, 5, 5]], [[4, 6, 10, 12, 8, 6, 5, 5]]])
-
+    X = np.array([[[4, 6, 10, 12, 8, 6, 5, 5], [4, 6, 10, 12, 8, 6, 5, 5]]])
     s = SlopeTransformer(n_intervals=2).fit(X)
     res = s.transform(X)
     orig = np.array(
         [
-            [[(5 + math.sqrt(41)) / 4, (1 + math.sqrt(101)) / -10]],
-            [[(5 + math.sqrt(41)) / 4, (1 + math.sqrt(101)) / -10]],
+            [
+                [(5 + math.sqrt(41)) / 4, (1 + math.sqrt(101)) / -10],
+                [(5 + math.sqrt(41)) / 4, (1 + math.sqrt(101)) / -10],
+            ]
         ]
     )
-    np.testing.assert_array_almost_equal(res, orig, decimal=2)
+    np.testing.assert_array_almost_equal(res, orig, decimal=5)
diff --git a/build_tools/check_install_from_test_pypi.sh b/build_tools/check_install_from_test_pypi.sh
deleted file mode 100644
index a99deec0..00000000
--- a/build_tools/check_install_from_test_pypi.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-# Helper script to download and install aeon from test PyPI to check wheel
-# and upload prior to new release
-
-set -e
-
-# Version to test, passed as input argument to script
-VERSION=$1
-
-# Make temporary directory
-echo "Making test directory ..."
-mkdir "$HOME"/testdir
-cd "$HOME"/testdir
-
-# Create test environment
-echo "Creating test environment ..."
-
-# shellcheck disable=SC1091
-source "$(conda info --base)"/etc/profile.d/conda.sh  # set up conda
-conda create -n aeon_testenv python=3.9
-conda activate aeon_testenv
-
-# Install from test PyPI
-echo "Installing aeon from Test PyPI ..."
-pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple aeon=="$VERSION"
-echo "Successfully installed aeon from Test PyPI."
-
-# Clean up test directory and environment
-echo "Cleaning up ..."
-conda deactivate
-conda remove -n aeon_testenv --all -y
-rm -r "$HOME"/testdir
-
-echo "Done."