Repository: aeon-toolkit/aeon
Pull request #510 (2023-06-23 15:06:00)

[ENH] Add function to make example 3D numpy problems
#### Reference Issues/PRs

Part of #499.

#### What does this implement/fix? Explain your changes.

Essentially wraps the testing data generator for 3D numpy but is public facing. Updates a few parameter named from `series_length` to `n_timepoints`.


Patch:
diff --git a/aeon/datasets/__init__.py b/aeon/datasets/__init__.py
index 0a98ab67..1a076dbb 100644
--- a/aeon/datasets/__init__.py
+++ b/aeon/datasets/__init__.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+# copyright: aeon developers, BSD-3-Clause License (see LICENSE file)
 """Functions to load and write datasets."""
 
 __all__ = [
@@ -15,6 +16,7 @@ __all__ = [
     "write_to_tsfile",
     "write_results_to_uea_format",
     # Data generators
+    "make_example_3d_numpy",
     "make_example_long_table",
     "make_example_multi_index_dataframe",
     # Single problem loaders
@@ -48,6 +50,7 @@ __all__ = [
 ]
 
 from aeon.datasets._data_generators import (
+    make_example_3d_numpy,
     make_example_long_table,
     make_example_multi_index_dataframe,
 )
@@ -64,6 +67,7 @@ from aeon.datasets._data_loaders import (
 from aeon.datasets._data_writers import write_results_to_uea_format, write_to_tsfile
 from aeon.datasets._dataframe_loaders import (
     load_from_long_to_dataframe,
+    load_from_tsfile_to_dataframe,
     load_tsf_to_dataframe,
 )
 from aeon.datasets._single_problem_loaders import (
diff --git a/aeon/datasets/_data_generators.py b/aeon/datasets/_data_generators.py
index 1a41b799..bcbc9c74 100644
--- a/aeon/datasets/_data_generators.py
+++ b/aeon/datasets/_data_generators.py
@@ -1,28 +1,104 @@
 # -*- coding: utf-8 -*-
-from typing import Dict
+# copyright: aeon developers, BSD-3-Clause License (see LICENSE file)
+"""Data generators."""
+
+__author__ = ["MatthewMiddlehurst", "TonyBagnall"]
+__all__ = [
+    "make_example_3d_numpy",
+    "make_example_long_table",
+    "make_example_multi_index_dataframe",
+]
+
+from typing import Dict, Tuple, Union
 
 import numpy as np
 import pandas as pd
 
+from aeon.utils._testing.collection import make_3d_test_data
+
+
+def make_example_3d_numpy(
+    n_cases: int = 10,
+    n_channels: int = 1,
+    n_timepoints: int = 12,
+    return_y: bool = False,
+    n_labels: int = 2,
+    regression_target: bool = False,
+    random_state: Union[int, None] = None,
+) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
+    """Randomly generate 3D X and y data for testing.
+
+    Will ensure there is at least one sample per label if a regression problems.
+
+    Parameters
+    ----------
+    n_cases : int
+        The number of samples to generate.
+    n_channels : int
+        The number of series channels to generate.
+    n_timepoints : int
+        The number of features/series length to generate.
+    return_y : bool
+        If True, return the labels as well as the data.
+    n_labels : int
+        The number of unique labels to generate.
+    regression_target : bool
+        If True, the target will be a float, otherwise an int.
+    random_state : int or None
+        Seed for random number generation.
 
-def make_example_long_table(n_cases=50, n_channels=2, series_length=20):
+    Returns
+    -------
+    X : np.ndarray
+        Randomly generated 3D data.
+    y : np.ndarray
+        Randomly generated labels.
+
+    Examples
+    --------
+    >>> from aeon.datasets import make_example_3d_numpy
+    >>> data, labels = make_example_3d_numpy(n_cases=2, n_channels=2, n_timepoints=6,
+    ...                                      return_y=True, n_labels=2, random_state=0)
+    >>> print(data)
+    [[[0.         1.43037873 1.20552675 1.08976637 0.8473096  1.29178823]
+      [0.87517442 1.783546   1.92732552 0.76688304 1.58345008 1.05778984]]
+    <BLANKLINE>
+     [[2.         3.70238655 0.28414423 0.3485172  0.08087359 3.33047938]
+      [3.112627   3.48004859 3.91447337 3.19663426 1.84591745 3.12211671]]]
+    >>> print(labels)
+    [0 1]
+    """
+    X, y = make_3d_test_data(
+        n_cases=n_cases,
+        n_channels=n_channels,
+        n_timepoints=n_timepoints,
+        n_labels=n_labels,
+        regression_target=regression_target,
+        random_state=random_state,
+    )
+    if return_y:
+        return X, y
+    return X
+
+
+def make_example_long_table(n_cases=50, n_channels=2, n_timepoints=20):
     """Generate example from long table format file.
 
     Parameters
     ----------
     n_cases: int
         Number of cases.
-    series_length: int
-        Length of the series.
     n_channels: int
         Number of dimensions.
+    n_timepoints: int
+        Length of the series.
 
     Returns
     -------
     DataFrame containing random data in long format.
     """
-    rows_per_case = series_length * n_channels
-    total_rows = n_cases * series_length * n_channels
+    rows_per_case = n_timepoints * n_channels
+    total_rows = n_cases * n_timepoints * n_channels
 
     case_ids = np.empty(total_rows, dtype=int)
     idxs = np.empty(total_rows, dtype=int)
@@ -32,8 +108,8 @@ def make_example_long_table(n_cases=50, n_channels=2, series_length=20):
     for i in range(total_rows):
         case_ids[i] = int(i / rows_per_case)
         rem = i % rows_per_case
-        dims[i] = int(rem / series_length)
-        idxs[i] = rem % series_length
+        dims[i] = int(rem / n_timepoints)
+        idxs[i] = rem % n_timepoints
 
     df = pd.DataFrame()
     df["case_id"] = pd.Series(case_ids)
@@ -43,7 +119,7 @@ def make_example_long_table(n_cases=50, n_channels=2, series_length=20):
     return df
 
 
-def make_example_multi_index_dataframe(n_instances=50, n_channels=3, series_length=20):
+def make_example_multi_index_dataframe(n_instances=50, n_channels=3, n_timepoints=20):
     """Generate example multi-index DataFrame.
 
     Parameters
@@ -52,7 +128,7 @@ def make_example_multi_index_dataframe(n_instances=50, n_channels=3, series_leng
         Number of instances.
     n_channels : int
         Number of columns (series) in multi-indexed DataFrame.
-    series_length : int
+    n_timepoints : int
         Number of timepoints per instance-column pair.
 
     Returns
@@ -63,7 +139,7 @@ def make_example_multi_index_dataframe(n_instances=50, n_channels=3, series_leng
     """
     # Make long DataFrame
     long_df = make_example_long_table(
-        n_cases=n_instances, series_length=series_length, n_channels=n_channels
+        n_cases=n_instances, n_timepoints=n_timepoints, n_channels=n_channels
     )
     # Make Multi index DataFrame
     mi_df = long_df.set_index(["case_id", "reading_id"]).pivot(columns="dim_id")


Test patch:
diff --git a/aeon/datatypes/tests/test_panel_converters.py b/aeon/datatypes/tests/test_panel_converters.py
index 2eb317a1..fca11e97 100644
--- a/aeon/datatypes/tests/test_panel_converters.py
+++ b/aeon/datatypes/tests/test_panel_converters.py
@@ -109,7 +109,7 @@ def test_from_3d_numpy_to_2d_array(n_instances, n_columns, n_timepoints):
 def test_from_multi_index_to_3d_numpy(n_instances, n_columns, n_timepoints):
     """Test from_multi_index_to_3d_numpy for correctness."""
     mi_df = make_example_multi_index_dataframe(
-        n_instances=n_instances, series_length=n_timepoints, n_channels=n_columns
+        n_instances=n_instances, n_timepoints=n_timepoints, n_channels=n_columns
     )
 
     array = from_multi_index_to_3d_numpy(mi_df)
@@ -149,7 +149,7 @@ def test_from_3d_numpy_to_multi_index(n_instances, n_columns, n_timepoints):
 def test_from_multi_index_to_nested(n_instances, n_columns, n_timepoints):
     """Test from_multi_index_to_nested for correctness."""
     mi_df = make_example_multi_index_dataframe(
-        n_instances=n_instances, series_length=n_timepoints, n_channels=n_columns
+        n_instances=n_instances, n_timepoints=n_timepoints, n_channels=n_columns
     )
     nested_df = from_multi_index_to_nested(
         mi_df, instance_index="case_id", cells_as_numpy=False
@@ -188,7 +188,7 @@ def test_is_nested_dataframe(n_instances, n_columns, n_timepoints):
     nested_heterogenous = pd.concat([zero_df, nested], axis=1)
 
     mi_df = make_example_multi_index_dataframe(
-        n_instances=n_instances, series_length=n_timepoints, n_channels=n_columns
+        n_instances=n_instances, n_timepoints=n_timepoints, n_channels=n_columns
     )
 
     assert not is_nested_dataframe(array)
@@ -216,7 +216,7 @@ def test_from_2d_array_to_nested(n_instances, n_columns, n_timepoints):
 def test_from_long_to_nested(n_instances, n_columns, n_timepoints):
     """Test from_long_to_nested for correctness."""
     X_long = make_example_long_table(
-        n_cases=n_instances, series_length=n_timepoints, n_channels=n_columns
+        n_cases=n_instances, n_timepoints=n_timepoints, n_channels=n_columns
     )
     nested_df = from_long_to_nested(X_long)