Source code for transient_data_table.scripting_api.transient_data_table_setup

import typing

from more.api.exceptions.api_exception import NameNotFoundError, IndexNotFoundError, NotCompatibleError, \
    NameNotUniqueError
from typing import NamedTuple
from more.api.exceptions.api_exception import NameNotFoundError, IndexNotFoundError, NotCompatibleError
from more.api.simulation.data_tables.data_table_setup import DataTableSetup
from more.api.utils.change_duplicate_name import change_duplicate_name
from more.api.utils.interface_registries import register_api_implementation
from more.transient_data_table import TransientDataTable
from more import log
from os import PathLike
import numpy as np
import numpy.typing as npt

log.init_logging()
logger = log.getLogger(__name__)


def _create_transient_data_table_setup(api) -> 'TransientDataTableSetup':
    simulation_setup = api.create_simulation_setup()
    return simulation_setup.create_data_table_setup(data_table_type_name='Transient data table')


[docs] class VarAndData(NamedTuple): """ Named tuple containing the data points and the data itself with shapes (n), (n, m) respectively Shapes are not enforced """ var: npt.ArrayLike data: npt.ArrayLike
[docs] @register_api_implementation(core_class=TransientDataTable) class TransientDataTableSetup(DataTableSetup): """ API class for handling transient data tables. In order to create a transient data table using the API perform the following: .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> simulation_setup = ApiGateway(proj=proj).create_simulation_setup() >>> data_table_setup = simulation_setup \\ ... .create_data_table_setup(data_table_type_name='Transient data table') .. >>> isinstance(data_table_setup._data_table, TransientDataTable) True In order to get an API setup object for a transient data table that already exists: .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> simulation_setup = ApiGateway(proj=proj).create_simulation_setup() .. >>> data_table_setup_1 = simulation_setup \\ ... .create_data_table_setup(data_table_type_name='Transient data table').set_name(name='Example data table') >>> data_table_setup = simulation_setup.get_data_table_setup(name='Example data table') # The data table must already exist .. >>> data_table_setup._data_table == data_table_setup_1._data_table True The following examples assume that a TransientDataTableSetup class exists as created by one of the above methods. """
[docs] def import_data(self, path: typing.Union[str, PathLike]) -> 'TransientDataTableSetup': """ Imports data from a .mat file to the data table .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup_1 = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup_1 = data_table_setup_1 \\ ... .set_data(var=var_val, data=data_val) >>> data_table_setup_2 = _create_transient_data_table_setup(api) >>> import tempfile >>> import os >>> with tempfile.TemporaryDirectory(prefix='more_') as tmp_dir: ... data_path = os.path.join(tmp_dir, 'data.mat') ... data_table_setup_1 = data_table_setup_1.export_data(path=data_path) ... data_table_setup_2 = data_table_setup_2.import_data(path=data_path) .. >>> np.allclose(data_table_setup_1._data_table.dataset.var, data_table_setup_2._data_table.dataset.var) True >>> np.allclose(data_table_setup_1._data_table.dataset.data, data_table_setup_2._data_table.dataset.data) True >>> data_table_setup_1.import_data(path='Non existent path.mat') Traceback (most recent call last): ... FileNotFoundError: [Errno 2] No such file or directory: 'Non existent path.mat' Parameters ---------- path A string of PathLike with the path to the desired export location Returns ------- self This object Raises ------ FileNotFoundError Raised if the file with the given name was not found """ self.transient_data_table.import_data(path) return self
[docs] def export_data(self, path: typing.Union[str, PathLike]) -> 'TransientDataTableSetup': """ Exports a data to a given location as a .mat file .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup.set_data(var=var_val, data=data_val) >>> import tempfile >>> import os >>> with tempfile.TemporaryDirectory(prefix='more_') as tmp_dir: ... data_path = os.path.join(tmp_dir, 'data.mat') ... data_table_setup.export_data(path=data_path) <...> Parameters ---------- path A string of PathLike with the path to the desired export location Returns ------- self This object Raises ------ """ self.transient_data_table.export_data(path) return self
@property def available_interpolation_method_names(self) -> typing.List[str]: """ Returns the available interpolation method names .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> print(f'Available interpolation methods are: \"{data_table_setup.available_interpolation_method_names}\"') Available interpolation methods are: "['hold', 'nearest']" Returns ------- methods: List[str] The available interpolation methods """ return self.transient_data_table.dataset.get_compatible_interpolation_methods()
[docs] def set_interpolation_method(self, method_name: str) -> 'TransientDataTableSetup': """ Sets the interpolation method .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup \\ ... .set_data(var=var_val, data=data_val) >>> data_table_setup = data_table_setup.set_interpolation_method(method_name='linear') .. >>> data_table_setup._data_table.interpolation == 'linear' True >>> data_table_setup.set_interpolation_method(method_name='Non existent interpolation method') Traceback (most recent call last): ... more.api.exceptions.api_exception.NameNotFoundError: Interpolation method with name: "Non existent interpolation method" not found. >>> data_table_setup.set_interpolation_method(method_name=None) Traceback (most recent call last): ... TypeError: The interpolation_method must be a string, but a type: "<class 'NoneType'>" was given >>> data_table_setup.set_interpolation_method(method_name='bspline') Traceback (most recent call last): ... more.api.exceptions.api_exception.NotCompatibleError: Interpolation method: "bspline" is not compatible with the current data shape. Compatible types are: "['linear', 'hold', 'nearest']" Parameters ---------- method_name The name of the interpolation method Returns ------- self This object Raises ------ TypeError Raised it the interpolation method name is not a string NameNotFoundError Raised if the given interpolation method name does not exist NotCompatibleError Raised if the interpolation method exists, but is not compatible with the data """ if not isinstance(method_name, str): raise TypeError(f'The interpolation_method must be a string, but a type: \"{type(method_name)}\" was given') if method_name not in list(self.transient_data_table.dataset.trait('interpolation').trait_type.values): raise NameNotFoundError(f'Interpolation method with name: \"{method_name}\" not found.') if method_name not in self.available_interpolation_method_names: raise NotCompatibleError(f'Interpolation method: \"{method_name}\" is not compatible with the current data shape. Compatible types are: \"{self.available_interpolation_method_names}\"') self._data_table.interpolation = method_name return self
[docs] def set_data(self, var: np.ndarray, data: np.ndarray) -> 'TransientDataTableSetup': """ Sets the data table data .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup \\ ... .set_data(var=var_val, data=data_val) .. >>> np.allclose(data_table_setup._data_table.dataset.var, var_val) True >>> np.allclose(data_table_setup._data_table.dataset.data, data_val) True >>> data_table_setup.set_data(var='not a numpy array', data=data_val) Traceback (most recent call last): ... TypeError: The "var" value must be a numpy array, but type: "<class 'str'>" was given >>> data_table_setup.set_data(var=var_val, data='Not an array') Traceback (most recent call last): ... TypeError: The "data" value must be a numpy array, but type: "<class 'str'>" was given >>> data_table_setup.set_data(var=var_val, data=np.array([[0, 1], [1e-3, 2]])) Traceback (most recent call last): ... ValueError: The "var" and "data" shapes should be respectively (n, ) and (m, n) but var.shape = (3,) and data.shape = (2, 2) were given >>> data_table_setup.set_data(var=var_val, data=np.array([0, 1, 2])) Traceback (most recent call last): ... ValueError: The "var" and "data" shapes should be respectively (n, ) and (m, n) but var.shape = (3,) and data.shape = (3,) were given Parameters ---------- var: np.ndarray A 1D array of shape (n, ) representing the data points of the array, with 'n' being the number of rows in the table. The table will grow or shrink to accomodate this size. data: np.ndarray A 2D array of shape (m, n) with 'n' being the number of rows, and 'm' being the number of data-carrying columns in the table. Returns ------- self This object Raises ------ TypeError Raised it either var or data are not numpy arrays ValueError Raised if the provided var or data shapes are not compatible """ if not isinstance(var, np.ndarray): raise TypeError(f'The \"var\" value must be a numpy array, but type: \"{type(var)}\" was given') if not isinstance(data, np.ndarray): raise TypeError(f'The \"data\" value must be a numpy array, but type: \"{type(data)}\" was given') if not len(var.shape) == 1 or not len(data.shape) == 2 or data.shape[1] != var.shape[0]: raise ValueError(f'The \"var\" and \"data\" shapes should be respectively (n, ) and (m, n) but var.shape = {var.shape} and data.shape = {data.shape} were given') self._data_table.set_data(var, data) return self
[docs] def set_column_name(self, index: int, new_name: str) -> 'TransientDataTableSetup': """ Set the name of the column .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup \\ ... .set_data(var=var_val, data=data_val) >>> data_table_setup = data_table_setup.set_column_name(index=1, new_name='Second column') .. >>> data_table_setup._data_table.column_headers[2] == 'Second column' True >>> data_table_setup.set_column_name(index=3, new_name='Some name') Traceback (most recent call last): ... more.api.exceptions.api_exception.IndexNotFoundError: Index "3" is out of bounds, the highest possible index is: "1" >>> data_table_setup.set_column_name(index=None, new_name='Some name') Traceback (most recent call last): ... TypeError: ... >>> data_table_setup.set_column_name(index=2, new_name=None) Traceback (most recent call last): ... TypeError: ... Parameters ---------- index The index of the column whose name is to be changed new_name The new name to apply Returns ------- self This object Raises ------ IndexNotFoundError Raised if the given index is out of range for the given data table TypeError Raised if one of the parameters has the wrong type """ if not isinstance(index, int): raise TypeError(f'The index must be an integer, but type: \"{type(index)}\" was given') if not isinstance(new_name, str): raise TypeError(f'The new_name must be a string, but type: \"{type(index)}\" was given') headers = self._data_table.column_headers[1:] if index >= len(headers): raise IndexNotFoundError(f'Index \"{index}\" is out of bounds, the highest possible index is: \"{len(headers) - 1}\"') self._data_table.tabular.column_headers[index + 1] = new_name return self
[docs] def get_column_name(self, index: int) -> str: """ Get the name of the column from the index .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup \\ ... .set_data(var=var_val, data=data_val) >>> data_table_setup = data_table_setup.set_column_name(index=1, new_name='Second column') >>> data_table_setup.get_column_name(index=1) 'Second column' .. >>> data_table_setup.get_column_name(index=10) Traceback (most recent call last): ... more.api.exceptions.api_exception.IndexNotFoundError: Index "10" is out of bounds, the highest possible index is: "1" >>> data_table_setup.get_column_name(index=None) Traceback (most recent call last): ... TypeError: ... Parameters ---------- index The index of the column for which teh name is requested Returns ------- column_name: str the name of the column Raises ------ TypeError Raised if the index is not an integer IndexNotFoundError Raised if the given index is not in range """ if not isinstance(index, int): raise TypeError(f'The index must be an integer, but type: \"{type(index)}\" was given') headers = self._data_table.column_headers[1:] if index >= len(headers): raise IndexNotFoundError(f'Index \"{index}\" is out of bounds, the highest possible index is: \"{len(headers) - 1}\"') return self._data_table.column_headers[index + 1]
[docs] def get_column_index(self, name: str) -> int: """ Get the index of a column with a given name .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup \\ ... .set_data(var=var_val, data=data_val) >>> data_table_setup = data_table_setup.set_column_name(index=1, new_name='Second column') >>> data_table_setup.get_column_index(name='Second column') 1 .. >>> data_table_setup.get_column_index(name='Non existent name') Traceback (most recent call last): ... more.api.exceptions.api_exception.NameNotFoundError: ... >>> data_table_setup.get_column_index(name=None) Traceback (most recent call last): ... TypeError: A column name must be a string, but type: "<class 'NoneType'>" was given >>> idx = data_table_setup.get_column_index(name='Second column') >>> data_table_setup.get_column_name(index=idx) 'Second column' Parameters ---------- name: str The name of the column for which to returen the index Returns ------- column_index: int The index of the column with the given name Raises ------ NameNotFoundError Raised if a column with such a name does not exist TypeError Raised if the given parameter is not a string """ if not isinstance(name, str): raise TypeError(f'A column name must be a string, but type: \"{type(name)}\" was given') headers = self._data_table.column_headers[1:] if name not in headers: raise NameNotFoundError(f'Name: \"{name}\" is not one of the column header names, available names are: \"{headers}\" ') return self._data_table.column_headers.index(name) - 1
[docs] def get_data(self) -> VarAndData: """ Returns the data table data .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> import numpy as np >>> var_val, data_val = np.array([0, 0.3, 0.6]), np.array([[0, 1e-3, 2e-3], [1, 2, 3]]) >>> data_table_setup = data_table_setup \\ ... .set_data(var=var_val, data=data_val) >>> var, data = data_table_setup.get_data() .. >>> np.allclose(var, var_val) True >>> np.allclose(data, data_val) True Returns ------- data tuple Var and data in the form of a named tuple containing the data points and the data itself from the data table with shapes (n), (n, m) respectively """ return self._data_table.dataset.var, self._data_table.dataset.data
[docs] def set_name(self, name: str, resolve_duplicate_name: bool = False) -> 'DataTableSetup': """ Changes the name of the simresults .. admonition:: Example :class: note .. >>> import more.project >>> proj = more.project.Project() >>> from more.api import ApiGateway >>> api = ApiGateway(proj=proj) >>> data_table_setup = _create_transient_data_table_setup(api) >>> second_data_table_setup = _create_transient_data_table_setup(api) >>> data_table_setup.set_name('new_name') <more...> >>> second_data_table_setup.set_name(name='new_name', resolve_duplicate_name=True).get_name() 'new_name 1' .. >>> data_table_setup._data_table.name 'new_name' Trying to set the name to a non-string value >>> data_table_setup.set_name(name=None) Traceback (most recent call last): ... TypeError: ... Setting a non-unique name >>> second_data_table_setup.set_name('new_name') Traceback (most recent call last): ... more.api.exceptions.api_exception.NameNotUniqueError: ... Parameters ---------- resolve_duplicate_name: bool Whether to automatically assign a new name when the chosen one is already taken name: str The new name Returns ------- self This object Raises ------ TypeError Raised if the given name is not a string NameNotUniqueError Raised if the given name is not unique """ pass if not isinstance(name, str): raise TypeError("The given name: \"{}\" is not a string, but a \"{}\"".format(name, type(name))) created_names = [dt.name for dt in self._data_table.parent.elements] if name in created_names: if not resolve_duplicate_name: raise NameNotUniqueError(f'The given name: \"{name}\" is already given in the list: \"{created_names}\"') else: name = change_duplicate_name(name=name, names_list=created_names) self._data_table.name = name return self
def get_name(self) -> str: return self._data_table.name