From ca23f0f6f6f42a36f79a38d1a7a696ca523bfd0c Mon Sep 17 00:00:00 2001 From: Gerhardsa0 <113539440+Gerhardsa0@users.noreply.github.com> Date: Mon, 18 Mar 2024 16:23:57 +0100 Subject: [PATCH] feat: when using from table to time series feature must be given (#572) Closes #571 ### Summary of Changes TimeSeries no longer is subclass of TaggedTable only of Table --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> --- src/safeds/data/tabular/containers/_table.py | 4 +- .../data/tabular/containers/_time_series.py | 359 ++++++++++-------- tests/helpers/_assertions.py | 5 +- .../_tagged_table/_time_series/test_eq.py | 61 --- .../_tagged_table/_time_series/test_hash.py | 42 -- .../_tagged_table/_time_series/test_sizeof.py | 39 -- .../_time_series/__init__.py | 0 .../test_should_return_table.png | Bin .../test_should_plot_feature.png | Bin .../test_should_plot_feature_x.png | Bin .../test_should_plot_feature_y.png | Bin .../test_should_return_table.png | Bin .../test_should_return_table_both.png | Bin .../test_should_plot_feature.png | Bin .../test_should_plot_feature_both_set.png | Bin .../test_should_plot_feature_only_x.png | Bin ...st_should_plot_feature_only_y_optional.png | Bin .../test_should_return_table.png | Bin .../_time_series/test_add_column.py | 2 +- .../test_add_column_as_feature.py | 0 .../_time_series/test_add_columns.py | 2 +- .../test_add_columns_as_features.py | 0 .../_time_series/test_add_row.py | 0 .../_time_series/test_add_rows.py | 0 .../_time_series/test_as_table.py | 0 .../containers/_time_series/test_eq.py | 100 +++++ .../_time_series/test_features.py | 1 + .../_time_series/test_filter_rows.py | 0 .../test_from_table_to_time_series.py | 39 +- .../_time_series/test_from_tagged_table.py | 0 .../containers/_time_series/test_hash.py | 64 ++++ .../_time_series/test_init.py | 36 +- .../_time_series/test_keep_only_columns.py | 36 +- .../_time_series/test_plot_lag.py | 0 .../_time_series/test_plot_lineplot.py | 0 .../_time_series/test_plot_scatterplot.py | 0 .../_time_series/test_remove_columns.py | 52 +-- ...test_remove_columns_with_missing_values.py | 34 +- ...emove_columns_with_non_numerical_values.py | 34 +- .../test_remove_duplicate_rows.py | 0 .../test_remove_rows_with_missing_values.py | 0 .../test_remove_rows_with_outliers.py | 0 .../_time_series/test_rename_column.py | 0 .../_time_series/test_replace_column.py | 0 .../containers/_time_series/test_sizeof.py | 37 ++ .../_time_series/test_slice_rows.py | 8 +- .../_time_series/test_sort_columns.py | 4 +- .../containers/_time_series/test_time.py | 42 ++ .../_time_series/test_time_target.py | 0 .../_time_series/test_transform_column.py | 0 50 files changed, 486 insertions(+), 515 deletions(-) delete mode 100644 tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_eq.py delete mode 100644 tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_hash.py delete mode 100644 tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__init__.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_lag/test_should_return_table.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_x.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_y.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table_both.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_both_set.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_x.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_y_optional.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/__snapshots__/test_plot_scatterplot/test_should_return_table.png (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_add_column.py (96%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_add_column_as_feature.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_add_columns.py (97%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_add_columns_as_features.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_add_row.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_add_rows.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_as_table.py (100%) create mode 100644 tests/safeds/data/tabular/containers/_time_series/test_eq.py rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_features.py (96%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_filter_rows.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_from_table_to_time_series.py (83%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_from_tagged_table.py (100%) create mode 100644 tests/safeds/data/tabular/containers/_time_series/test_hash.py rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_init.py (81%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_keep_only_columns.py (79%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_plot_lag.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_plot_lineplot.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_plot_scatterplot.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_remove_columns.py (78%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_remove_columns_with_missing_values.py (84%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_remove_columns_with_non_numerical_values.py (84%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_remove_duplicate_rows.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_remove_rows_with_missing_values.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_remove_rows_with_outliers.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_rename_column.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_replace_column.py (100%) create mode 100644 tests/safeds/data/tabular/containers/_time_series/test_sizeof.py rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_slice_rows.py (88%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_sort_columns.py (94%) create mode 100644 tests/safeds/data/tabular/containers/_time_series/test_time.py rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_time_target.py (100%) rename tests/safeds/data/tabular/containers/{_table/_tagged_table => }/_time_series/test_transform_column.py (100%) diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index 1ff71abc8..f5bb80b34 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -1,8 +1,8 @@ from __future__ import annotations -import sys import functools import io +import sys import warnings from pathlib import Path from typing import TYPE_CHECKING, Any, TypeVar @@ -1774,7 +1774,7 @@ def time_columns(self, target_name: str, time_name: str, feature_names: list[str """ from ._time_series import TimeSeries - return TimeSeries._from_table_to_time_series(self, target_name, time_name, feature_names) + return TimeSeries._from_table(self, target_name, time_name, feature_names) def transform_column(self, name: str, transformer: Callable[[Row], Any]) -> Table: """ diff --git a/src/safeds/data/tabular/containers/_time_series.py b/src/safeds/data/tabular/containers/_time_series.py index 1612935ac..105c29258 100644 --- a/src/safeds/data/tabular/containers/_time_series.py +++ b/src/safeds/data/tabular/containers/_time_series.py @@ -24,7 +24,7 @@ from typing import Any -class TimeSeries(TaggedTable): +class TimeSeries(Table): # ------------------------------------------------------------------------------------------------------------------ # Creation @@ -81,7 +81,7 @@ def _from_tagged_table( return result @staticmethod - def _from_table_to_time_series( + def _from_table( table: Table, target_name: str, time_name: str, @@ -109,8 +109,6 @@ def _from_table_to_time_series( ------ UnknownColumnNameError If target_name or time_name matches none of the column names. - Value Error - If there is no other column than the specified target and time columns left to be a feature column Value Error If one column is target and feature Value Error @@ -119,21 +117,32 @@ def _from_table_to_time_series( Examples -------- >>> from safeds.data.tabular.containers import Table, TimeSeries - >>> table = Table({"date": ["01.01", "01.02", "01.03", "01.04"], "f1": ["a", "b", "c", "a"], "t": [1,2,3,4]}) - >>> timeseries = TimeSeries._from_table_to_time_series(table, "t", "date", ["f1"]) + >>> test_table = Table({"date": ["01.01", "01.02", "01.03", "01.04"], "f1": ["a", "b", "c", "a"], "t": [1,2,3,4]}) + >>> timeseries = TimeSeries._from_table(test_table, "t", "date", ["f1"]) """ + table = table._as_table() if feature_names is not None and time_name in feature_names: raise ValueError(f"Column '{time_name}' can not be time and feature column.") + if feature_names is not None and target_name in feature_names: + raise ValueError(f"Column '{target_name}' can not be target and feature column.") + + if target_name not in table.column_names: + raise UnknownColumnNameError([target_name]) + + result = object.__new__(TimeSeries) + result._data = table._data + result._schema = table._schema + result._time = table.get_column(time_name) + result._target = table.get_column(target_name) + if feature_names is None or len(feature_names) == 0: + result._feature_names = [] + result._features = Table() + else: + result._feature_names = feature_names + result._features = table.keep_only_columns(feature_names) - if feature_names is None: - feature_names = table.column_names - if time_name in feature_names: - feature_names.remove(time_name) - if target_name in feature_names: - feature_names.remove(target_name) - tagged_table = TaggedTable._from_table(table=table, target_name=target_name, feature_names=feature_names) # check if time column got added as feature column - return TimeSeries._from_tagged_table(tagged_table=tagged_table, time_name=time_name) + return result # ------------------------------------------------------------------------------------------------------------------ # Dunder methods @@ -166,8 +175,6 @@ def __init__( If columns have different lengths. ValueError If the target column is also a feature column. - ValueError - If no feature columns are specified. ValueError If time column is also a feature column UnknownColumnNameError @@ -178,22 +185,24 @@ def __init__( >>> from safeds.data.tabular.containers import TaggedTable >>> table = TaggedTable({"a": [1, 2, 3], "b": [4, 5, 6]}, "b", ["a"]) """ + # Validate inputs + super().__init__(data) _data = Table(data) - if feature_names is None: - feature_names = _data.column_names - if time_name in feature_names: - feature_names.remove(time_name) - if target_name in feature_names: - feature_names.remove(target_name) - - # Validate inputs - super().__init__(data, target_name, feature_names) + self._features = Table() + self._feature_names = [] + feature_names = [] + else: + self._feature_names = feature_names + self._features = _data.keep_only_columns(feature_names) if time_name in feature_names: raise ValueError(f"Column '{time_name}' can not be time and feature column.") - if time_name not in (_data.column_names): + if target_name in feature_names: + raise ValueError(f"Column '{target_name}' can not be time and feature column.") + if time_name not in _data.column_names: raise UnknownColumnNameError([time_name]) self._time: Column = _data.get_column(time_name) + self._target: Column = _data.get_column(target_name) def __eq__(self, other: object) -> bool: """ @@ -207,7 +216,12 @@ def __eq__(self, other: object) -> bool: return NotImplemented if self is other: return True - return self.time == other.time and TaggedTable.__eq__(self, other) + return ( + self.time == other.time + and self.target == other.target + and self.features == other.features + and Table.__eq__(self, other) + ) def __hash__(self) -> int: """ @@ -218,7 +232,12 @@ def __hash__(self) -> int: hash : int The hash value. """ - return xxhash.xxh3_64(hash(self.time).to_bytes(8) + TaggedTable.__hash__(self).to_bytes(8)).intdigest() + return xxhash.xxh3_64( + hash(self.time).to_bytes(8) + + hash(self.target).to_bytes(8) + + hash(self.features).to_bytes(8) + + Table.__hash__(self).to_bytes(8), + ).intdigest() def __sizeof__(self) -> int: """ @@ -228,12 +247,36 @@ def __sizeof__(self) -> int: ------- Size of this object in bytes. """ - return TaggedTable.__sizeof__(self) + sys.getsizeof(self._time) + return Table.__sizeof__(self) + sys.getsizeof(self._time) # ------------------------------------------------------------------------------------------------------------------ # Properties # ------------------------------------------------------------------------------------------------------------------ + @property + def target(self) -> Column: + """ + Get the target column of the tagged table. + + Returns + ------- + Column + The target column. + """ + return self._target + + @property + def features(self) -> Table: + """ + Get the feature columns of the tagged table. + + Returns + ------- + Table + The table containing the feature columns. + """ + return self._features + @property def time(self) -> Column: """ @@ -291,9 +334,10 @@ def add_column(self, column: Column) -> TimeSeries: ColumnSizeError If the size of the column does not match the number of rows. """ - return TimeSeries._from_tagged_table( + return TimeSeries._from_table( super().add_column(column), time_name=self.time.name, + target_name=self._target.name, ) def add_column_as_feature(self, column: Column) -> TimeSeries: @@ -319,9 +363,11 @@ def add_column_as_feature(self, column: Column) -> TimeSeries: ColumnSizeError If the size of the column does not match the number of rows. """ - return TimeSeries._from_tagged_table( - super().add_column_as_feature(column), + return TimeSeries._from_table( + super().add_column(column), + target_name=self._target.name, time_name=self.time.name, + feature_names=[*self._feature_names, column.name], ) def add_columns_as_features(self, columns: list[Column] | Table) -> TimeSeries: @@ -347,9 +393,12 @@ def add_columns_as_features(self, columns: list[Column] | Table) -> TimeSeries: ColumnSizeError If the size of any feature column does not match the number of rows. """ - return TimeSeries._from_tagged_table( - super().add_columns_as_features(columns), + return TimeSeries._from_table( + super().add_columns(columns), time_name=self.time.name, + target_name=self._target.name, + feature_names=self._feature_names + + [col.name for col in (columns.to_columns() if isinstance(columns, Table) else columns)], ) def add_columns(self, columns: list[Column] | Table) -> TimeSeries: @@ -375,9 +424,11 @@ def add_columns(self, columns: list[Column] | Table) -> TimeSeries: ColumnSizeError If at least one of the column sizes from the provided column list does not match the time series. """ - return TimeSeries._from_tagged_table( + return TimeSeries._from_table( super().add_columns(columns), time_name=self.time.name, + target_name=self._target.name, + feature_names=self._feature_names, ) def add_row(self, row: Row) -> TimeSeries: @@ -401,7 +452,12 @@ def add_row(self, row: Row) -> TimeSeries: UnknownColumnNameError If the row has different column names than the time series. """ - return TimeSeries._from_tagged_table(super().add_row(row), time_name=self.time.name) + return TimeSeries._from_table( + super().add_row(row), + target_name=self._target.name, + time_name=self.time.name, + feature_names=self._feature_names, + ) def add_rows(self, rows: list[Row] | Table) -> TimeSeries: """ @@ -424,7 +480,12 @@ def add_rows(self, rows: list[Row] | Table) -> TimeSeries: UnknownColumnNameError If at least one of the rows have different column names than the time series. """ - return TimeSeries._from_tagged_table(super().add_rows(rows), time_name=self.time.name) + return TimeSeries._from_table( + super().add_rows(rows), + target_name=self._target.name, + time_name=self.time.name, + feature_names=self._feature_names, + ) def filter_rows(self, query: Callable[[Row], bool]) -> TimeSeries: """ @@ -442,9 +503,11 @@ def filter_rows(self, query: Callable[[Row], bool]) -> TimeSeries: result: TimeSeries A time series containing only the rows to match the query. """ - return TimeSeries._from_tagged_table( + return TimeSeries._from_table( super().filter_rows(query), + target_name=self._target.name, time_name=self.time.name, + feature_names=self._feature_names, ) def keep_only_columns(self, column_names: list[str]) -> TimeSeries: @@ -470,22 +533,18 @@ def keep_only_columns(self, column_names: list[str]) -> TimeSeries: IllegalSchemaModificationError If none of the given columns is the target or time column or any of the feature columns. """ - if self.target.name not in column_names: + if self._target.name not in column_names: raise IllegalSchemaModificationError("Must keep the target column.") - if len(set(self.features.column_names).intersection(set(column_names))) == 0: - raise IllegalSchemaModificationError("Must keep at least one feature column.") if self.time.name not in column_names: raise IllegalSchemaModificationError("Must keep the time column.") - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().keep_only_columns(column_names), - target_name=self.target.name, - feature_names=sorted( - set(self.features.column_names).intersection(set(column_names)), - key={val: ix for ix, val in enumerate(self.features.column_names)}.__getitem__, - ), - ), + return TimeSeries._from_table( + super().keep_only_columns(column_names), + target_name=self._target.name, time_name=self.time.name, + feature_names=sorted( + set(self._feature_names).intersection(set(column_names)), + key={val: ix for ix, val in enumerate(self._feature_names)}.__getitem__, + ), ) def remove_columns(self, column_names: list[str]) -> TimeSeries: @@ -515,22 +574,18 @@ def remove_columns(self, column_names: list[str]) -> TimeSeries: IllegalSchemaModificationError If the given columns contain all the feature columns. """ - if self.target.name in column_names: - raise ColumnIsTargetError(self.target.name) - if len(set(self.features.column_names) - set(column_names)) == 0: - raise IllegalSchemaModificationError("You cannot remove every feature column.") + if self._target.name in column_names: + raise ColumnIsTargetError(self._target.name) if self.time.name in column_names: raise ColumnIsTimeError(self.time.name) - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().remove_columns(column_names), - target_name=self.target.name, - feature_names=sorted( - set(self.features.column_names) - set(column_names), - key={val: ix for ix, val in enumerate(self.features.column_names)}.__getitem__, - ), - ), + return TimeSeries._from_table( + super().remove_columns(column_names), + target_name=self._target.name, time_name=self.time.name, + feature_names=sorted( + set(self._feature_names) - set(column_names), + key={val: ix for ix, val in enumerate(self._feature_names)}.__getitem__, + ), ) def remove_columns_with_missing_values(self) -> TimeSeries: @@ -554,18 +609,18 @@ def remove_columns_with_missing_values(self) -> TimeSeries: If the columns to remove contain all the feature columns. """ table = super().remove_columns_with_missing_values() + if self._target.name not in table.column_names: + raise ColumnIsTargetError(self._target.name) if self.time.name not in table.column_names: raise ColumnIsTimeError(self.time.name) - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - table, - self.target.name, - feature_names=sorted( - set(self.features.column_names).intersection(set(table.column_names)), - key={val: ix for ix, val in enumerate(self.features.column_names)}.__getitem__, - ), + return TimeSeries._from_table( + table, + target_name=self._target.name, + time_name=self._time.name, + feature_names=sorted( + set(self._feature_names).intersection(set(table.column_names)), + key={val: ix for ix, val in enumerate(self._feature_names)}.__getitem__, ), - time_name=self.time.name, ) def remove_columns_with_non_numerical_values(self) -> TimeSeries: @@ -589,18 +644,18 @@ def remove_columns_with_non_numerical_values(self) -> TimeSeries: If the columns to remove contain all the feature columns. """ table = super().remove_columns_with_non_numerical_values() + if self._target.name not in table.column_names: + raise ColumnIsTargetError(self._target.name) if self.time.name not in table.column_names: raise ColumnIsTimeError(self.time.name) - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - table, - self.target.name, - feature_names=sorted( - set(self.features.column_names).intersection(set(table.column_names)), - key={val: ix for ix, val in enumerate(self.features.column_names)}.__getitem__, - ), - ), + return TimeSeries._from_table( + table, + self._target.name, time_name=self.time.name, + feature_names=sorted( + set(self._feature_names).intersection(set(table.column_names)), + key={val: ix for ix, val in enumerate(self._feature_names)}.__getitem__, + ), ) def remove_duplicate_rows(self) -> TimeSeries: @@ -614,12 +669,10 @@ def remove_duplicate_rows(self) -> TimeSeries: result : TimeSeries The time series with the duplicate rows removed. """ - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().remove_duplicate_rows(), - target_name=self.target.name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().remove_duplicate_rows(), + target_name=self._target.name, + feature_names=self._feature_names, time_name=self.time.name, ) @@ -634,13 +687,11 @@ def remove_rows_with_missing_values(self) -> TimeSeries: table : TimeSeries A time series without the rows that contain missing values. """ - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().remove_rows_with_missing_values(), - target_name=self.target.name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().remove_rows_with_missing_values(), time_name=self.time.name, + target_name=self._target.name, + feature_names=self._feature_names, ) def remove_rows_with_outliers(self) -> TimeSeries: @@ -658,13 +709,11 @@ def remove_rows_with_outliers(self) -> TimeSeries: new_time_series : TimeSeries A new time series without rows containing outliers. """ - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().remove_rows_with_outliers(), - target_name=self.target.name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().remove_rows_with_outliers(), time_name=self.time.name, + target_name=self._target.name, + feature_names=self._feature_names, ) def rename_column(self, old_name: str, new_name: str) -> TimeSeries: @@ -692,20 +741,15 @@ def rename_column(self, old_name: str, new_name: str) -> TimeSeries: DuplicateColumnNameError If the specified new target column name already exists. """ - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().rename_column(old_name, new_name), - target_name=new_name if self.target.name == old_name else self.target.name, - feature_names=( - self.features.column_names - if old_name not in self.features.column_names - else [ - column_name if column_name != old_name else new_name - for column_name in self.features.column_names - ] - ), - ), + return TimeSeries._from_table( + super().rename_column(old_name, new_name), time_name=new_name if self.time.name == old_name else self.time.name, + target_name=new_name if self._target.name == old_name else self._target.name, + feature_names=( + self._feature_names + if old_name not in self._feature_names + else [column_name if column_name != old_name else new_name for column_name in self._feature_names] + ), ) def replace_column(self, old_column_name: str, new_columns: list[Column]) -> TimeSeries: @@ -747,42 +791,37 @@ def replace_column(self, old_column_name: str, new_columns: list[Column]) -> Tim f'Time column "{self.time.name}" can only be replaced by exactly one new column.', ) else: - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().replace_column(old_column_name, new_columns), - target_name=self.target.name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().replace_column(old_column_name, new_columns), + target_name=self._target.name, + feature_names=self._feature_names, time_name=new_columns[0].name, ) - if old_column_name == self.target.name: + if old_column_name == self._target.name: if len(new_columns) != 1: raise IllegalSchemaModificationError( - f'Target column "{self.target.name}" can only be replaced by exactly one new column.', + f'Target column "{self._target.name}" can only be replaced by exactly one new column.', ) else: - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().replace_column(old_column_name, new_columns), - target_name=new_columns[0].name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().replace_column(old_column_name, new_columns), + target_name=new_columns[0].name, time_name=self.time.name, + feature_names=self._feature_names, ) + else: - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().replace_column(old_column_name, new_columns), - target_name=self.target.name, - feature_names=( - self.features.column_names - if old_column_name not in self.features.column_names - else self.features.column_names[: self.features.column_names.index(old_column_name)] - + [col.name for col in new_columns] - + self.features.column_names[self.features.column_names.index(old_column_name) + 1 :] - ), - ), + return TimeSeries._from_table( + super().replace_column(old_column_name, new_columns), + target_name=self._target.name, time_name=self.time.name, + feature_names=( + self._feature_names + if old_column_name not in self._feature_names + else self._feature_names[: self._feature_names.index(old_column_name)] + + [col.name for col in new_columns] + + self._feature_names[self._feature_names.index(old_column_name) + 1 :] + ), ) def slice_rows( @@ -815,12 +854,10 @@ def slice_rows( IndexOutOfBoundsError If the index is out of bounds. """ - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().slice_rows(start, end, step), - target_name=self.target.name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().slice_rows(start, end, step), + target_name=self._target.name, + feature_names=self._feature_names, time_name=self.time.name, ) @@ -854,16 +891,14 @@ def sort_columns( A new time series with sorted columns. """ sorted_table = super().sort_columns(comparator) - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - sorted_table, - target_name=self.target.name, - feature_names=sorted( - set(sorted_table.column_names).intersection(self.features.column_names), - key={val: ix for ix, val in enumerate(sorted_table.column_names)}.__getitem__, - ), - ), + return TimeSeries._from_table( + sorted_table, time_name=self.time.name, + target_name=self._target.name, + feature_names=sorted( + set(sorted_table.column_names).intersection(self._feature_names), + key={val: ix for ix, val in enumerate(sorted_table.column_names)}.__getitem__, + ), ) def transform_column(self, name: str, transformer: Callable[[Row], Any]) -> TimeSeries: @@ -889,13 +924,11 @@ def transform_column(self, name: str, transformer: Callable[[Row], Any]) -> Time UnknownColumnNameError If the column does not exist. """ - return TimeSeries._from_tagged_table( - TaggedTable._from_table( - super().transform_column(name, transformer), - target_name=self.target.name, - feature_names=self.features.column_names, - ), + return TimeSeries._from_table( + super().transform_column(name, transformer), time_name=self.time.name, + target_name=self._target.name, + feature_names=self._feature_names, ) def plot_lagplot(self, lag: int) -> Image: @@ -924,9 +957,9 @@ def plot_lagplot(self, lag: int) -> Image: >>> image = table.plot_lagplot(lag = 1) """ - if not self.target.type.is_numeric(): + if not self._target.type.is_numeric(): raise NonNumericColumnError("This time series target contains non-numerical columns.") - ax = pd.plotting.lag_plot(self.target._data, lag=lag) + ax = pd.plotting.lag_plot(self._target._data, lag=lag) fig = ax.figure buffer = io.BytesIO() fig.savefig(buffer, format="png") @@ -974,7 +1007,7 @@ def plot_lineplot(self, x_column_name: str | None = None, y_column_name: str | N raise NonNumericColumnError("The time series plotted column contains non-numerical columns.") if y_column_name is None: - y_column_name = self.target.name + y_column_name = self._target.name elif y_column_name not in self._data.columns: raise UnknownColumnNameError([y_column_name]) @@ -1050,7 +1083,7 @@ def plot_scatterplot( raise NonNumericColumnError("The time series plotted column contains non-numerical columns.") if y_column_name is None: - y_column_name = self.target.name + y_column_name = self._target.name elif y_column_name not in self._data.columns: raise UnknownColumnNameError([y_column_name]) if x_column_name is None: diff --git a/tests/helpers/_assertions.py b/tests/helpers/_assertions.py index 308db5b4f..e71d1cdaf 100644 --- a/tests/helpers/_assertions.py +++ b/tests/helpers/_assertions.py @@ -53,7 +53,8 @@ def assert_that_time_series_are_equal(table1: TimeSeries, table2: TimeSeries) -> The timeseries to compare the first timeseries to. """ assert table1.schema == table2.schema - assert table1.features == table2.features - assert table1.target == table2.target + assert table1._feature_names == table2._feature_names + assert table1.features == table2._features + assert table1.target == table2._target assert table1.time == table2.time assert table1 == table2 diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_eq.py b/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_eq.py deleted file mode 100644 index e7104ba38..000000000 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_eq.py +++ /dev/null @@ -1,61 +0,0 @@ -from typing import Any - -import pytest -from safeds.data.tabular.containers import Row, Table, TimeSeries, TimeSeries, TaggedTable - - -@pytest.mark.parametrize( - ("table1", "table2", "expected"), - [ - (TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), True), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), True), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "c", "d", ["a"]), False), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "e": [10, 11, 12]}, "b", "c", ["a"]), False), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TimeSeries({"a": [1, 1, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), False), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TimeSeries({"a": ["1", "2", "3"], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), False), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["c"]), False), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), False), - ], - ids=[ - "rowless table", - "equal tables", - "different target", - "different column names", - "different values", - "different types", - "different features", - "different time", - ], -) -def test_should_return_whether_two_tagged_tables_are_equal(table1: TimeSeries, table2: TimeSeries, expected: bool) -> None: - assert (table1.__eq__(table2)) == expected - - -@pytest.mark.parametrize( - "table1", - [TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"])], - ids=[ - "any", - ], -) -def test_should_return_true_if_objects_are_identical(table1: TimeSeries) -> None: - assert (table1.__eq__(table1)) is True - - -@pytest.mark.parametrize( - ("table", "other"), - [ - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), None), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), Row()), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), Table()), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TaggedTable({"a": [1, 2, 3], "b": [4, 5, 6]}, "b", ["a"])) - ], - ids=[ - "TimeSeries vs. None", - "TimeSeries vs. Row", - "TimeSeries vs. Table", - "TimeSeries vs. TaggedTable", - ], -) -def test_should_return_not_implemented_if_other_is_not_tagged_table(table: TimeSeries, other: Any) -> None: - assert (table.__eq__(other)) is NotImplemented diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_hash.py b/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_hash.py deleted file mode 100644 index 59fcdd0fa..000000000 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_hash.py +++ /dev/null @@ -1,42 +0,0 @@ -from typing import Any - -import pytest -from safeds.data.tabular.containers import Row, Table, TimeSeries, TimeSeries, TaggedTable - - -@pytest.mark.parametrize( - ("table1", "table2"), - [ - (TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"])), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"])), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TimeSeries({"a": [1, 1, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"])), - ], - ids=[ - "rowless table", - "equal tables", - "different values", - ], -) -def test_should_return_same_hash_for_equal_time_series(table1: TimeSeries, table2: TimeSeries) -> None: - assert hash(table1) == hash(table2) - - -@pytest.mark.parametrize( - ("table1", "table2"), - [ - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "c", "d", ["a"])), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "e": [10, 11, 12]}, "b", "c", ["a"])), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), TimeSeries({"a": ["1", "2", "3"], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"])), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["c"])), - (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"])), - ], - ids=[ - "different target", - "different column names", - "different types", - "different features", - "different time", - ], -) -def test_should_return_different_hash_for_unequal_time_series(table1: TimeSeries, table2: TimeSeries) -> None: - assert hash(table1) != hash(table2) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py b/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py deleted file mode 100644 index d431dd6f7..000000000 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py +++ /dev/null @@ -1,39 +0,0 @@ -import sys - -import pytest -from safeds.data.tabular.containers import TimeSeries - - -@pytest.mark.parametrize( - "time_series", - [ - - TimeSeries( - { - "time": [0, 1, 2], - "feature_1": [3, 9, 6], - "feature_2": [6, 12, 9], - "target": [1, 3, 2], - }, - "target", - "time", - ["feature_1", "feature_2"], - ), - - TimeSeries( - { - "time": [0, 1, 2], - "feature_1": [3, 9, 6], - "feature_2": [6, 12, 9], - "other": [3, 9, 12], - "target": [1, 3, 2], - }, - "target", - "time", - ["feature_1", "feature_2"], - ), - ], - ids=["normal", "table_with_column_as_non_feature"], -) -def test_should_size_be_greater_than_normal_object(time_series: TimeSeries) -> None: - assert sys.getsizeof(time_series) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__init__.py b/tests/safeds/data/tabular/containers/_time_series/__init__.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__init__.py rename to tests/safeds/data/tabular/containers/_time_series/__init__.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lag/test_should_return_table.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lag/test_should_return_table.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lag/test_should_return_table.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lag/test_should_return_table.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_x.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_x.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_x.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_x.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_y.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_y.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_y.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_plot_feature_y.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table_both.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table_both.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table_both.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_lineplot/test_should_return_table_both.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_both_set.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_both_set.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_both_set.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_both_set.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_x.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_x.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_x.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_x.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_y_optional.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_y_optional.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_y_optional.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_plot_feature_only_y_optional.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_return_table.png b/tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_return_table.png similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/__snapshots__/test_plot_scatterplot/test_should_return_table.png rename to tests/safeds/data/tabular/containers/_time_series/__snapshots__/test_plot_scatterplot/test_should_return_table.png diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_column.py b/tests/safeds/data/tabular/containers/_time_series/test_add_column.py similarity index 96% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_column.py rename to tests/safeds/data/tabular/containers/_time_series/test_add_column.py index 431fc1283..8cb4eb7ac 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_column.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_add_column.py @@ -28,7 +28,7 @@ }, target_name="target", time_name="time", - feature_names=["feature_1"], + feature_names=None, ), ), ], diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_column_as_feature.py b/tests/safeds/data/tabular/containers/_time_series/test_add_column_as_feature.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_column_as_feature.py rename to tests/safeds/data/tabular/containers/_time_series/test_add_column_as_feature.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_columns.py b/tests/safeds/data/tabular/containers/_time_series/test_add_columns.py similarity index 97% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_columns.py rename to tests/safeds/data/tabular/containers/_time_series/test_add_columns.py index ca86cc122..3433e4d28 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_columns.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_add_columns.py @@ -29,7 +29,7 @@ }, "target", "time", - ["feature_1"], + None, ), ), ], diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_columns_as_features.py b/tests/safeds/data/tabular/containers/_time_series/test_add_columns_as_features.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_columns_as_features.py rename to tests/safeds/data/tabular/containers/_time_series/test_add_columns_as_features.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_row.py b/tests/safeds/data/tabular/containers/_time_series/test_add_row.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_row.py rename to tests/safeds/data/tabular/containers/_time_series/test_add_row.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_rows.py b/tests/safeds/data/tabular/containers/_time_series/test_add_rows.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_add_rows.py rename to tests/safeds/data/tabular/containers/_time_series/test_add_rows.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_as_table.py b/tests/safeds/data/tabular/containers/_time_series/test_as_table.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_as_table.py rename to tests/safeds/data/tabular/containers/_time_series/test_as_table.py diff --git a/tests/safeds/data/tabular/containers/_time_series/test_eq.py b/tests/safeds/data/tabular/containers/_time_series/test_eq.py new file mode 100644 index 000000000..3c45ee150 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_time_series/test_eq.py @@ -0,0 +1,100 @@ +from typing import Any + +import pytest +from safeds.data.tabular.containers import Row, Table, TaggedTable, TimeSeries + + +@pytest.mark.parametrize( + ("table1", "table2", "expected"), + [ + ( + TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), + TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), + True, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + True, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "c", "d", ["a"]), + False, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "e": [10, 11, 12]}, "b", "c", ["a"]), + False, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TimeSeries({"a": [1, 1, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + False, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TimeSeries({"a": ["1", "2", "3"], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + False, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["c"]), + False, + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), + False, + ), + ], + ids=[ + "rowless table", + "equal tables", + "different target", + "different column names", + "different values", + "different types", + "different features", + "different time", + ], +) +def test_should_return_whether_two_tagged_tables_are_equal( + table1: TimeSeries, + table2: TimeSeries, + expected: bool, +) -> None: + assert (table1.__eq__(table2)) == expected + + +@pytest.mark.parametrize( + "table1", + [TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"])], + ids=[ + "any", + ], +) +def test_should_return_true_if_objects_are_identical(table1: TimeSeries) -> None: + assert (table1.__eq__(table1)) is True + + +@pytest.mark.parametrize( + ("table", "other"), + [ + (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), None), + (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), Row()), + (TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), Table()), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TaggedTable({"a": [1, 2, 3], "b": [4, 5, 6]}, "b", ["a"]), + ), + ], + ids=[ + "TimeSeries vs. None", + "TimeSeries vs. Row", + "TimeSeries vs. Table", + "TimeSeries vs. TaggedTable", + ], +) +def test_should_return_not_implemented_if_other_is_not_tagged_table(table: TimeSeries, other: Any) -> None: + assert (table.__eq__(other)) is NotImplemented diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_features.py b/tests/safeds/data/tabular/containers/_time_series/test_features.py similarity index 96% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_features.py rename to tests/safeds/data/tabular/containers/_time_series/test_features.py index aa9631fc5..5b75cb317 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_features.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_features.py @@ -16,6 +16,7 @@ }, target_name="T", time_name="time", + feature_names=["A", "B", "C"], ), Table({"A": [1, 4], "B": [2, 5], "C": [3, 6]}), ), diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_filter_rows.py b/tests/safeds/data/tabular/containers/_time_series/test_filter_rows.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_filter_rows.py rename to tests/safeds/data/tabular/containers/_time_series/test_filter_rows.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_from_table_to_time_series.py b/tests/safeds/data/tabular/containers/_time_series/test_from_table_to_time_series.py similarity index 83% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_from_table_to_time_series.py rename to tests/safeds/data/tabular/containers/_time_series/test_from_table_to_time_series.py index 73e9f602b..559cf0b0f 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_from_table_to_time_series.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_from_table_to_time_series.py @@ -52,36 +52,7 @@ "time", ["A", "B", "C"], ValueError, - r"Column 'A' cannot be both feature and target.", - ), - ( - Table( - { - "time": [0, 1], - "A": [1, 4], - "B": [2, 5], - "C": [3, 6], - "T": [0, 1], - }, - ), - "A", - "time", - [], - ValueError, - r"At least one feature column must be specified.", - ), - ( - Table( - { - "time": [0, 1], - "A": [1, 4], - }, - ), - "A", - "time", - None, - ValueError, - r"At least one feature column must be specified.", + r"Column 'A' can not be target and feature column.", ), ( Table( @@ -120,8 +91,6 @@ "feature_does_not_exist", "target_does_not_exist", "target_and_feature_overlap", - "features_are_empty-explicitly", - "features_are_empty_implicitly", "time_does_not_exist", "time_is_also_feature", ], @@ -135,7 +104,7 @@ def test_should_raise_error( error_msg: str, ) -> None: with pytest.raises(error, match=error_msg): - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( table, target_name=target_name, time_name=time_name, @@ -186,7 +155,7 @@ def test_should_raise_error( ), "T", "time", - None, + ["B"], ), ], ids=["create_tagged_table", "tagged_table_not_all_columns_are_features", "tagged_table_with_feature_names_as_None"], @@ -197,7 +166,7 @@ def test_should_create_a_tagged_table( time_name: str, feature_names: list[str] | None, ) -> None: - time_series = TimeSeries._from_table_to_time_series( + time_series = TimeSeries._from_table( table, target_name=target_name, time_name=time_name, diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_from_tagged_table.py b/tests/safeds/data/tabular/containers/_time_series/test_from_tagged_table.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_from_tagged_table.py rename to tests/safeds/data/tabular/containers/_time_series/test_from_tagged_table.py diff --git a/tests/safeds/data/tabular/containers/_time_series/test_hash.py b/tests/safeds/data/tabular/containers/_time_series/test_hash.py new file mode 100644 index 000000000..94015139b --- /dev/null +++ b/tests/safeds/data/tabular/containers/_time_series/test_hash.py @@ -0,0 +1,64 @@ +import pytest +from safeds.data.tabular.containers import TimeSeries + + +@pytest.mark.parametrize( + ("table1", "table2"), + [ + ( + TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), + TimeSeries({"a": [], "b": [], "c": []}, "b", "c", ["a"]), + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TimeSeries({"a": [1, 1, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + ), + ], + ids=[ + "rowless table", + "equal tables", + "different values", + ], +) +def test_should_return_same_hash_for_equal_time_series(table1: TimeSeries, table2: TimeSeries) -> None: + assert hash(table1) == hash(table2) + + +@pytest.mark.parametrize( + ("table1", "table2"), + [ + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "c", "d", ["a"]), + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "e": [10, 11, 12]}, "b", "c", ["a"]), + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + TimeSeries({"a": ["1", "2", "3"], "b": [4, 5, 6], "c": [7, 8, 9]}, "b", "c", ["a"]), + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["c"]), + ), + ( + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "d", ["a"]), + TimeSeries({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}, "b", "c", ["a"]), + ), + ], + ids=[ + "different target", + "different column names", + "different types", + "different features", + "different time", + ], +) +def test_should_return_different_hash_for_unequal_time_series(table1: TimeSeries, table2: TimeSeries) -> None: + assert hash(table1) != hash(table2) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_init.py b/tests/safeds/data/tabular/containers/_time_series/test_init.py similarity index 81% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_init.py rename to tests/safeds/data/tabular/containers/_time_series/test_init.py index 8c7619fcc..aad1be76e 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_init.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_init.py @@ -46,32 +46,7 @@ "A", ["A", "B", "C"], ValueError, - r"Column 'A' cannot be both feature and target.", - ), - ( - { - "time": [0, 1], - "A": [1, 4], - "B": [2, 5], - "C": [3, 6], - "T": [0, 1], - }, - "time", - "D", - [], - ValueError, - r"At least one feature column must be specified.", - ), - ( - { - "time": [0, 1], - "A": [1, 4], - }, - "time", - "A", - None, - ValueError, - r"At least one feature column must be specified.", + r"Column 'A' can not be time and feature column.", ), ( { @@ -106,8 +81,6 @@ "feature_does_not_exist", "target_does_not_exist", "target_and_feature_overlap", - "features_are_empty-explicitly", - "features_are_empty_implicitly", "time_column_does_not_exist", "time_is_also_feature", ], @@ -174,11 +147,10 @@ def test_should_create_a_time_series( ) -> None: time_series = TimeSeries(data, target_name=target_name, time_name=time_name, feature_names=feature_names) if feature_names is None: - feature_names = list(data.keys()) - feature_names.remove(target_name) - feature_names.remove(time_name) + feature_names = [] + assert isinstance(time_series, TimeSeries) - assert time_series._features.column_names == feature_names + assert time_series._feature_names == feature_names assert time_series._target.name == target_name assert time_series._features == Table(data).keep_only_columns(feature_names) assert time_series._target == Table(data).get_column(target_name) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_keep_only_columns.py b/tests/safeds/data/tabular/containers/_time_series/test_keep_only_columns.py similarity index 79% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_keep_only_columns.py rename to tests/safeds/data/tabular/containers/_time_series/test_keep_only_columns.py index 190c387d1..c6a7ac051 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_keep_only_columns.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_keep_only_columns.py @@ -9,7 +9,7 @@ ("table", "column_names", "expected"), [ ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -22,7 +22,7 @@ "time", ), ["feat1", "target", "time"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -35,7 +35,7 @@ ), ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -49,7 +49,7 @@ "time", ), ["feat1", "other", "target", "time"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -63,7 +63,7 @@ ), ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -77,7 +77,7 @@ "time", ), ["feat1", "target", "time"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -101,7 +101,7 @@ def test_should_return_table(table: TimeSeries, column_names: list[str], expecte ("table", "column_names", "error_msg"), [ ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -119,25 +119,7 @@ def test_should_return_table(table: TimeSeries, column_names: list[str], expecte r"Illegal schema modification: Must keep the target column.", ), ( - TimeSeries._from_table_to_time_series( - Table( - { - "time": [0, 1, 2], - "feat1": [1, 2, 3], - "feat2": [4, 5, 6], - "other": [3, 5, 7], - "target": [7, 8, 9], - }, - ), - "target", - "time", - ["feat1", "feat2"], - ), - ["target", "other"], - r"Illegal schema modification: Must keep at least one feature column.", - ), - ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -155,7 +137,7 @@ def test_should_return_table(table: TimeSeries, column_names: list[str], expecte r"Illegal schema modification: Must keep the time column.", ), ], - ids=["table_remove_target", "table_remove_all_features", "table_remove_time"], + ids=["table_remove_target", "table_remove_time"], ) def test_should_raise_illegal_schema_modification(table: TimeSeries, column_names: list[str], error_msg: str) -> None: with pytest.raises( diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_plot_lag.py b/tests/safeds/data/tabular/containers/_time_series/test_plot_lag.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_plot_lag.py rename to tests/safeds/data/tabular/containers/_time_series/test_plot_lag.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_plot_lineplot.py b/tests/safeds/data/tabular/containers/_time_series/test_plot_lineplot.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_plot_lineplot.py rename to tests/safeds/data/tabular/containers/_time_series/test_plot_lineplot.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_plot_scatterplot.py b/tests/safeds/data/tabular/containers/_time_series/test_plot_scatterplot.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_plot_scatterplot.py rename to tests/safeds/data/tabular/containers/_time_series/test_plot_scatterplot.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns.py b/tests/safeds/data/tabular/containers/_time_series/test_remove_columns.py similarity index 78% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns.py rename to tests/safeds/data/tabular/containers/_time_series/test_remove_columns.py index ccadbd877..5a51e70e1 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_remove_columns.py @@ -1,6 +1,6 @@ import pytest from safeds.data.tabular.containers import Table, TimeSeries -from safeds.exceptions import ColumnIsTargetError, ColumnIsTimeError, IllegalSchemaModificationError +from safeds.exceptions import ColumnIsTargetError, ColumnIsTimeError from tests.helpers import assert_that_time_series_are_equal @@ -9,7 +9,7 @@ ("table", "columns", "expected"), [ ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -25,7 +25,7 @@ ["feat_1", "feat_2"], ), ["feat_2"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({ "time": [0, 1, 2], "feat_1": [1, 2, 3], @@ -39,7 +39,7 @@ ), ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -55,7 +55,7 @@ ["feat_1", "feat_2"], ), ["non_feat_2"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({ "time": [0, 1, 2], "feat_1": [1, 2, 3], @@ -69,7 +69,7 @@ ), ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -85,7 +85,7 @@ ["feat_1", "feat_2"], ), ["non_feat_1", "non_feat_2"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({"time": [0, 1, 2], "feat_1": [1, 2, 3], "feat_2": [4, 5, 6], "target": [7, 8, 9]}), "target", "time", @@ -93,7 +93,7 @@ ), ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -109,7 +109,7 @@ ["feat_1", "feat_2"], ), ["feat_2", "non_feat_2"], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({"time": [0, 1, 2], "feat_1": [1, 2, 3], "non_feat_1": [2, 4, 6], "target": [7, 8, 9]}), "target", "time", @@ -117,7 +117,7 @@ ), ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table( { "time": [0, 1, 2], @@ -131,7 +131,7 @@ ["feat_1"], ), [], - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({"time": [0, 1, 2], "feat_1": [1, 2, 3], "non_feat_1": [2, 4, 6], "target": [7, 8, 9]}), "target", "time", @@ -156,7 +156,7 @@ def test_should_remove_columns(table: TimeSeries, columns: list[str], expected: ("table", "columns", "error", "error_msg"), [ ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({"time": [0, 1, 2], "feat": [1, 2, 3], "non_feat": [1, 2, 3], "target": [4, 5, 6]}), "target", "time", @@ -167,7 +167,7 @@ def test_should_remove_columns(table: TimeSeries, columns: list[str], expected: r'Illegal schema modification: Column "target" is the target column and cannot be removed.', ), ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({"time": [0, 1, 2], "feat": [1, 2, 3], "non_feat": [1, 2, 3], "target": [4, 5, 6]}), "target", "time", @@ -178,29 +178,7 @@ def test_should_remove_columns(table: TimeSeries, columns: list[str], expected: r'Illegal schema modification: Column "target" is the target column and cannot be removed.', ), ( - TimeSeries._from_table_to_time_series( - Table({"time": [0, 1, 2], "feat": [1, 2, 3], "non_feat": [1, 2, 3], "target": [4, 5, 6]}), - "target", - "time", - ["feat"], - ), - ["feat"], - IllegalSchemaModificationError, - r"Illegal schema modification: You cannot remove every feature column.", - ), - ( - TimeSeries._from_table_to_time_series( - Table({"time": [0, 1, 2], "feat": [1, 2, 3], "non_feat": [1, 2, 3], "target": [4, 5, 6]}), - "target", - "time", - ["feat"], - ), - ["feat", "non_feat"], - IllegalSchemaModificationError, - r"Illegal schema modification: You cannot remove every feature column.", - ), - ( - TimeSeries._from_table_to_time_series( + TimeSeries._from_table( Table({"time": [0, 1, 2], "feat": [1, 2, 3], "non_feat": [1, 2, 3], "target": [4, 5, 6]}), "target", "time", @@ -214,8 +192,6 @@ def test_should_remove_columns(table: TimeSeries, columns: list[str], expected: ids=[ "remove_only_target", "remove_non_feat_and_target", - "remove_all_features", - "remove_non_feat_and_all_features", "remove_time_column", ], ) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns_with_missing_values.py b/tests/safeds/data/tabular/containers/_time_series/test_remove_columns_with_missing_values.py similarity index 84% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns_with_missing_values.py rename to tests/safeds/data/tabular/containers/_time_series/test_remove_columns_with_missing_values.py index 01958f2dc..319e27c5f 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns_with_missing_values.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_remove_columns_with_missing_values.py @@ -1,6 +1,6 @@ import pytest from safeds.data.tabular.containers import TimeSeries -from safeds.exceptions import ColumnIsTargetError, ColumnIsTimeError, IllegalSchemaModificationError +from safeds.exceptions import ColumnIsTargetError, ColumnIsTimeError from tests.helpers import assert_that_time_series_are_equal @@ -168,36 +168,6 @@ def test_should_remove_columns_with_non_numerical_values(table: TimeSeries, expe ColumnIsTargetError, 'Illegal schema modification: Column "target" is the target column and cannot be removed.', ), - ( - TimeSeries( - { - "time": [0, 1, 2], - "feature": [0, None, 2], - "non_feature": [1, 2, 3], - "target": [3, 2, 5], - }, - "target", - "time", - ["feature"], - ), - IllegalSchemaModificationError, - "Illegal schema modification: You cannot remove every feature column.", - ), - ( - TimeSeries( - { - "time": [0, 1, 2], - "feature": [0, None, 2], - "non_feature": [1, None, 3], - "target": [3, 2, 5], - }, - "target", - "time", - ["feature"], - ), - IllegalSchemaModificationError, - "Illegal schema modification: You cannot remove every feature column.", - ), ], ids=[ "only_target_incomplete", @@ -205,8 +175,6 @@ def test_should_remove_columns_with_non_numerical_values(table: TimeSeries, expe "time_is_incomplete", "also_non_feature_incomplete", "all_incomplete", - "all_features_incomplete", - "all_features_and_non_feature_incomplete", ], ) def test_should_raise_in_remove_columns_with_missing_values( diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns_with_non_numerical_values.py b/tests/safeds/data/tabular/containers/_time_series/test_remove_columns_with_non_numerical_values.py similarity index 84% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns_with_non_numerical_values.py rename to tests/safeds/data/tabular/containers/_time_series/test_remove_columns_with_non_numerical_values.py index d735edb51..03d6e8572 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_columns_with_non_numerical_values.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_remove_columns_with_non_numerical_values.py @@ -1,6 +1,6 @@ import pytest from safeds.data.tabular.containers import TimeSeries -from safeds.exceptions import ColumnIsTargetError, ColumnIsTimeError, IllegalSchemaModificationError +from safeds.exceptions import ColumnIsTargetError, ColumnIsTimeError from tests.helpers import assert_that_time_series_are_equal @@ -168,36 +168,6 @@ def test_should_remove_columns_with_non_numerical_values(table: TimeSeries, expe ColumnIsTargetError, r'Illegal schema modification: Column "target" is the target column and cannot be removed.', ), - ( - TimeSeries( - { - "time": [0, 1, 2], - "feature": [0, "a", 2], - "non_feature": [1, 2, 3], - "target": [3, 2, 5], - }, - "target", - "time", - ["feature"], - ), - IllegalSchemaModificationError, - r"Illegal schema modification: You cannot remove every feature column.", - ), - ( - TimeSeries( - { - "time": [0, 1, 2], - "feature": [0, "a", 2], - "non_feature": [1, "b", 3], - "target": [3, 2, 5], - }, - "target", - "time", - ["feature"], - ), - IllegalSchemaModificationError, - r"Illegal schema modification: You cannot remove every feature column.", - ), ], ids=[ "only_target_non_numerical", @@ -205,8 +175,6 @@ def test_should_remove_columns_with_non_numerical_values(table: TimeSeries, expe "also_non_feature_non_numerical", "time_non_numerical", "all_non_numerical", - "all_features_non_numerical", - "all_features_and_non_feature_non_numerical", ], ) def test_should_raise_in_remove_columns_with_non_numerical_values( diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_duplicate_rows.py b/tests/safeds/data/tabular/containers/_time_series/test_remove_duplicate_rows.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_duplicate_rows.py rename to tests/safeds/data/tabular/containers/_time_series/test_remove_duplicate_rows.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_rows_with_missing_values.py b/tests/safeds/data/tabular/containers/_time_series/test_remove_rows_with_missing_values.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_rows_with_missing_values.py rename to tests/safeds/data/tabular/containers/_time_series/test_remove_rows_with_missing_values.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_rows_with_outliers.py b/tests/safeds/data/tabular/containers/_time_series/test_remove_rows_with_outliers.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_remove_rows_with_outliers.py rename to tests/safeds/data/tabular/containers/_time_series/test_remove_rows_with_outliers.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_rename_column.py b/tests/safeds/data/tabular/containers/_time_series/test_rename_column.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_rename_column.py rename to tests/safeds/data/tabular/containers/_time_series/test_rename_column.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_replace_column.py b/tests/safeds/data/tabular/containers/_time_series/test_replace_column.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_replace_column.py rename to tests/safeds/data/tabular/containers/_time_series/test_replace_column.py diff --git a/tests/safeds/data/tabular/containers/_time_series/test_sizeof.py b/tests/safeds/data/tabular/containers/_time_series/test_sizeof.py new file mode 100644 index 000000000..1a0ded04b --- /dev/null +++ b/tests/safeds/data/tabular/containers/_time_series/test_sizeof.py @@ -0,0 +1,37 @@ +import sys + +import pytest +from safeds.data.tabular.containers import TimeSeries + + +@pytest.mark.parametrize( + "time_series", + [ + TimeSeries( + { + "time": [0, 1, 2], + "feature_1": [3, 9, 6], + "feature_2": [6, 12, 9], + "target": [1, 3, 2], + }, + "target", + "time", + ["feature_1", "feature_2"], + ), + TimeSeries( + { + "time": [0, 1, 2], + "feature_1": [3, 9, 6], + "feature_2": [6, 12, 9], + "other": [3, 9, 12], + "target": [1, 3, 2], + }, + "target", + "time", + ["feature_1", "feature_2"], + ), + ], + ids=["normal", "table_with_column_as_non_feature"], +) +def test_should_size_be_greater_than_normal_object(time_series: TimeSeries) -> None: + assert sys.getsizeof(time_series) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_slice_rows.py b/tests/safeds/data/tabular/containers/_time_series/test_slice_rows.py similarity index 88% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_slice_rows.py rename to tests/safeds/data/tabular/containers/_time_series/test_slice_rows.py index 1625ba17c..e8788e52d 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_slice_rows.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_slice_rows.py @@ -3,7 +3,7 @@ from safeds.data.tabular.containers import TimeSeries from safeds.exceptions import IndexOutOfBoundsError -from tests.helpers import assert_that_tagged_tables_are_equal +from tests.helpers import assert_that_time_series_are_equal @pytest.mark.parametrize( @@ -36,9 +36,9 @@ def test_should_slice_rows(table: TimeSeries, test_table: TimeSeries, second_tes new_table = table.slice_rows(0, 2, 1) second_new_table = table.slice_rows(0, 3, 2) third_new_table = table.slice_rows() - assert_that_tagged_tables_are_equal(new_table, test_table) - assert_that_tagged_tables_are_equal(second_new_table, second_test_table) - assert_that_tagged_tables_are_equal(third_new_table, table) + assert_that_time_series_are_equal(new_table, test_table) + assert_that_time_series_are_equal(second_new_table, second_test_table) + assert_that_time_series_are_equal(third_new_table, table) @pytest.mark.parametrize( diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sort_columns.py b/tests/safeds/data/tabular/containers/_time_series/test_sort_columns.py similarity index 94% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sort_columns.py rename to tests/safeds/data/tabular/containers/_time_series/test_sort_columns.py index c50bb9f78..679816069 100644 --- a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sort_columns.py +++ b/tests/safeds/data/tabular/containers/_time_series/test_sort_columns.py @@ -57,6 +57,6 @@ def test_should_return_sorted_table( assert table_sorted_columns[2] == columns[col3] assert table_sorted_columns[3] == columns[col4] assert table_sorted_columns[4] == columns[col5] - assert table_sorted.features == table1.features - assert table_sorted.target == table1.target + assert table_sorted._features == table1._features + assert table_sorted._target == table1._target assert table_sorted.time == table1.time diff --git a/tests/safeds/data/tabular/containers/_time_series/test_time.py b/tests/safeds/data/tabular/containers/_time_series/test_time.py new file mode 100644 index 000000000..f1a65de0f --- /dev/null +++ b/tests/safeds/data/tabular/containers/_time_series/test_time.py @@ -0,0 +1,42 @@ +import pytest +from safeds.data.tabular.containers import Column, TimeSeries + + +@pytest.mark.parametrize( + ("time_series", "time"), + [ + ( + TimeSeries( + { + "time": [0, 1], + "A": [1, 4], + "B": [2, 5], + "C": [3, 6], + "T": [0, 1], + }, + target_name="T", + time_name="time", + feature_names=["A", "B", "C"], + ), + Column("time", [0, 1]), + ), + ( + TimeSeries( + { + "time": [1, 2], + "A": [1, 4], + "B": [2, 5], + "C": [3, 6], + "T": [0, 1], + }, + target_name="T", + time_name="time", + feature_names=["A", "C"], + ), + Column("time", [1, 2]), + ), + ], + ids=["only_target_and_features", "target_features_and_other"], +) +def test_should_return_features(time_series: TimeSeries, time: Column) -> None: + assert time_series.time == time diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_time_target.py b/tests/safeds/data/tabular/containers/_time_series/test_time_target.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_time_target.py rename to tests/safeds/data/tabular/containers/_time_series/test_time_target.py diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_transform_column.py b/tests/safeds/data/tabular/containers/_time_series/test_transform_column.py similarity index 100% rename from tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_transform_column.py rename to tests/safeds/data/tabular/containers/_time_series/test_transform_column.py