Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add SequentialTableTransformer #893

Merged
merged 21 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
92d8909
first implementation of SequentialTableTransformer
xXstupidnameXx Jun 21, 2024
73e765d
implemented TransformerNotInvertableError
xXstupidnameXx Jun 28, 2024
3c5619a
all current tests passed.
xXstupidnameXx Jun 28, 2024
d847ab0
improved tests
xXstupidnameXx Jun 28, 2024
4029628
finished tests
xXstupidnameXx Jul 5, 2024
8254b2e
finished SequentialTableTransformer
xXstupidnameXx Jul 5, 2024
a47cdbe
last minute typo fix (changed invertable to invertible)
xXstupidnameXx Jul 5, 2024
ab706f4
fixed documentation
xXstupidnameXx Jul 5, 2024
5b0f9e7
Merge branch 'main' into 802-sequential-table-transformer
xXstupidnameXx Jul 5, 2024
b5d1393
fixed tests
xXstupidnameXx Jul 5, 2024
4dbcab4
Merge remote-tracking branch 'origin/802-sequential-table-transformer…
xXstupidnameXx Jul 5, 2024
d24a14a
fixing linter errors
xXstupidnameXx Jul 5, 2024
445d9ac
fixed mypy errors
xXstupidnameXx Jul 12, 2024
b2ea3c1
style: apply automated linter fixes
megalinter-bot Jul 12, 2024
7097f7d
style: apply automated linter fixes
megalinter-bot Jul 12, 2024
0960374
Merge branch 'main' into 802-sequential-table-transformer
xXstupidnameXx Jul 12, 2024
946ccc6
minor code improvements
xXstupidnameXx Jul 12, 2024
a3e0fe9
Merge remote-tracking branch 'origin/802-sequential-table-transformer…
xXstupidnameXx Jul 12, 2024
5251c57
style: apply automated linter fixes
megalinter-bot Jul 12, 2024
e88083e
removed ValueError from __init__ in SequentialTableTransformer
xXstupidnameXx Jul 12, 2024
4273811
docs: minor changes
lars-reimann Jul 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from typing import TYPE_CHECKING
from warnings import warn

from safeds._utils import _structural_hash
from safeds.exceptions import TransformerNotFittedError, TransformerNotInvertibleError
Expand All @@ -25,20 +26,25 @@
Raises
------
ValueError:
Raises a ValueError if the list of Transformers is None or contains no transformers.
Raises a ValueError if the list of Transformers is None.
"""

def __init__(
self,
transformers: list[TableTransformer],
*,
column_names: str | list[str] | None = None, # noqa: ARG002
) -> None:
super().__init__(None)

if transformers is None:
raise ValueError("transformers can't be None")

Check warning on line 39 in src/safeds/data/tabular/transformation/_sequential_table_transformer.py

View check run for this annotation

Codecov / codecov/patch

src/safeds/data/tabular/transformation/_sequential_table_transformer.py#L39

Added line #L39 was not covered by tests
xXstupidnameXx marked this conversation as resolved.
Show resolved Hide resolved

# Check if transformers actually contains any transformers.
if transformers is None or len(transformers) == 0:
raise ValueError("transformers must contain at least 1 transformer")
warn(
("transformers should contain at least 1 transformer"),
UserWarning,
stacklevel=2,
)

# Parameters
self._transformers: list[TableTransformer] = transformers
Expand Down Expand Up @@ -95,7 +101,6 @@

result: SequentialTableTransformer = SequentialTableTransformer(
transformers=fitted_transformers,
column_names=self._column_names,
)

result._is_fitted = True
Expand Down
8 changes: 4 additions & 4 deletions tests/helpers/_assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def assert_tables_equal(
ignore_column_order: bool = False,
ignore_row_order: bool = False,
ignore_types: bool = False,
check_exact: bool = False,
ignore_float_imprecision: bool = True,
) -> None:
"""
Assert that two tables are almost equal.
Expand All @@ -30,16 +30,16 @@ def assert_tables_equal(
Ignore the column order when True. Will return true, even when the row order is different.
ignore_types:
Ignore differing data Types. Will return true, even when columns have differing data types.
check_exact:
If True, check, if floating point values match EXACTLY.
ignore_float_imprecision:
If False, check if floating point values match EXACTLY.
"""
assert_frame_equal(
table1._data_frame,
table2._data_frame,
check_row_order=not ignore_row_order,
check_column_order=not ignore_column_order,
check_dtypes=not ignore_types,
check_exact=check_exact,
check_exact=not ignore_float_imprecision,
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,27 @@

class TestInit:

def test_should_raise_value_error_on_empty_list(self) -> None:
with pytest.raises(ValueError, match=("transformers must contain at least 1 transformer")):
SequentialTableTransformer(transformers=[]) # type: ignore # noqa: PGH003
def test_should_warn_on_empty_list(self) -> None:
with pytest.warns(UserWarning, match=("transformers should contain at least 1 transformer")):
SequentialTableTransformer(transformers=[]) # type: ignore[attr-defined]


class TestFit:
def test_should_raise_value_error_on_empty_table(self) -> None:
one_hot = OneHotEncoder()
imputer = SimpleImputer(SimpleImputer.Strategy.constant(0))
transformers = [one_hot, imputer]
test_table = Table(
{
"col1": [],
"col2": [],
},
)
sequential_table_transformer = SequentialTableTransformer(transformers)
sequential_table_transformer = SequentialTableTransformer([SimpleImputer(SimpleImputer.Strategy.constant(0))])
with pytest.raises(
ValueError,
match=("The SequentialTableTransformer cannot be fitted because the table contains 0 rows."),
):
sequential_table_transformer.fit(test_table)

def test_fit_does_not_change_original_transformer(self) -> None:
def test_should_not_change_original_transformer(self) -> None:
one_hot = OneHotEncoder()
imputer = SimpleImputer(SimpleImputer.Strategy.constant(0))
transformer_list = [one_hot, imputer]
Expand Down Expand Up @@ -94,7 +91,7 @@ def test_should_do_same_as_transformer_with_single_transformer(self, transformer
test_table_sequential = sequential_transformer.transform(test_table)
assert_tables_equal(test_table_normal, test_table_sequential)

def test_transforms_correctly_with_multiple_transformers(self) -> None:
def test_should_transform_with_multiple_transformers(self) -> None:
one_hot = OneHotEncoder()
imputer = SimpleImputer(SimpleImputer.Strategy.constant(0))
transformers = [one_hot, imputer]
Expand All @@ -106,25 +103,25 @@ def test_transforms_correctly_with_multiple_transformers(self) -> None:
)
sequential_table_transformer = SequentialTableTransformer(transformers)
fitted_sequential_table_transformer = sequential_table_transformer.fit(test_table)
transfromed_table_sequential = fitted_sequential_table_transformer.transform(test_table)
transformed_table_sequential = fitted_sequential_table_transformer.transform(test_table)

one_hot = one_hot.fit(test_table)
transormed_table_individual = one_hot.transform(test_table)
imputer = imputer.fit(transormed_table_individual)
transormed_table_individual = imputer.transform(transormed_table_individual)
transformed_table_individual = one_hot.transform(test_table)
imputer = imputer.fit(transformed_table_individual)
transformed_table_individual = imputer.transform(transformed_table_individual)

assert_tables_equal(transfromed_table_sequential, transormed_table_individual)
assert_tables_equal(transformed_table_sequential, transformed_table_individual)


class TestIsFitted:
def test_should_return_false_before_fiting(self) -> None:
def test_should_return_false_before_fitting(self) -> None:
one_hot = OneHotEncoder()
imputer = SimpleImputer(SimpleImputer.Strategy.constant(0))
transformers = [one_hot, imputer]
sequential_table_transformer = SequentialTableTransformer(transformers)
assert sequential_table_transformer.is_fitted is False

def test_should_return_true_after_fiting(self) -> None:
def test_should_return_true_after_fitting(self) -> None:
one_hot = OneHotEncoder()
imputer = SimpleImputer(SimpleImputer.Strategy.constant(0))
transformers = [one_hot, imputer]
Expand Down