From 49b4401d99dbae467647490c71e5ab50945ad2f6 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 18 May 2024 20:23:03 +0200 Subject: [PATCH 01/49] feat: namespace for string operations on cells --- src/safeds/data/tabular/containers/_cell.py | 19 ++++++-- .../data/tabular/containers/_lazy_cell.py | 12 +++++ .../tabular/containers/_lazy_string_cell.py | 46 +++++++++++++++++++ .../data/tabular/containers/_string_cell.py | 34 ++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 src/safeds/data/tabular/containers/_lazy_string_cell.py create mode 100644 src/safeds/data/tabular/containers/_string_cell.py diff --git a/src/safeds/data/tabular/containers/_cell.py b/src/safeds/data/tabular/containers/_cell.py index b0c54f580..cb8d5a121 100644 --- a/src/safeds/data/tabular/containers/_cell.py +++ b/src/safeds/data/tabular/containers/_cell.py @@ -6,8 +6,10 @@ if TYPE_CHECKING: import polars as pl + from ._string_cell import StringCell + T_co = TypeVar("T_co", covariant=True) -P = TypeVar("P") +P_contra = TypeVar("P_contra", contravariant=True) R_co = TypeVar("R_co", covariant=True) @@ -109,10 +111,10 @@ def __mul__(self, other: Any) -> Cell[R_co]: ... def __rmul__(self, other: Any) -> Cell[R_co]: ... @abstractmethod - def __pow__(self, other: float | Cell[P]) -> Cell[R_co]: ... + def __pow__(self, other: float | Cell[P_contra]) -> Cell[R_co]: ... @abstractmethod - def __rpow__(self, other: float | Cell[P]) -> Cell[R_co]: ... + def __rpow__(self, other: float | Cell[P_contra]) -> Cell[R_co]: ... @abstractmethod def __sub__(self, other: Any) -> Cell[R_co]: ... @@ -134,6 +136,15 @@ def __hash__(self) -> int: ... @abstractmethod def __sizeof__(self) -> int: ... + # ------------------------------------------------------------------------------------------------------------------ + # Properties + # ------------------------------------------------------------------------------------------------------------------ + + @property + @abstractmethod + def string(self) -> StringCell: + """These operations only make sense for string cells.""" + # ------------------------------------------------------------------------------------------------------------------ # Boolean operations # ------------------------------------------------------------------------------------------------------------------ @@ -432,7 +443,7 @@ def mul(self, other: Any) -> Cell[R_co]: """ return self.__mul__(other) - def pow(self, other: float | Cell[P]) -> Cell[R_co]: + def pow(self, other: float | Cell[P_contra]) -> Cell[R_co]: """ Raise to a power. This is equivalent to the `**` operator. diff --git a/src/safeds/data/tabular/containers/_lazy_cell.py b/src/safeds/data/tabular/containers/_lazy_cell.py index 4e66678e5..b985bba46 100644 --- a/src/safeds/data/tabular/containers/_lazy_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_cell.py @@ -9,6 +9,8 @@ if TYPE_CHECKING: import polars as pl + from ._string_cell import StringCell + T = TypeVar("T") P = TypeVar("P") R = TypeVar("R") @@ -166,6 +168,16 @@ def __hash__(self) -> int: def __sizeof__(self) -> int: return self._expression.__sizeof__() + # ------------------------------------------------------------------------------------------------------------------ + # Properties + # ------------------------------------------------------------------------------------------------------------------ + + @property + def string(self) -> StringCell: + from ._lazy_string_cell import _LazyStringCell # circular import + + return _LazyStringCell(self._expression) + # ------------------------------------------------------------------------------------------------------------------ # Internal # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py new file mode 100644 index 000000000..5737a7ec3 --- /dev/null +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from safeds._utils import _structural_hash + +from ._lazy_cell import _LazyCell +from ._string_cell import StringCell + +if TYPE_CHECKING: + import polars as pl + + from ._cell import Cell + + +class _LazyStringCell(StringCell): + # ------------------------------------------------------------------------------------------------------------------ + # Dunder methods + # ------------------------------------------------------------------------------------------------------------------ + + def __init__(self, expression: pl.Expr) -> None: + self._expression: pl.Expr = expression + + def __hash__(self) -> int: + return _structural_hash(self._expression.meta.serialize()) + + def __sizeof__(self) -> int: + return self._expression.__sizeof__() + + # ------------------------------------------------------------------------------------------------------------------ + # String operations + # ------------------------------------------------------------------------------------------------------------------ + + def contains(self, substring: str) -> Cell[bool]: + return _LazyCell(self._expression.str.contains(substring, literal=True)) + + # ------------------------------------------------------------------------------------------------------------------ + # Internal + # ------------------------------------------------------------------------------------------------------------------ + + def _equals(self, other: object) -> bool: + if not isinstance(other, _LazyStringCell): + return NotImplemented + if self is other: + return True + return self._expression.meta.eq(other._expression.meta) diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py new file mode 100644 index 000000000..5d9715183 --- /dev/null +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from safeds.data.tabular.containers import Cell + + +class StringCell(ABC): + """These operations only make sense for string cells.""" + + @abstractmethod + def contains(self, substring: str) -> Cell[bool]: + """ + Check if the string value in the cell contains the substring. + + Parameters + ---------- + substring: + The substring to search for. + + Returns + ------- + result: + Whether the string value contains the substring. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.count_if(lambda cell: cell.string.contains("b")) + 2 + """ From 104903304d3af62c1da766125d397f3577d739ad Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 18 May 2024 20:33:20 +0200 Subject: [PATCH 02/49] feat: `starts_with` and `ends_with` --- .../tabular/containers/_lazy_string_cell.py | 6 ++ .../data/tabular/containers/_string_cell.py | 64 +++++++++++++++++++ tests/helpers/__init__.py | 2 + tests/helpers/_assertions.py | 35 ++++++++-- .../containers/_string_cell/__init__.py | 0 .../containers/_string_cell/test_contains.py | 21 ++++++ .../containers/_string_cell/test_ends_with.py | 21 ++++++ .../_string_cell/test_starts_with.py | 21 ++++++ 8 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/__init__.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_contains.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index 5737a7ec3..ba3893887 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -34,6 +34,12 @@ def __sizeof__(self) -> int: def contains(self, substring: str) -> Cell[bool]: return _LazyCell(self._expression.str.contains(substring, literal=True)) + def starts_with(self, prefix: str) -> Cell[bool]: + return _LazyCell(self._expression.str.starts_with(prefix)) + + def ends_with(self, suffix: str) -> Cell[bool]: + return _LazyCell(self._expression.str.ends_with(suffix)) + # ------------------------------------------------------------------------------------------------------------------ # Internal # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 5d9715183..6f25bbe78 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -32,3 +32,67 @@ def contains(self, substring: str) -> Cell[bool]: >>> column.count_if(lambda cell: cell.string.contains("b")) 2 """ + + @abstractmethod + def ends_with(self, suffix: str) -> Cell[bool]: + """ + Check if the string value in the cell ends with the suffix. + + Parameters + ---------- + suffix: + The suffix to search for. + + Returns + ------- + result: + Whether the string value ends with the suffix. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.count_if(lambda cell: cell.string.ends_with("c")) + 1 + """ + + @abstractmethod + def starts_with(self, prefix: str) -> Cell[bool]: + """ + Check if the string value in the cell starts with the prefix. + + Parameters + ---------- + prefix: + The prefix to search for. + + Returns + ------- + result: + Whether the string value starts with the prefix. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.count_if(lambda cell: cell.string.starts_with("a")) + 1 + """ + + + # indexOf + # lastIndexOf + # length + # replace + # split + # substring + # toFloat + # toInt + # toLowercase + # toUppercase + # trim + # trimEnd + # trimStart + # toDate + # toTime + # toDatetime diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py index 20a9ac339..1215fa494 100644 --- a/tests/helpers/__init__.py +++ b/tests/helpers/__init__.py @@ -1,4 +1,5 @@ from ._assertions import ( + assert_cell_operation_works, assert_tables_equal, assert_that_tabular_datasets_are_equal, ) @@ -36,6 +37,7 @@ from ._resources import resolve_resource_path __all__ = [ + "assert_cell_operation_works", "assert_tables_equal", "assert_that_tabular_datasets_are_equal", "configure_test_with_device", diff --git a/tests/helpers/_assertions.py b/tests/helpers/_assertions.py index 8976fc063..2dc9b7709 100644 --- a/tests/helpers/_assertions.py +++ b/tests/helpers/_assertions.py @@ -1,6 +1,9 @@ +from collections.abc import Callable +from typing import Any + from polars.testing import assert_frame_equal from safeds.data.labeled.containers import TabularDataset -from safeds.data.tabular.containers import Table +from safeds.data.tabular.containers import Cell, Column, Table def assert_tables_equal(table1: Table, table2: Table) -> None: @@ -9,9 +12,9 @@ def assert_tables_equal(table1: Table, table2: Table) -> None: Parameters ---------- - table1: Table + table1: The first table. - table2: Table + table2: The table to compare the first table to. """ assert_frame_equal(table1._data_frame, table2._data_frame) @@ -23,12 +26,34 @@ def assert_that_tabular_datasets_are_equal(table1: TabularDataset, table2: Tabul Parameters ---------- - table1: TabularDataset + table1: The first table. - table2: TabularDataset + table2: The table to compare the first table to. """ assert table1._table.schema == table2._table.schema assert table1.features == table2.features assert table1.target == table2.target assert table1 == table2 + + +def assert_cell_operation_works( + input_data: list[Any], + transformer: Callable[[Cell], Cell], + expected_data: list[Any], +) -> None: + """ + Assert that a cell operation works as expected. + + Parameters + ---------- + input_data: + The input data. + transformer: + The transformer to apply to the cells. + expected_data: + The expected data. + """ + column = Column("A", input_data) + transformed_column = column.transform(transformer) + assert transformed_column == Column("A", expected_data) diff --git a/tests/safeds/data/tabular/containers/_string_cell/__init__.py b/tests/safeds/data/tabular/containers/_string_cell/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_contains.py b/tests/safeds/data/tabular/containers/_string_cell/test_contains.py new file mode 100644 index 000000000..80acf2d07 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_contains.py @@ -0,0 +1,21 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "substring", "expected"), + [ + ("", "a", False), + ("abc", "", True), + ("abc", "a", True), + ("abc", "d", False), + ], + ids=[ + "empty string", + "empty substring", + "contained", + "not contained", + ], +) +def test_should_check_whether_string_contains_substring(string: str, substring: str, expected: bool) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.contains(substring), [expected]) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py b/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py new file mode 100644 index 000000000..1052cffcc --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py @@ -0,0 +1,21 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "suffix", "expected"), + [ + ("", "a", False), + ("abc", "", True), + ("abc", "c", True), + ("abc", "a", False), + ], + ids=[ + "empty string", + "empty suffix", + "ends with", + "does not end with", + ], +) +def test_should_check_whether_string_ends_with_prefix(string: str, suffix: str, expected: bool) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.ends_with(suffix), [expected]) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py b/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py new file mode 100644 index 000000000..5848f626b --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py @@ -0,0 +1,21 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "prefix", "expected"), + [ + ("", "a", False), + ("abc", "", True), + ("abc", "a", True), + ("abc", "c", False), + ], + ids=[ + "empty string", + "empty prefix", + "starts with", + "does not start with", + ], +) +def test_should_check_whether_string_start_with_prefix(string: str, prefix: str, expected: bool) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.starts_with(prefix), [expected]) From 773789c51308a54aca6a0db75475eace5afaffbb Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 18 May 2024 22:19:50 +0200 Subject: [PATCH 03/49] feat: `length` --- .../tabular/containers/_lazy_string_cell.py | 10 ++++++-- .../data/tabular/containers/_string_cell.py | 25 ++++++++++++++++++- .../containers/_string_cell/test_length.py | 25 +++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_length.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index ba3893887..23ab20ae3 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -34,12 +34,18 @@ def __sizeof__(self) -> int: def contains(self, substring: str) -> Cell[bool]: return _LazyCell(self._expression.str.contains(substring, literal=True)) - def starts_with(self, prefix: str) -> Cell[bool]: - return _LazyCell(self._expression.str.starts_with(prefix)) + def length(self, optimize_for_ascii: bool = False) -> Cell[int]: + if optimize_for_ascii: + return _LazyCell(self._expression.str.len_bytes()) + else: + return _LazyCell(self._expression.str.len_chars()) def ends_with(self, suffix: str) -> Cell[bool]: return _LazyCell(self._expression.str.ends_with(suffix)) + def starts_with(self, prefix: str) -> Cell[bool]: + return _LazyCell(self._expression.str.starts_with(prefix)) + # ------------------------------------------------------------------------------------------------------------------ # Internal # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 6f25bbe78..2b5cd8746 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -56,6 +56,30 @@ def ends_with(self, suffix: str) -> Cell[bool]: 1 """ + @abstractmethod + def length(self, *, optimize_for_ascii: bool = False) -> Cell[int]: + """ + Get the number of characters of the string value in the cell. + + Parameters + ---------- + optimize_for_ascii: + Greatly speed up this operation if the string is ASCII-only. If the string contains non-ASCII characters, + this option will return incorrect results, though. + + Returns + ------- + length: + The length of the string value. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["", "a", "abc"]) + >>> column.transform(lambda cell: cell.string.length()) + [0, 1, 3] + """ + @abstractmethod def starts_with(self, prefix: str) -> Cell[bool]: """ @@ -82,7 +106,6 @@ def starts_with(self, prefix: str) -> Cell[bool]: # indexOf # lastIndexOf - # length # replace # split # substring diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_length.py b/tests/safeds/data/tabular/containers/_string_cell/test_length.py new file mode 100644 index 000000000..a8237fe2a --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_length.py @@ -0,0 +1,25 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "optimize_for_ascii", "expected"), + [ + ("", False, 0), + ("", True, 0), + ("abc", False, 3), + ("abc", True, 3), + ], + ids=[ + "empty (unoptimized)", + "empty (optimized)", + "non-empty (unoptimized)", + "non-empty (optimized)", + ], +) +def test_should_return_number_of_characters(string: str, optimize_for_ascii: bool, expected: bool) -> None: + assert_cell_operation_works( + [string], + lambda cell: cell.string.length(optimize_for_ascii=optimize_for_ascii), + [expected], + ) From 7242f069a580d65d0115a139c4a7ed31a20a7d85 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 18 May 2024 22:25:56 +0200 Subject: [PATCH 04/49] feat: `to_lowercase` and `to_uppercase` --- .../tabular/containers/_lazy_string_cell.py | 6 +++ .../data/tabular/containers/_string_cell.py | 37 ++++++++++++++++++- .../_string_cell/test_to_lowercase.py | 17 +++++++++ .../_string_cell/test_to_uppercase.py | 17 +++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index 23ab20ae3..3843e9a58 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -46,6 +46,12 @@ def ends_with(self, suffix: str) -> Cell[bool]: def starts_with(self, prefix: str) -> Cell[bool]: return _LazyCell(self._expression.str.starts_with(prefix)) + def to_lowercase(self) -> Cell[str]: + return _LazyCell(self._expression.str.to_lowercase()) + + def to_uppercase(self) -> Cell[str]: + return _LazyCell(self._expression.str.to_uppercase()) + # ------------------------------------------------------------------------------------------------------------------ # Internal # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 2b5cd8746..19e3f6d51 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -103,6 +103,41 @@ def starts_with(self, prefix: str) -> Cell[bool]: 1 """ + @abstractmethod + def to_lowercase(self) -> Cell[str]: + """ + Convert the string value in the cell to lowercase. + + Returns + ------- + result: + The string value in lowercase. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["AB", "BC", "CD"]) + >>> column.transform(lambda cell: cell.string.to_lowercase()) + ["ab", "bc", "cd"] + """ + + @abstractmethod + def to_uppercase(self) -> Cell[str]: + """ + Convert the string value in the cell to uppercase. + + Returns + ------- + result: + The string value in uppercase. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.transform(lambda cell: cell.string.to_uppercase()) + ["AB", "BC", "CD"] + """ # indexOf # lastIndexOf @@ -111,8 +146,6 @@ def starts_with(self, prefix: str) -> Cell[bool]: # substring # toFloat # toInt - # toLowercase - # toUppercase # trim # trimEnd # trimStart diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py new file mode 100644 index 000000000..14bb63b4c --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py @@ -0,0 +1,17 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", ""), + ("AbC", "abc"), + ], + ids=[ + "empty", + "non-empty", + ], +) +def test_should_lowercase_a_string(string: str, expected: str) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.to_lowercase(), [expected]) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py new file mode 100644 index 000000000..a7a6005bf --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py @@ -0,0 +1,17 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", ""), + ("AbC", "ABC"), + ], + ids=[ + "empty", + "non-empty", + ], +) +def test_should_uppercase_a_string(string: str, expected: str) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.to_uppercase(), [expected]) From 1423d149b785d3b8fd2842ab286cbf27e3478e04 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 18 May 2024 22:36:18 +0200 Subject: [PATCH 05/49] feat: `trim`, `trim_start`, `trim_end` --- .../tabular/containers/_lazy_string_cell.py | 9 +++ .../data/tabular/containers/_string_cell.py | 57 ++++++++++++++++++- .../containers/_string_cell/test_trim.py | 23 ++++++++ .../containers/_string_cell/test_trim_end.py | 23 ++++++++ .../_string_cell/test_trim_start.py | 23 ++++++++ 5 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_trim.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index 3843e9a58..f8c015e20 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -52,6 +52,15 @@ def to_lowercase(self) -> Cell[str]: def to_uppercase(self) -> Cell[str]: return _LazyCell(self._expression.str.to_uppercase()) + def trim(self) -> Cell[str]: + return _LazyCell(self._expression.str.strip_chars()) + + def trim_end(self) -> Cell[str]: + return _LazyCell(self._expression.str.strip_chars_end()) + + def trim_start(self) -> Cell[str]: + return _LazyCell(self._expression.str.strip_chars_start()) + # ------------------------------------------------------------------------------------------------------------------ # Internal # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 19e3f6d51..d874bd5cf 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -139,6 +139,60 @@ def to_uppercase(self) -> Cell[str]: ["AB", "BC", "CD"] """ + @abstractmethod + def trim(self) -> Cell[str]: + """ + Remove whitespace from the start and end of the string value in the cell. + + Returns + ------- + result: + The string value without whitespace at the start and end. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["", " abc", "abc ", " abc "]) + >>> column.transform(lambda cell: cell.string.trim()) + ["", "abc", "abc", "abc"] + """ + + @abstractmethod + def trim_end(self) -> Cell[str]: + """ + Remove whitespace from the end of the string value in the cell. + + Returns + ------- + result: + The string value without whitespace at the end. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["", " abc", "abc ", " abc "]) + >>> column.transform(lambda cell: cell.string.trim_end()) + ["", " abc", "abc", " abc"] + """ + + @abstractmethod + def trim_start(self) -> Cell[str]: + """ + Remove whitespace from the start of the string value in the cell. + + Returns + ------- + result: + The string value without whitespace at the start. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["", " abc", "abc ", " abc "]) + >>> column.transform(lambda cell: cell.string.trim_start()) + ["", "abc", "abc ", "abc "] + """ + # indexOf # lastIndexOf # replace @@ -146,9 +200,6 @@ def to_uppercase(self) -> Cell[str]: # substring # toFloat # toInt - # trim - # trimEnd - # trimStart # toDate # toTime # toDatetime diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim.py new file mode 100644 index 000000000..29dbb5723 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim.py @@ -0,0 +1,23 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", ""), + ("abc", "abc"), + (" abc", "abc"), + ("abc ", "abc"), + (" abc ", "abc"), + ], + ids=[ + "empty", + "non-empty", + "whitespace start", + "whitespace end", + "whitespace start and end", + ], +) +def test_should_remove_whitespace_prefix_and_suffix(string: str, expected: str) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.trim(), [expected]) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py new file mode 100644 index 000000000..6ece3f6fa --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py @@ -0,0 +1,23 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", ""), + ("abc", "abc"), + (" abc", " abc"), + ("abc ", "abc"), + (" abc ", " abc"), + ], + ids=[ + "empty", + "non-empty", + "whitespace start", + "whitespace end", + "whitespace start and end", + ], +) +def test_should_remove_whitespace_suffix(string: str, expected: str) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.trim_end(), [expected]) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py new file mode 100644 index 000000000..bdd34f406 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py @@ -0,0 +1,23 @@ +import pytest +from helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", ""), + ("abc", "abc"), + (" abc", "abc"), + ("abc ", "abc "), + (" abc ", "abc "), + ], + ids=[ + "empty", + "non-empty", + "whitespace start", + "whitespace end", + "whitespace start and end", + ], +) +def test_should_remove_whitespace_prefix(string: str, expected: str) -> None: + assert_cell_operation_works([string], lambda cell: cell.string.trim_start(), [expected]) From 959156199eda95833249cf4630ee911d1326f1e0 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 18 May 2024 22:56:46 +0200 Subject: [PATCH 06/49] docs: minor improvements --- .../data/tabular/containers/__init__.py | 3 ++ src/safeds/data/tabular/containers/_cell.py | 2 +- .../data/tabular/containers/_string_cell.py | 29 +++++++++++++------ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/safeds/data/tabular/containers/__init__.py b/src/safeds/data/tabular/containers/__init__.py index 5512b0b1e..35815eff3 100644 --- a/src/safeds/data/tabular/containers/__init__.py +++ b/src/safeds/data/tabular/containers/__init__.py @@ -8,6 +8,7 @@ from ._cell import Cell from ._column import Column from ._row import Row + from ._string_cell import StringCell from ._table import Table apipkg.initpkg( @@ -16,6 +17,7 @@ "Cell": "._cell:Cell", "Column": "._column:Column", "Row": "._row:Row", + "StringCell": "._string_cell:StringCell", "Table": "._table:Table", }, ) @@ -24,5 +26,6 @@ "Cell", "Column", "Row", + "StringCell", "Table", ] diff --git a/src/safeds/data/tabular/containers/_cell.py b/src/safeds/data/tabular/containers/_cell.py index cb8d5a121..d8d783d93 100644 --- a/src/safeds/data/tabular/containers/_cell.py +++ b/src/safeds/data/tabular/containers/_cell.py @@ -143,7 +143,7 @@ def __sizeof__(self) -> int: ... @property @abstractmethod def string(self) -> StringCell: - """These operations only make sense for string cells.""" + """Namespace for operations on strings.""" # ------------------------------------------------------------------------------------------------------------------ # Boolean operations diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index d874bd5cf..1d11bbb70 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -8,7 +8,18 @@ class StringCell(ABC): - """These operations only make sense for string cells.""" + """ + Namespace for operations on strings. + + This class cannot be instantiated directly. It can only be accessed using the `string` attribute of a cell. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.transform(lambda cell: cell.string.to_uppercase()) + ["AB", "BC", "CD"] + """ @abstractmethod def contains(self, substring: str) -> Cell[bool]: @@ -22,7 +33,7 @@ def contains(self, substring: str) -> Cell[bool]: Returns ------- - result: + contains: Whether the string value contains the substring. Examples @@ -45,7 +56,7 @@ def ends_with(self, suffix: str) -> Cell[bool]: Returns ------- - result: + ends_with: Whether the string value ends with the suffix. Examples @@ -92,7 +103,7 @@ def starts_with(self, prefix: str) -> Cell[bool]: Returns ------- - result: + starts_with: Whether the string value starts with the prefix. Examples @@ -110,7 +121,7 @@ def to_lowercase(self) -> Cell[str]: Returns ------- - result: + lowercase: The string value in lowercase. Examples @@ -128,7 +139,7 @@ def to_uppercase(self) -> Cell[str]: Returns ------- - result: + uppercase: The string value in uppercase. Examples @@ -146,7 +157,7 @@ def trim(self) -> Cell[str]: Returns ------- - result: + trimmed: The string value without whitespace at the start and end. Examples @@ -164,7 +175,7 @@ def trim_end(self) -> Cell[str]: Returns ------- - result: + trimmed: The string value without whitespace at the end. Examples @@ -182,7 +193,7 @@ def trim_start(self) -> Cell[str]: Returns ------- - result: + trimmed: The string value without whitespace at the start. Examples From 6ed16c027aeece3d302fa7505d48ccc6ae72d9e3 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 10:19:18 +0200 Subject: [PATCH 07/49] refactor: make metrics classes abstract They only have static methods and should not be instantiated. --- src/safeds/ml/metrics/_classification_metrics.py | 6 +++++- src/safeds/ml/metrics/_regression_metrics.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/safeds/ml/metrics/_classification_metrics.py b/src/safeds/ml/metrics/_classification_metrics.py index 94b1efe72..5a06390c5 100644 --- a/src/safeds/ml/metrics/_classification_metrics.py +++ b/src/safeds/ml/metrics/_classification_metrics.py @@ -1,5 +1,6 @@ from __future__ import annotations +from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any from safeds.data.labeled.containers import TabularDataset @@ -10,9 +11,12 @@ from safeds.data.tabular.containers import Column -class ClassificationMetrics: +class ClassificationMetrics(ABC): """A collection of classification metrics.""" + @abstractmethod + def __init__(self): ... + @staticmethod def summarize(predicted: Column | TabularDataset, expected: Column | TabularDataset, positive_class: Any) -> Table: """ diff --git a/src/safeds/ml/metrics/_regression_metrics.py b/src/safeds/ml/metrics/_regression_metrics.py index df75940bc..d0506b2e8 100644 --- a/src/safeds/ml/metrics/_regression_metrics.py +++ b/src/safeds/ml/metrics/_regression_metrics.py @@ -1,13 +1,18 @@ from __future__ import annotations +from abc import ABC, abstractmethod + from safeds.data.labeled.containers import TabularDataset from safeds.data.tabular.containers import Column, Table from safeds.exceptions import ColumnLengthMismatchError -class RegressionMetrics: +class RegressionMetrics(ABC): """A collection of regression metrics.""" + @abstractmethod + def __init__(self): ... + @staticmethod def summarize(predicted: Column | TabularDataset, expected: Column | TabularDataset) -> Table: """ From 6b7fac6855751b85bca17480668065986c34f737 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 10:20:34 +0200 Subject: [PATCH 08/49] docs: normalize result names of plot methods --- src/safeds/data/tabular/plotting/_column_plotter.py | 6 +++--- src/safeds/data/tabular/plotting/_table_plotter.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/safeds/data/tabular/plotting/_column_plotter.py b/src/safeds/data/tabular/plotting/_column_plotter.py index e9692c7da..f47dc8003 100644 --- a/src/safeds/data/tabular/plotting/_column_plotter.py +++ b/src/safeds/data/tabular/plotting/_column_plotter.py @@ -35,7 +35,7 @@ def box_plot(self) -> Image: Returns ------- - box_plot: + plot: The box plot as an image. Raises @@ -78,7 +78,7 @@ def histogram(self, *, max_bin_count: int = 10) -> Image: Returns ------- - histogram: + plot: The plot as an image. Examples @@ -100,7 +100,7 @@ def lag_plot(self, lag: int) -> Image: Returns ------- - lag_plot: + plot: The plot as an image. Raises diff --git a/src/safeds/data/tabular/plotting/_table_plotter.py b/src/safeds/data/tabular/plotting/_table_plotter.py index 81bd3f83e..5526dda48 100644 --- a/src/safeds/data/tabular/plotting/_table_plotter.py +++ b/src/safeds/data/tabular/plotting/_table_plotter.py @@ -210,7 +210,7 @@ def line_plot(self, x_name: str, y_name: str) -> Image: Returns ------- - line_plot: + plot: The plot as an image. Raises @@ -296,7 +296,7 @@ def scatter_plot(self, x_name: str, y_name: str) -> Image: Returns ------- - scatter_plot: + plot: The plot as an image. Raises From b96d9e67a332454dd7ff4d69016a2a18603732dd Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 10:23:07 +0200 Subject: [PATCH 09/49] docs: rename variable in example --- src/safeds/data/tabular/containers/_column.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/safeds/data/tabular/containers/_column.py b/src/safeds/data/tabular/containers/_column.py index 9a013cbf7..f047a8741 100644 --- a/src/safeds/data/tabular/containers/_column.py +++ b/src/safeds/data/tabular/containers/_column.py @@ -756,8 +756,8 @@ def correlation_with(self, other: Column) -> float: >>> column1.correlation_with(column2) 1.0 - >>> column4 = Column("test", [3, 2, 1]) - >>> column1.correlation_with(column4) + >>> column3 = Column("test", [3, 2, 1]) + >>> column1.correlation_with(column3) -1.0 """ import polars as pl From ea95caa2c2775a6bce979ac532ef2d4763836a62 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 10:24:00 +0200 Subject: [PATCH 10/49] docs: note that regressors must be fitted before using metrics methods --- .../ml/classical/regression/_regressor.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/safeds/ml/classical/regression/_regressor.py b/src/safeds/ml/classical/regression/_regressor.py index 4efd9c894..ce08bd506 100644 --- a/src/safeds/ml/classical/regression/_regressor.py +++ b/src/safeds/ml/classical/regression/_regressor.py @@ -23,6 +23,8 @@ def summarize_metrics(self, validation_or_test_set: Table | TabularDataset) -> T """ Summarize the regressor's metrics on the given data. + **Note:** The model must be fitted. + Parameters ---------- validation_or_test_set: @@ -65,7 +67,10 @@ def coefficient_of_determination(self, validation_or_test_set: Table | TabularDa | 0.0 | The model is as good as predicting the mean of the target values. Try something else. | | (-∞, 0.0) | The model is worse than predicting the mean of the target values. Something is very wrong. | - **Note:** Some other libraries call this metric `r2_score`. + **Notes:** + + - The model must be fitted. + - Some other libraries call this metric `r2_score`. Parameters ---------- @@ -100,6 +105,8 @@ def mean_absolute_error(self, validation_or_test_set: Table | TabularDataset) -> values. The **lower** the mean absolute error, the better the regressor. Results range from 0.0 to positive infinity. + **Note:** The model must be fitted. + Parameters ---------- validation_or_test_set: @@ -137,6 +144,8 @@ def mean_directional_accuracy(self, validation_or_test_set: Table | TabularDatas This metric is useful for time series data, where the order of the target values has a meaning. It is not useful for other types of data. Because of this, it is not included in the `summarize_metrics` method. + **Note:** The model must be fitted. + Parameters ---------- validation_or_test_set: @@ -170,7 +179,10 @@ def mean_squared_error(self, validation_or_test_set: Table | TabularDataset) -> values. The **lower** the mean squared error, the better the regressor. Results range from 0.0 to positive infinity. - **Note:** To get the root mean squared error (RMSE), take the square root of the result. + **NoteS:** + + - The model must be fitted. + - To get the root mean squared error (RMSE), take the square root of the result. Parameters ---------- @@ -205,6 +217,8 @@ def median_absolute_deviation(self, validation_or_test_set: Table | TabularDatas target values. The **lower** the median absolute deviation, the better the regressor. Results range from 0.0 to positive infinity. + **Note:** The model must be fitted. + Parameters ---------- validation_or_test_set: From 6dbdce227187871f9a32fd053dc0d39629dbc113 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 10:28:06 +0200 Subject: [PATCH 11/49] feat: property access to parameters of label and one-hot encoder --- .../data/tabular/transformation/_label_encoder.py | 11 ++++++++++- .../data/tabular/transformation/_one_hot_encoder.py | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/safeds/data/tabular/transformation/_label_encoder.py b/src/safeds/data/tabular/transformation/_label_encoder.py index fa7f2e09b..c95d17b02 100644 --- a/src/safeds/data/tabular/transformation/_label_encoder.py +++ b/src/safeds/data/tabular/transformation/_label_encoder.py @@ -20,7 +20,7 @@ class LabelEncoder(InvertibleTableTransformer): ---------- partial_order: The partial order of the labels. The labels are encoded in the order of the given list. Additional values are - encoded as the next integer after the last value in the list in the order they appear in the data. + assigned labels in the order they are encountered during fitting. """ # ------------------------------------------------------------------------------------------------------------------ @@ -47,6 +47,15 @@ def __hash__(self) -> int: # Leave out the internal state for faster hashing ) + # ------------------------------------------------------------------------------------------------------------------ + # Properties + # ------------------------------------------------------------------------------------------------------------------ + + @property + def partial_order(self) -> list[Any]: + """The partial order of the labels.""" + return list(self._partial_order) # defensive copy + # ------------------------------------------------------------------------------------------------------------------ # Learning and transformation # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/transformation/_one_hot_encoder.py b/src/safeds/data/tabular/transformation/_one_hot_encoder.py index 25d6a82d9..ca488d8f8 100644 --- a/src/safeds/data/tabular/transformation/_one_hot_encoder.py +++ b/src/safeds/data/tabular/transformation/_one_hot_encoder.py @@ -99,6 +99,15 @@ def __hash__(self) -> int: self._mapping, ) + # ------------------------------------------------------------------------------------------------------------------ + # Properties + # ------------------------------------------------------------------------------------------------------------------ + + @property + def separator(self) -> str: + """The separator used to separate the original column name from the value in the new column names.""" + return self._separator + # ------------------------------------------------------------------------------------------------------------------ # Learning and transformation # ------------------------------------------------------------------------------------------------------------------ From 541b1dbc355058ef761dd2daa9d4ad44446478fc Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 10:33:48 +0200 Subject: [PATCH 12/49] docs: document the class, not the `__init__` --- .../ml/nn/layers/_convolutional2d_layer.py | 62 ++++----- src/safeds/ml/nn/layers/_flatten_layer.py | 3 +- src/safeds/ml/nn/layers/_forward_layer.py | 88 ++++++------- src/safeds/ml/nn/layers/_lstm_layer.py | 87 ++++++------- src/safeds/ml/nn/layers/_pooling2d_layer.py | 119 +++++++++--------- 5 files changed, 183 insertions(+), 176 deletions(-) diff --git a/src/safeds/ml/nn/layers/_convolutional2d_layer.py b/src/safeds/ml/nn/layers/_convolutional2d_layer.py index 70b717487..8c507c933 100644 --- a/src/safeds/ml/nn/layers/_convolutional2d_layer.py +++ b/src/safeds/ml/nn/layers/_convolutional2d_layer.py @@ -82,21 +82,22 @@ def forward(self, x: Tensor) -> Tensor: class Convolutional2DLayer(Layer): - def __init__(self, output_channel: int, kernel_size: int, *, stride: int = 1, padding: int = 0): - """ - Create a Convolutional 2D Layer. + """ + A convolutional 2D Layer. + + Parameters + ---------- + output_channel: + the amount of output channels + kernel_size: + the size of the kernel + stride: + the stride of the convolution + padding: + the padding of the convolution + """ - Parameters - ---------- - output_channel: - the amount of output channels - kernel_size: - the size of the kernel - stride: - the stride of the convolution - padding: - the padding of the convolution - """ + def __init__(self, output_channel: int, kernel_size: int, *, stride: int = 1, padding: int = 0): self._output_channel = output_channel self._kernel_size = kernel_size self._stride = stride @@ -246,6 +247,23 @@ def __sizeof__(self) -> int: class ConvolutionalTranspose2DLayer(Convolutional2DLayer): + """ + A convolutional transpose 2D Layer. + + Parameters + ---------- + output_channel: + the amount of output channels + kernel_size: + the size of the kernel + stride: + the stride of the transposed convolution + padding: + the padding of the transposed convolution + output_padding: + the output padding of the transposed convolution + """ + def __init__( self, output_channel: int, @@ -255,22 +273,6 @@ def __init__( padding: int = 0, output_padding: int = 0, ): - """ - Create a Convolutional Transpose 2D Layer. - - Parameters - ---------- - output_channel: - the amount of output channels - kernel_size: - the size of the kernel - stride: - the stride of the transposed convolution - padding: - the padding of the transposed convolution - output_padding: - the output padding of the transposed convolution - """ super().__init__(output_channel, kernel_size, stride=stride, padding=padding) self._output_padding = output_padding diff --git a/src/safeds/ml/nn/layers/_flatten_layer.py b/src/safeds/ml/nn/layers/_flatten_layer.py index 5ac58e318..17f72388f 100644 --- a/src/safeds/ml/nn/layers/_flatten_layer.py +++ b/src/safeds/ml/nn/layers/_flatten_layer.py @@ -31,8 +31,9 @@ def forward(self, x: Tensor) -> Tensor: class FlattenLayer(Layer): + """A flatten layer.""" + def __init__(self) -> None: - """Create a Flatten Layer.""" self._input_size: ImageSize | None = None self._output_size: int | None = None diff --git a/src/safeds/ml/nn/layers/_forward_layer.py b/src/safeds/ml/nn/layers/_forward_layer.py index 745178cfa..741668b69 100644 --- a/src/safeds/ml/nn/layers/_forward_layer.py +++ b/src/safeds/ml/nn/layers/_forward_layer.py @@ -13,52 +13,25 @@ from torch import Tensor, nn -def _create_internal_model(input_size: int, output_size: int, activation_function: str) -> nn.Module: - from torch import nn - - _init_default_device() - - class _InternalLayer(nn.Module): - def __init__(self, input_size: int, output_size: int, activation_function: str): - super().__init__() - self._layer = nn.Linear(input_size, output_size) - match activation_function: - case "sigmoid": - self._fn = nn.Sigmoid() - case "relu": - self._fn = nn.ReLU() - case "softmax": - self._fn = nn.Softmax() - case "none": - self._fn = None - case _: - raise ValueError("Unknown Activation Function: " + activation_function) - - def forward(self, x: Tensor) -> Tensor: - return self._fn(self._layer(x)) if self._fn is not None else self._layer(x) - - return _InternalLayer(input_size, output_size, activation_function) - - class ForwardLayer(Layer): - def __init__(self, output_size: int, input_size: int | None = None): - """ - Create a Feed Forward Layer. - - Parameters - ---------- - input_size: - The number of neurons in the previous layer - output_size: - The number of neurons in this layer + """ + Create a forward Layer. + + Parameters + ---------- + output_size: + The number of neurons in this layer + input_size: + The number of neurons in the previous layer + + Raises + ------ + OutOfBoundsError + If input_size < 1 + If output_size < 1 + """ - Raises - ------ - OutOfBoundsError - If input_size < 1 - If output_size < 1 - - """ + def __init__(self, output_size: int, input_size: int | None = None): if input_size is not None: self._set_input_size(input_size=input_size) @@ -145,3 +118,30 @@ def __sizeof__(self) -> int: import sys return sys.getsizeof(self._input_size) + sys.getsizeof(self._output_size) + + +def _create_internal_model(input_size: int, output_size: int, activation_function: str) -> nn.Module: + from torch import nn + + _init_default_device() + + class _InternalLayer(nn.Module): + def __init__(self, input_size: int, output_size: int, activation_function: str): + super().__init__() + self._layer = nn.Linear(input_size, output_size) + match activation_function: + case "sigmoid": + self._fn = nn.Sigmoid() + case "relu": + self._fn = nn.ReLU() + case "softmax": + self._fn = nn.Softmax() + case "none": + self._fn = None + case _: + raise ValueError("Unknown Activation Function: " + activation_function) + + def forward(self, x: Tensor) -> Tensor: + return self._fn(self._layer(x)) if self._fn is not None else self._layer(x) + + return _InternalLayer(input_size, output_size, activation_function) diff --git a/src/safeds/ml/nn/layers/_lstm_layer.py b/src/safeds/ml/nn/layers/_lstm_layer.py index db74f36a9..0e15149cb 100644 --- a/src/safeds/ml/nn/layers/_lstm_layer.py +++ b/src/safeds/ml/nn/layers/_lstm_layer.py @@ -14,51 +14,25 @@ from torch import Tensor, nn -def _create_internal_model(input_size: int, output_size: int, activation_function: str) -> nn.Module: - from torch import nn - - _init_default_device() - - class _InternalLayer(nn.Module): - def __init__(self, input_size: int, output_size: int, activation_function: str): - super().__init__() - self._layer = nn.LSTM(input_size, output_size) - match activation_function: - case "sigmoid": - self._fn = nn.Sigmoid() - case "relu": - self._fn = nn.ReLU() - case "softmax": - self._fn = nn.Softmax() - case "none": - self._fn = None - case _: - raise ValueError("Unknown Activation Function: " + activation_function) - - def forward(self, x: Tensor) -> Tensor: - return self._fn(self._layer(x)[0]) if self._fn is not None else self._layer(x)[0] - - return _InternalLayer(input_size, output_size, activation_function) - - class LSTMLayer(Layer): - def __init__(self, output_size: int, input_size: int | None = None): - """ - Create a LSTM Layer. + """ + A long short-term memory (LSTM) layer. + + Parameters + ---------- + output_size: + The number of neurons in this layer + input_size: + The number of neurons in the previous layer + + Raises + ------ + OutOfBoundsError + If input_size < 1 + If output_size < 1 + """ - Parameters - ---------- - input_size: - The number of neurons in the previous layer - output_size: - The number of neurons in this layer - - Raises - ------ - OutOfBoundsError - If input_size < 1 - If output_size < 1 - """ + def __init__(self, output_size: int, input_size: int | None = None): if input_size is not None: self._set_input_size(input_size=input_size) @@ -149,3 +123,30 @@ def __sizeof__(self) -> int: Size of this object in bytes. """ return sys.getsizeof(self._input_size) + sys.getsizeof(self._output_size) + + +def _create_internal_model(input_size: int, output_size: int, activation_function: str) -> nn.Module: + from torch import nn + + _init_default_device() + + class _InternalLayer(nn.Module): + def __init__(self, input_size: int, output_size: int, activation_function: str): + super().__init__() + self._layer = nn.LSTM(input_size, output_size) + match activation_function: + case "sigmoid": + self._fn = nn.Sigmoid() + case "relu": + self._fn = nn.ReLU() + case "softmax": + self._fn = nn.Softmax() + case "none": + self._fn = None + case _: + raise ValueError("Unknown Activation Function: " + activation_function) + + def forward(self, x: Tensor) -> Tensor: + return self._fn(self._layer(x)[0]) if self._fn is not None else self._layer(x)[0] + + return _InternalLayer(input_size, output_size, activation_function) diff --git a/src/safeds/ml/nn/layers/_pooling2d_layer.py b/src/safeds/ml/nn/layers/_pooling2d_layer.py index ffd6c2f9d..1e2170b14 100644 --- a/src/safeds/ml/nn/layers/_pooling2d_layer.py +++ b/src/safeds/ml/nn/layers/_pooling2d_layer.py @@ -14,42 +14,23 @@ from torch import Tensor, nn -def _create_internal_model(strategy: Literal["max", "avg"], kernel_size: int, padding: int, stride: int) -> nn.Module: - from torch import nn - - _init_default_device() - - class _InternalLayer(nn.Module): - def __init__(self, strategy: Literal["max", "avg"], kernel_size: int, padding: int, stride: int): - super().__init__() - match strategy: - case "max": - self._layer = nn.MaxPool2d(kernel_size=kernel_size, padding=padding, stride=stride) - case "avg": - self._layer = nn.AvgPool2d(kernel_size=kernel_size, padding=padding, stride=stride) - - def forward(self, x: Tensor) -> Tensor: - return self._layer(x) - - return _InternalLayer(strategy, kernel_size, padding, stride) - - class _Pooling2DLayer(Layer): - def __init__(self, strategy: Literal["max", "avg"], kernel_size: int, *, stride: int = -1, padding: int = 0): - """ - Create a Pooling 2D Layer. + """ + A pooling 2D Layer. + + Parameters + ---------- + strategy: + the strategy of the pooling + kernel_size: + the size of the kernel + stride: + the stride of the pooling + padding: + the padding of the pooling + """ - Parameters - ---------- - strategy: - the strategy of the pooling - kernel_size: - the size of the kernel - stride: - the stride of the pooling - padding: - the padding of the pooling - """ + def __init__(self, strategy: Literal["max", "avg"], kernel_size: int, *, stride: int = -1, padding: int = 0): self._strategy = strategy self._kernel_size = kernel_size self._stride = stride if stride != -1 else kernel_size @@ -177,34 +158,56 @@ def __sizeof__(self) -> int: class MaxPooling2DLayer(_Pooling2DLayer): - def __init__(self, kernel_size: int, *, stride: int = -1, padding: int = 0) -> None: - """ - Create a maximum Pooling 2D Layer. + """ + A maximum Pooling 2D Layer. + + Parameters + ---------- + kernel_size: + the size of the kernel + stride: + the stride of the pooling + padding: + the padding of the pooling + """ - Parameters - ---------- - kernel_size: - the size of the kernel - stride: - the stride of the pooling - padding: - the padding of the pooling - """ + def __init__(self, kernel_size: int, *, stride: int = -1, padding: int = 0) -> None: super().__init__("max", kernel_size, stride=stride, padding=padding) class AveragePooling2DLayer(_Pooling2DLayer): - def __init__(self, kernel_size: int, *, stride: int = -1, padding: int = 0) -> None: - """ - Create a average Pooling 2D Layer. + """ + An average pooling 2D Layer. + + Parameters + ---------- + kernel_size: + the size of the kernel + stride: + the stride of the pooling + padding: + the padding of the pooling + """ - Parameters - ---------- - kernel_size: - the size of the kernel - stride: - the stride of the pooling - padding: - the padding of the pooling - """ + def __init__(self, kernel_size: int, *, stride: int = -1, padding: int = 0) -> None: super().__init__("avg", kernel_size, stride=stride, padding=padding) + + +def _create_internal_model(strategy: Literal["max", "avg"], kernel_size: int, padding: int, stride: int) -> nn.Module: + from torch import nn + + _init_default_device() + + class _InternalLayer(nn.Module): + def __init__(self, strategy: Literal["max", "avg"], kernel_size: int, padding: int, stride: int): + super().__init__() + match strategy: + case "max": + self._layer = nn.MaxPool2d(kernel_size=kernel_size, padding=padding, stride=stride) + case "avg": + self._layer = nn.AvgPool2d(kernel_size=kernel_size, padding=padding, stride=stride) + + def forward(self, x: Tensor) -> Tensor: + return self._layer(x) + + return _InternalLayer(strategy, kernel_size, padding, stride) From cdfdbbbf013c15aefd76c757733744629ac081d0 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:03:57 +0200 Subject: [PATCH 13/49] test: refactor helper for cell tests --- src/safeds/data/tabular/containers/_cell.py | 60 +++++++++---------- tests/helpers/_assertions.py | 16 ++--- .../containers/_string_cell/test_contains.py | 5 +- .../containers/_string_cell/test_ends_with.py | 5 +- .../containers/_string_cell/test_length.py | 7 ++- .../_string_cell/test_starts_with.py | 5 +- .../_string_cell/test_to_lowercase.py | 5 +- .../_string_cell/test_to_uppercase.py | 5 +- .../containers/_string_cell/test_trim.py | 5 +- .../containers/_string_cell/test_trim_end.py | 5 +- .../_string_cell/test_trim_start.py | 5 +- 11 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/safeds/data/tabular/containers/_cell.py b/src/safeds/data/tabular/containers/_cell.py index d8d783d93..aa9543c7d 100644 --- a/src/safeds/data/tabular/containers/_cell.py +++ b/src/safeds/data/tabular/containers/_cell.py @@ -383,6 +383,36 @@ def add(self, other: Any) -> Cell[R_co]: """ return self.__add__(other) + def div(self, other: Any) -> Cell[R_co]: + """ + Divide by a value. This is equivalent to the `/` operator. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", [6, 8]) + >>> column.transform(lambda cell: cell.div(2)) + +---------+ + | example | + | --- | + | f64 | + +=========+ + | 3.00000 | + | 4.00000 | + +---------+ + + >>> column.transform(lambda cell: cell / 2) + +---------+ + | example | + | --- | + | f64 | + +=========+ + | 3.00000 | + | 4.00000 | + +---------+ + """ + return self.__truediv__(other) + def mod(self, other: Any) -> Cell[R_co]: """ Perform a modulo operation. This is equivalent to the `%` operator. @@ -503,36 +533,6 @@ def sub(self, other: Any) -> Cell[R_co]: """ return self.__sub__(other) - def div(self, other: Any) -> Cell[R_co]: - """ - Divide by a value. This is equivalent to the `/` operator. - - Examples - -------- - >>> from safeds.data.tabular.containers import Column - >>> column = Column("example", [6, 8]) - >>> column.transform(lambda cell: cell.div(2)) - +---------+ - | example | - | --- | - | f64 | - +=========+ - | 3.00000 | - | 4.00000 | - +---------+ - - >>> column.transform(lambda cell: cell / 2) - +---------+ - | example | - | --- | - | f64 | - +=========+ - | 3.00000 | - | 4.00000 | - +---------+ - """ - return self.__truediv__(other) - # ------------------------------------------------------------------------------------------------------------------ # Comparison operations # ------------------------------------------------------------------------------------------------------------------ diff --git a/tests/helpers/_assertions.py b/tests/helpers/_assertions.py index 2dc9b7709..a3e774faf 100644 --- a/tests/helpers/_assertions.py +++ b/tests/helpers/_assertions.py @@ -38,22 +38,22 @@ def assert_that_tabular_datasets_are_equal(table1: TabularDataset, table2: Tabul def assert_cell_operation_works( - input_data: list[Any], + input_value: Any, transformer: Callable[[Cell], Cell], - expected_data: list[Any], + expected_value: Any, ) -> None: """ Assert that a cell operation works as expected. Parameters ---------- - input_data: - The input data. + input_value: + The value in the input cell. transformer: The transformer to apply to the cells. - expected_data: - The expected data. + expected_value: + The expected value of the transformed cell. """ - column = Column("A", input_data) + column = Column("A", [input_value]) transformed_column = column.transform(transformer) - assert transformed_column == Column("A", expected_data) + assert transformed_column == Column("A", [expected_value]) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_contains.py b/tests/safeds/data/tabular/containers/_string_cell/test_contains.py index 80acf2d07..d826bf5c8 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_contains.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_contains.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -18,4 +19,4 @@ ], ) def test_should_check_whether_string_contains_substring(string: str, substring: str, expected: bool) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.contains(substring), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.contains(substring), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py b/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py index 1052cffcc..02ef18d32 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -18,4 +19,4 @@ ], ) def test_should_check_whether_string_ends_with_prefix(string: str, suffix: str, expected: bool) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.ends_with(suffix), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.ends_with(suffix), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_length.py b/tests/safeds/data/tabular/containers/_string_cell/test_length.py index a8237fe2a..95b948685 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_length.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_length.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -19,7 +20,7 @@ ) def test_should_return_number_of_characters(string: str, optimize_for_ascii: bool, expected: bool) -> None: assert_cell_operation_works( - [string], + string, lambda cell: cell.string.length(optimize_for_ascii=optimize_for_ascii), - [expected], + expected, ) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py b/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py index 5848f626b..ebc6ec4d6 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -18,4 +19,4 @@ ], ) def test_should_check_whether_string_start_with_prefix(string: str, prefix: str, expected: bool) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.starts_with(prefix), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.starts_with(prefix), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py index 14bb63b4c..f1266a36a 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -14,4 +15,4 @@ ], ) def test_should_lowercase_a_string(string: str, expected: str) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.to_lowercase(), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.to_lowercase(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py index a7a6005bf..f7a7e85fc 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -14,4 +15,4 @@ ], ) def test_should_uppercase_a_string(string: str, expected: str) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.to_uppercase(), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.to_uppercase(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim.py index 29dbb5723..cbd5c9ed6 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_trim.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -20,4 +21,4 @@ ], ) def test_should_remove_whitespace_prefix_and_suffix(string: str, expected: str) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.trim(), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.trim(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py index 6ece3f6fa..0fe242079 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -20,4 +21,4 @@ ], ) def test_should_remove_whitespace_suffix(string: str, expected: str) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.trim_end(), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.trim_end(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py index bdd34f406..10477f8cb 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py @@ -1,5 +1,6 @@ import pytest -from helpers import assert_cell_operation_works + +from tests.helpers import assert_cell_operation_works @pytest.mark.parametrize( @@ -20,4 +21,4 @@ ], ) def test_should_remove_whitespace_prefix(string: str, expected: str) -> None: - assert_cell_operation_works([string], lambda cell: cell.string.trim_start(), [expected]) + assert_cell_operation_works(string, lambda cell: cell.string.trim_start(), expected) From 1e9036b2e87732edd495956b3b685440a5347a80 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:04:11 +0200 Subject: [PATCH 14/49] test: `Cell.abs` --- .../data/tabular/containers/_cell/__init__.py | 0 .../data/tabular/containers/_cell/test_abs.py | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/__init__.py create mode 100644 tests/safeds/data/tabular/containers/_cell/test_abs.py diff --git a/tests/safeds/data/tabular/containers/_cell/__init__.py b/tests/safeds/data/tabular/containers/_cell/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/safeds/data/tabular/containers/_cell/test_abs.py b/tests/safeds/data/tabular/containers/_cell/test_abs.py new file mode 100644 index 000000000..de02f2eff --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_abs.py @@ -0,0 +1,30 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (0, 0), + (0.0, 0.0), + (10, 10), + (10.5, 10.5), + (-10, 10), + (-10.5, 10.5), + ], + ids=[ + "zero int", + "zero float", + "positive int", + "positive float", + "negative int", + "negative float", + ], +) +class TestShouldReturnAbsoluteValueOfCell: + def test_dunder_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: abs(cell), expected) + + def test_named_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: cell.abs(), expected) From bd0d172ccd0bfabdf0085578c232944593817bb3 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:06:17 +0200 Subject: [PATCH 15/49] test: `Cell.neg` --- .../data/tabular/containers/_cell/test_neg.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_neg.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_neg.py b/tests/safeds/data/tabular/containers/_cell/test_neg.py new file mode 100644 index 000000000..c724c00db --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_neg.py @@ -0,0 +1,30 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (0, 0), + (0.0, 0.0), + (10, -10), + (10.5, -10.5), + (-10, 10), + (-10.5, 10.5), + ], + ids=[ + "zero int", + "zero float", + "positive int", + "positive float", + "negative int", + "negative float", + ], +) +class TestShouldNegateValueOfCell: + def test_dunder_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: -cell, expected) + + def test_named_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: cell.neg(), expected) From b8f9c95280e33f3a52593fb60ae678de8f3e4b80 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:08:08 +0200 Subject: [PATCH 16/49] test: `+Cell` --- .../data/tabular/containers/_cell/test_pos.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_pos.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_pos.py b/tests/safeds/data/tabular/containers/_cell/test_pos.py new file mode 100644 index 000000000..c373c41c8 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_pos.py @@ -0,0 +1,27 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (0, 0), + (0.0, 0.0), + (10, 10), + (10.5, 10.5), + (-10, -10), + (-10.5, -10.5), + ], + ids=[ + "zero int", + "zero float", + "positive int", + "positive float", + "negative int", + "negative float", + ], +) +class TestShouldReturnValueOfCell: + def test_dunder_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: +cell, expected) From fc391daf81e30d697b1266e8ba5427dd226c72e6 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:17:22 +0200 Subject: [PATCH 17/49] test: `Cell.floor` --- .../data/tabular/containers/_lazy_cell.py | 5 ++- tests/helpers/_assertions.py | 2 +- .../tabular/containers/_cell/test_floor.py | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_floor.py diff --git a/src/safeds/data/tabular/containers/_lazy_cell.py b/src/safeds/data/tabular/containers/_lazy_cell.py index b985bba46..c30a09580 100644 --- a/src/safeds/data/tabular/containers/_lazy_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_cell.py @@ -88,7 +88,10 @@ def __ceil__(self) -> Cell[R]: return _wrap(self._expression.ceil()) def __floor__(self) -> Cell[R]: - return _wrap(self._expression.floor()) + import polars as pl + + # polars does not yet implement floor for integers + return _wrap(self._expression.cast(pl.Float64).floor()) def __neg__(self) -> Cell[R]: return _wrap(self._expression.__neg__()) diff --git a/tests/helpers/_assertions.py b/tests/helpers/_assertions.py index a3e774faf..4c4847c6e 100644 --- a/tests/helpers/_assertions.py +++ b/tests/helpers/_assertions.py @@ -56,4 +56,4 @@ def assert_cell_operation_works( """ column = Column("A", [input_value]) transformed_column = column.transform(transformer) - assert transformed_column == Column("A", [expected_value]) + assert transformed_column == Column("A", [expected_value]), f"Expected: {expected_value}\nGot: {transformed_column}" diff --git a/tests/safeds/data/tabular/containers/_cell/test_floor.py b/tests/safeds/data/tabular/containers/_cell/test_floor.py new file mode 100644 index 000000000..e4ce81cb5 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_floor.py @@ -0,0 +1,32 @@ +import math + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (0, 0), + (0.0, 0), + (10, 10), + (10.5, 10), + (-10, -10), + (-10.5, -11), + ], + ids=[ + "zero int", + "zero float", + "positive int", + "positive float", + "negative int", + "negative float", + ], +) +class TestShouldReturnFloorOfCell: + def test_dunder_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: math.floor(cell), expected) + + def test_named_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: cell.floor(), expected) From cdc11d7f590ef3ca309157913bc21e3180cda951 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:18:44 +0200 Subject: [PATCH 18/49] test: `Cell.ceil` --- .../data/tabular/containers/_lazy_cell.py | 5 ++- .../tabular/containers/_cell/test_ceil.py | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_ceil.py diff --git a/src/safeds/data/tabular/containers/_lazy_cell.py b/src/safeds/data/tabular/containers/_lazy_cell.py index c30a09580..0c1a1ba56 100644 --- a/src/safeds/data/tabular/containers/_lazy_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_cell.py @@ -85,7 +85,10 @@ def __abs__(self) -> Cell[R]: return _wrap(self._expression.__abs__()) def __ceil__(self) -> Cell[R]: - return _wrap(self._expression.ceil()) + import polars as pl + + # polars does not yet implement floor for integers + return _wrap(self._expression.cast(pl.Float64).ceil()) def __floor__(self) -> Cell[R]: import polars as pl diff --git a/tests/safeds/data/tabular/containers/_cell/test_ceil.py b/tests/safeds/data/tabular/containers/_cell/test_ceil.py new file mode 100644 index 000000000..647528f24 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_ceil.py @@ -0,0 +1,32 @@ +import math + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (0, 0), + (0.0, 0), + (10, 10), + (10.5, 11), + (-10, -10), + (-10.5, -10), + ], + ids=[ + "zero int", + "zero float", + "positive int", + "positive float", + "negative int", + "negative float", + ], +) +class TestShouldReturnCeilOfCell: + def test_dunder_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: math.ceil(cell), expected) + + def test_named_method(self, value: float, expected: float): + assert_cell_operation_works(value, lambda cell: cell.ceil(), expected) From fb766080647a173a57a514943617999920a442b4 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:22:03 +0200 Subject: [PATCH 19/49] test: `Cell.not_` --- .../data/tabular/containers/_lazy_cell.py | 4 ++- .../data/tabular/containers/_cell/test_not.py | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_not.py diff --git a/src/safeds/data/tabular/containers/_lazy_cell.py b/src/safeds/data/tabular/containers/_lazy_cell.py index 0c1a1ba56..371b4e298 100644 --- a/src/safeds/data/tabular/containers/_lazy_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_cell.py @@ -33,7 +33,9 @@ def __init__(self, expression: pl.Expr) -> None: # "Boolean" operators (actually bitwise) ----------------------------------- def __invert__(self) -> Cell[bool]: - return _wrap(self._expression.__invert__()) + import polars as pl + + return _wrap(self._expression.cast(pl.Boolean).__invert__()) def __and__(self, other: bool | Cell[bool]) -> Cell[bool]: return _wrap(self._expression.__and__(other)) diff --git a/tests/safeds/data/tabular/containers/_cell/test_not.py b/tests/safeds/data/tabular/containers/_cell/test_not.py new file mode 100644 index 000000000..1e5f342fd --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_not.py @@ -0,0 +1,28 @@ +from typing import Any + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (False, True), + (True, False), + (0, True), + (1, False), + ], + ids=[ + "false", + "true", + "falsy int", + "truthy int", + ], +) +class TestShouldInvertValueOfCell: + def test_dunder_method(self, value: Any, expected: bool): + assert_cell_operation_works(value, lambda cell: ~cell, expected) + + def test_named_method(self, value: Any, expected: bool): + assert_cell_operation_works(value, lambda cell: cell.not_(), expected) From 03e36c129600bc6f751a6b81748f4de4197b8ac0 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:56:01 +0200 Subject: [PATCH 20/49] test: `Cell.and_` --- .../data/tabular/containers/_cell/test_and.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_and.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_and.py b/tests/safeds/data/tabular/containers/_cell/test_and.py new file mode 100644 index 000000000..c751355f0 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_and.py @@ -0,0 +1,39 @@ +from typing import Any + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (False, False, False), + (False, True, False), + (True, False, False), + (True, True, True), + (0, False, False), + (0, True, False), + (1, False, False), + (1, True, True), + ], + ids=[ + "false - false", + "false - true", + "true - false", + "true - true", + "falsy int - false", + "falsy int - true", + "truthy int - false", + "truthy int - true", + ], +) +class TestShouldComputeConjunction: + def test_dunder_method(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell & value2, expected) + + def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 & cell, expected) + + def test_named_method(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.and_(value2), expected) From a1dba019f4a627293a95f3c68851a45a93cae6de Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:56:07 +0200 Subject: [PATCH 21/49] test: `Cell.or_` --- .../data/tabular/containers/_cell/test_or.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_or.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_or.py b/tests/safeds/data/tabular/containers/_cell/test_or.py new file mode 100644 index 000000000..85328e794 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_or.py @@ -0,0 +1,39 @@ +from typing import Any + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (False, False, False), + (False, True, True), + (True, False, True), + (True, True, True), + (0, False, False), + (0, True, True), + (1, False, True), + (1, True, True), + ], + ids=[ + "false - false", + "false - true", + "true - false", + "true - true", + "falsy int - false", + "falsy int - true", + "truthy int - false", + "truthy int - true", + ], +) +class TestShouldComputeDisjunction: + def test_dunder_method(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell | value2, expected) + + def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 | cell, expected) + + def test_named_method(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.or_(value2), expected) From 3e6a0d59b03a87d0e4f57f46ba9ba6bd4b2fade1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 11:58:37 +0200 Subject: [PATCH 22/49] test: `Cell.xor` --- .../data/tabular/containers/_cell/test_xor.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_xor.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_xor.py b/tests/safeds/data/tabular/containers/_cell/test_xor.py new file mode 100644 index 000000000..ddbe12e4a --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_xor.py @@ -0,0 +1,39 @@ +from typing import Any + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (False, False, False), + (False, True, True), + (True, False, True), + (True, True, False), + (0, False, False), + (0, True, True), + (1, False, True), + (1, True, False), + ], + ids=[ + "false - false", + "false - true", + "true - false", + "true - true", + "falsy int - false", + "falsy int - true", + "truthy int - false", + "truthy int - true", + ], +) +class TestShouldComputeExclusiveOr: + def test_dunder_method(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell ^ value2, expected) + + def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 ^ cell, expected) + + def test_named_method(self, value1: Any, value2: bool, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.xor(value2), expected) From 33eb5f23ced470d808b8f638ed1e78ff4fa9f295 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:06:34 +0200 Subject: [PATCH 23/49] test: `Cell.add` --- .../data/tabular/containers/_cell/test_add.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_add.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_add.py b/tests/safeds/data/tabular/containers/_cell/test_add.py new file mode 100644 index 000000000..238ecfb2f --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_add.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, 6), + (3, 1.5, 4.5), + (1.5, 3, 4.5), + (1.5, 1.5, 3.0), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeAddition: + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell + value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: value2 + cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell.add(value2), expected) From 74ff41b0bf603b38af88c9764c7cdb8e4b265f2d Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:07:56 +0200 Subject: [PATCH 24/49] test: `Cell.sub` --- .../data/tabular/containers/_cell/test_sub.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_sub.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_sub.py b/tests/safeds/data/tabular/containers/_cell/test_sub.py new file mode 100644 index 000000000..516c1048e --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_sub.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, 0), + (3, 1.5, 1.5), + (1.5, 3, -1.5), + (1.5, 1.5, 0.0), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeSubtraction: + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell - value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: value2 - cell, -expected) + + def test_named_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell.sub(value2), expected) From c0d7875d9a642fd0df52e61da83a5c1f44482ce5 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:08:43 +0200 Subject: [PATCH 25/49] test: `Cell.mul` --- .../data/tabular/containers/_cell/test_mul.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_mul.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_mul.py b/tests/safeds/data/tabular/containers/_cell/test_mul.py new file mode 100644 index 000000000..8ef843bc8 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_mul.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, 9), + (3, 1.5, 4.5), + (1.5, 3, 4.5), + (1.5, 1.5, 2.25), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeMultiplication: + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell * value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: value2 * cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell.mul(value2), expected) From 62fe85f04f2892981d190181336acca98f34b520 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:09:54 +0200 Subject: [PATCH 26/49] test: `Cell.div` --- .../data/tabular/containers/_cell/test_div.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_div.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_div.py b/tests/safeds/data/tabular/containers/_cell/test_div.py new file mode 100644 index 000000000..94ff3efc5 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_div.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, 1), + (3, 1.5, 2.0), + (1.5, 3, 0.5), + (1.5, 1.5, 1.0), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeDivision: + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell / value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: value2 / cell, 1 / expected) + + def test_named_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell.div(value2), expected) From eaea0322175452031047e5bcae1775edf258be1a Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:13:59 +0200 Subject: [PATCH 27/49] test: `Cell.__floordiv__` --- .../tabular/containers/_cell/test_floordiv.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_floordiv.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_floordiv.py b/tests/safeds/data/tabular/containers/_cell/test_floordiv.py new file mode 100644 index 000000000..368465283 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_floordiv.py @@ -0,0 +1,41 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +class TestShouldComputeDivision: + @pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 2, 1), + (3, 1.6, 1), + (1.5, 3, 0), + (1.5, 1.4, 1), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], + ) + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell // value2, expected) + + @pytest.mark.parametrize( + ("value1", "value2", "expected_inverted"), + [ + (3, 2, 0), + (3, 1.6, 0), + (1.5, 3, 2), + (1.5, 1.4, 0), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], + ) + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected_inverted: float): + assert_cell_operation_works(value1, lambda cell: value2 // cell, expected_inverted) From 4b8f287d09dc17ac236cc79c5a94a340d3174a82 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:16:40 +0200 Subject: [PATCH 28/49] test: `Cell.eq` --- .../data/tabular/containers/_cell/test_eq.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_eq.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_eq.py b/tests/safeds/data/tabular/containers/_cell/test_eq.py new file mode 100644 index 000000000..8a7616c70 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_eq.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, True), + (3, 1.5, False), + (1.5, 3, False), + (1.5, 1.5, True), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeEquality: + def test_dunder_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell == value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 == cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.eq(value2), expected) From 06a97145379426c288e7d90c3fa334618c7cb73f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:17:48 +0200 Subject: [PATCH 29/49] test: `Cell.ne` --- .../data/tabular/containers/_cell/test_ne.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_ne.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_ne.py b/tests/safeds/data/tabular/containers/_cell/test_ne.py new file mode 100644 index 000000000..93eacf824 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_ne.py @@ -0,0 +1,26 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, False), + (3, 1.5, True), + (1.5, 3, True), + (1.5, 1.5, False), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeNegatedEquality: + def test_dunder_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell != value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 != cell, expected) From 6baedcf6038b6031471032e72b3b0e2e909bbcd1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:25:39 +0200 Subject: [PATCH 30/49] test: `Cell.ge` --- .../data/tabular/containers/_cell/test_ge.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_ge.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_ge.py b/tests/safeds/data/tabular/containers/_cell/test_ge.py new file mode 100644 index 000000000..17256ce10 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_ge.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, True), + (3, 1.5, True), + (1.5, 3, False), + (1.5, 1.5, True), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeGreaterThanOrEqual: + def test_dunder_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell >= value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 <= cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.ge(value2), expected) From 7ac41ef48ff8c339edbeb861c22c6765aac55858 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:26:57 +0200 Subject: [PATCH 31/49] test: `Cell.lt` --- .../data/tabular/containers/_cell/test_gt.py | 29 +++++++++++++++++++ .../data/tabular/containers/_cell/test_lt.py | 29 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_gt.py create mode 100644 tests/safeds/data/tabular/containers/_cell/test_lt.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_gt.py b/tests/safeds/data/tabular/containers/_cell/test_gt.py new file mode 100644 index 000000000..2d7cde7be --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_gt.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, False), + (3, 1.5, True), + (1.5, 3, False), + (1.5, 1.5, False), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeGreaterThan: + def test_dunder_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell > value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 < cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.gt(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_lt.py b/tests/safeds/data/tabular/containers/_cell/test_lt.py new file mode 100644 index 000000000..83a6f5137 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_lt.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, False), + (3, 1.5, False), + (1.5, 3, True), + (1.5, 1.5, False), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeLessThan: + def test_dunder_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell < value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 > cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.lt(value2), expected) From 267e2afd20ee90c4ac02eff2e503fc5d582eb85d Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:28:44 +0200 Subject: [PATCH 32/49] test: `Cell.le` --- .../data/tabular/containers/_cell/test_le.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_le.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_le.py b/tests/safeds/data/tabular/containers/_cell/test_le.py new file mode 100644 index 000000000..a554f2669 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_le.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, True), + (3, 1.5, False), + (1.5, 3, True), + (1.5, 1.5, True), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeLessThanOrEqual: + def test_dunder_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell <= value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: value2 >= cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: bool): + assert_cell_operation_works(value1, lambda cell: cell.le(value2), expected) From 59018cd65510398905adcefb45e991629cd59838 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:33:20 +0200 Subject: [PATCH 33/49] test: `Cell.mod` --- .../data/tabular/containers/_cell/test_mod.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_mod.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_mod.py b/tests/safeds/data/tabular/containers/_cell/test_mod.py new file mode 100644 index 000000000..181d72c0f --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_mod.py @@ -0,0 +1,45 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 3, 0), + (3, 1.5, 0.0), + (1.5, 3, 1.5), + (1.5, 1.5, 0.0), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputeModulus: + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell % value2, expected) + + def test_named_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell.mod(value2), expected) + + +@pytest.mark.parametrize( + ("value1", "value2", "expected_inverted"), + [ + (3, 3, 0), + (3, 1.5, 1.5), + (1.5, 3, 0.0), + (1.5, 1.5, 0.0), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +def test_dunder_method_inverted_order(value1: float, value2: float, expected_inverted: float): + assert_cell_operation_works(value1, lambda cell: value2 % cell, expected_inverted) From bbd86e6402ffde69fa9ef238b6353a577303ef0f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:35:36 +0200 Subject: [PATCH 34/49] test: `Cell.pow` --- .../data/tabular/containers/_cell/test_pow.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_pow.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_pow.py b/tests/safeds/data/tabular/containers/_cell/test_pow.py new file mode 100644 index 000000000..62400720d --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_pow.py @@ -0,0 +1,29 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 2, 9), + (4, 0.5, 2.0), + (1.5, 2, 2.25), + (2.25, 0.5, 1.5), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) +class TestShouldComputePower: + def test_dunder_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell**value2, expected) + + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value2, lambda cell: value1**cell, expected) + + def test_named_method(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value1, lambda cell: cell.pow(value2), expected) From 38d3a3d77eba172c4aaf07d907ded993fdc07ac7 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:38:25 +0200 Subject: [PATCH 35/49] test: better way to test inverted dunder methods --- .../data/tabular/containers/_cell/test_add.py | 2 +- .../data/tabular/containers/_cell/test_and.py | 2 +- .../data/tabular/containers/_cell/test_div.py | 2 +- .../data/tabular/containers/_cell/test_eq.py | 2 +- .../tabular/containers/_cell/test_floordiv.py | 49 +++++++------------ .../data/tabular/containers/_cell/test_ge.py | 2 +- .../data/tabular/containers/_cell/test_gt.py | 2 +- .../data/tabular/containers/_cell/test_le.py | 2 +- .../data/tabular/containers/_cell/test_lt.py | 2 +- .../data/tabular/containers/_cell/test_mod.py | 22 ++------- .../data/tabular/containers/_cell/test_mul.py | 2 +- .../data/tabular/containers/_cell/test_or.py | 2 +- .../data/tabular/containers/_cell/test_sub.py | 2 +- .../data/tabular/containers/_cell/test_xor.py | 2 +- 14 files changed, 32 insertions(+), 63 deletions(-) diff --git a/tests/safeds/data/tabular/containers/_cell/test_add.py b/tests/safeds/data/tabular/containers/_cell/test_add.py index 238ecfb2f..4cf9a8ae3 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_add.py +++ b/tests/safeds/data/tabular/containers/_cell/test_add.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell + value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): - assert_cell_operation_works(value1, lambda cell: value2 + cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 + cell, expected) def test_named_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell.add(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_and.py b/tests/safeds/data/tabular/containers/_cell/test_and.py index c751355f0..6a5889fa2 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_and.py +++ b/tests/safeds/data/tabular/containers/_cell/test_and.py @@ -33,7 +33,7 @@ def test_dunder_method(self, value1: Any, value2: bool, expected: bool): assert_cell_operation_works(value1, lambda cell: cell & value2, expected) def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 & cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 & cell, expected) def test_named_method(self, value1: Any, value2: bool, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.and_(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_div.py b/tests/safeds/data/tabular/containers/_cell/test_div.py index 94ff3efc5..03b97446e 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_div.py +++ b/tests/safeds/data/tabular/containers/_cell/test_div.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell / value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): - assert_cell_operation_works(value1, lambda cell: value2 / cell, 1 / expected) + assert_cell_operation_works(value2, lambda cell: value1 / cell, expected) def test_named_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell.div(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_eq.py b/tests/safeds/data/tabular/containers/_cell/test_eq.py index 8a7616c70..0ea7398aa 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_eq.py +++ b/tests/safeds/data/tabular/containers/_cell/test_eq.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell == value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 == cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 == cell, expected) def test_named_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.eq(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_floordiv.py b/tests/safeds/data/tabular/containers/_cell/test_floordiv.py index 368465283..d9394782f 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_floordiv.py +++ b/tests/safeds/data/tabular/containers/_cell/test_floordiv.py @@ -3,39 +3,24 @@ from tests.helpers import assert_cell_operation_works +@pytest.mark.parametrize( + ("value1", "value2", "expected"), + [ + (3, 2, 1), + (3, 1.6, 1), + (1.5, 3, 0), + (1.5, 1.4, 1), + ], + ids=[ + "int - int", + "int - float", + "float - int", + "float - float", + ], +) class TestShouldComputeDivision: - @pytest.mark.parametrize( - ("value1", "value2", "expected"), - [ - (3, 2, 1), - (3, 1.6, 1), - (1.5, 3, 0), - (1.5, 1.4, 1), - ], - ids=[ - "int - int", - "int - float", - "float - int", - "float - float", - ], - ) def test_dunder_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell // value2, expected) - @pytest.mark.parametrize( - ("value1", "value2", "expected_inverted"), - [ - (3, 2, 0), - (3, 1.6, 0), - (1.5, 3, 2), - (1.5, 1.4, 0), - ], - ids=[ - "int - int", - "int - float", - "float - int", - "float - float", - ], - ) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected_inverted: float): - assert_cell_operation_works(value1, lambda cell: value2 // cell, expected_inverted) + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value2, lambda cell: value1 // cell, expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_ge.py b/tests/safeds/data/tabular/containers/_cell/test_ge.py index 17256ce10..d0e8aaaaf 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_ge.py +++ b/tests/safeds/data/tabular/containers/_cell/test_ge.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell >= value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 <= cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 >= cell, expected) def test_named_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.ge(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_gt.py b/tests/safeds/data/tabular/containers/_cell/test_gt.py index 2d7cde7be..b8f4bed35 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_gt.py +++ b/tests/safeds/data/tabular/containers/_cell/test_gt.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell > value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 < cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 > cell, expected) def test_named_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.gt(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_le.py b/tests/safeds/data/tabular/containers/_cell/test_le.py index a554f2669..b6aa09e1a 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_le.py +++ b/tests/safeds/data/tabular/containers/_cell/test_le.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell <= value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 >= cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 <= cell, expected) def test_named_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.le(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_lt.py b/tests/safeds/data/tabular/containers/_cell/test_lt.py index 83a6f5137..b3a685ae8 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_lt.py +++ b/tests/safeds/data/tabular/containers/_cell/test_lt.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell < value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 > cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 < cell, expected) def test_named_method(self, value1: float, value2: float, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.lt(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_mod.py b/tests/safeds/data/tabular/containers/_cell/test_mod.py index 181d72c0f..6873664a7 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_mod.py +++ b/tests/safeds/data/tabular/containers/_cell/test_mod.py @@ -22,24 +22,8 @@ class TestShouldComputeModulus: def test_dunder_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell % value2, expected) + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + assert_cell_operation_works(value2, lambda cell: value1 % cell, expected) + def test_named_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell.mod(value2), expected) - - -@pytest.mark.parametrize( - ("value1", "value2", "expected_inverted"), - [ - (3, 3, 0), - (3, 1.5, 1.5), - (1.5, 3, 0.0), - (1.5, 1.5, 0.0), - ], - ids=[ - "int - int", - "int - float", - "float - int", - "float - float", - ], -) -def test_dunder_method_inverted_order(value1: float, value2: float, expected_inverted: float): - assert_cell_operation_works(value1, lambda cell: value2 % cell, expected_inverted) diff --git a/tests/safeds/data/tabular/containers/_cell/test_mul.py b/tests/safeds/data/tabular/containers/_cell/test_mul.py index 8ef843bc8..39719d96a 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_mul.py +++ b/tests/safeds/data/tabular/containers/_cell/test_mul.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell * value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): - assert_cell_operation_works(value1, lambda cell: value2 * cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 * cell, expected) def test_named_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell.mul(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_or.py b/tests/safeds/data/tabular/containers/_cell/test_or.py index 85328e794..249907aed 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_or.py +++ b/tests/safeds/data/tabular/containers/_cell/test_or.py @@ -33,7 +33,7 @@ def test_dunder_method(self, value1: Any, value2: bool, expected: bool): assert_cell_operation_works(value1, lambda cell: cell | value2, expected) def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 | cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 | cell, expected) def test_named_method(self, value1: Any, value2: bool, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.or_(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_sub.py b/tests/safeds/data/tabular/containers/_cell/test_sub.py index 516c1048e..40eb65d98 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_sub.py +++ b/tests/safeds/data/tabular/containers/_cell/test_sub.py @@ -23,7 +23,7 @@ def test_dunder_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell - value2, expected) def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): - assert_cell_operation_works(value1, lambda cell: value2 - cell, -expected) + assert_cell_operation_works(value2, lambda cell: value1 - cell, expected) def test_named_method(self, value1: float, value2: float, expected: float): assert_cell_operation_works(value1, lambda cell: cell.sub(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_xor.py b/tests/safeds/data/tabular/containers/_cell/test_xor.py index ddbe12e4a..c9396f1a6 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_xor.py +++ b/tests/safeds/data/tabular/containers/_cell/test_xor.py @@ -33,7 +33,7 @@ def test_dunder_method(self, value1: Any, value2: bool, expected: bool): assert_cell_operation_works(value1, lambda cell: cell ^ value2, expected) def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 ^ cell, expected) + assert_cell_operation_works(value2, lambda cell: value1 ^ cell, expected) def test_named_method(self, value1: Any, value2: bool, expected: bool): assert_cell_operation_works(value1, lambda cell: cell.xor(value2), expected) From 8a5a25f0969bdd5fff03f399128def3c2d9a4d55 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:46:12 +0200 Subject: [PATCH 36/49] test: `Cell.__sizeof__` --- .../safeds/data/tabular/containers/_cell/test_sizeof.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_sizeof.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_sizeof.py b/tests/safeds/data/tabular/containers/_cell/test_sizeof.py new file mode 100644 index 000000000..acf2a950c --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_sizeof.py @@ -0,0 +1,9 @@ +import sys + +import polars as pl +from safeds.data.tabular.containers._lazy_cell import _LazyCell + + +def test_should_return_size_greater_than_normal_object() -> None: + cell = _LazyCell(pl.col("a")) + assert sys.getsizeof(cell) > sys.getsizeof(object()) From ccc5463f05e3c8c1ce4da8675764e306aad74953 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 12:52:54 +0200 Subject: [PATCH 37/49] test: `Cell._equals` --- .../tabular/containers/_cell/test_equals.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_equals.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_equals.py b/tests/safeds/data/tabular/containers/_cell/test_equals.py new file mode 100644 index 000000000..c24a078e1 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_equals.py @@ -0,0 +1,41 @@ +from typing import Any + +import polars as pl +import pytest +from safeds.data.tabular.containers import Cell, Table +from safeds.data.tabular.containers._lazy_cell import _LazyCell + + +@pytest.mark.parametrize( + ("cell1", "cell2", "expected"), + [ + (_LazyCell(pl.col("a")), _LazyCell(pl.col("a")), True), + (_LazyCell(pl.col("a")), _LazyCell(pl.col("b")), False), + ], + ids=[ + "equal", + "different", + ], +) +def test_should_return_whether_two_cells_are_equal(cell1: Cell, cell2: Cell, expected: bool) -> None: + assert (cell1._equals(cell2)) == expected + + +def test_should_return_true_if_objects_are_identical() -> None: + cell = _LazyCell(pl.col("a")) + assert (cell._equals(cell)) is True + + +@pytest.mark.parametrize( + ("cell", "other"), + [ + (_LazyCell(pl.col("a")), None), + (_LazyCell(pl.col("a")), Table()), + ], + ids=[ + "Cell vs. None", + "Cell vs. Table", + ], +) +def test_should_return_not_implemented_if_other_is_not_cell(cell: Cell, other: Any) -> None: + assert (cell._equals(other)) is NotImplemented From 011736db3f10c9d960ca4f487da34bfd96b4733a Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 13:02:20 +0200 Subject: [PATCH 38/49] test: `Cell._hash` --- .../tabular/containers/_cell/test_hash.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_cell/test_hash.py diff --git a/tests/safeds/data/tabular/containers/_cell/test_hash.py b/tests/safeds/data/tabular/containers/_cell/test_hash.py new file mode 100644 index 000000000..5eb2019fb --- /dev/null +++ b/tests/safeds/data/tabular/containers/_cell/test_hash.py @@ -0,0 +1,24 @@ +import polars as pl +import pytest +from safeds.data.tabular.containers import Cell +from safeds.data.tabular.containers._lazy_cell import _LazyCell + + +def test_should_be_deterministic() -> None: + cell = _LazyCell(pl.col("a")) + assert hash(cell) == 7139977585477665635 + + +@pytest.mark.parametrize( + ("cell1", "cell2", "expected"), + [ + (_LazyCell(pl.col("a")), _LazyCell(pl.col("a")), True), + (_LazyCell(pl.col("a")), _LazyCell(pl.col("b")), False), + ], + ids=[ + "equal", + "different", + ], +) +def test_should_be_good_hash(cell1: Cell, cell2: Cell, expected: bool) -> None: + assert (hash(cell1) == hash(cell2)) == expected From 9a2a4a537d2c716ecd53ccac763d77798a0bab5c Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 13:04:26 +0200 Subject: [PATCH 39/49] test: simplify test of `Column.__hash__` --- .../tabular/containers/_column/test_hash.py | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/tests/safeds/data/tabular/containers/_column/test_hash.py b/tests/safeds/data/tabular/containers/_column/test_hash.py index e388de15e..0f6900bcd 100644 --- a/tests/safeds/data/tabular/containers/_column/test_hash.py +++ b/tests/safeds/data/tabular/containers/_column/test_hash.py @@ -18,33 +18,22 @@ def test_should_be_deterministic(column: Column, expected: int) -> None: @pytest.mark.parametrize( - ("column1", "column2"), + ("column1", "column2", "expected"), [ - (Column("a"), Column("a")), - (Column("a", [1, 2, 3]), Column("a", [1, 2, 3])), - ], - ids=[ - "empty", - "non-empty", - ], -) -def test_should_return_same_hash_for_equal_columns(column1: Column, column2: Column) -> None: - assert hash(column1) == hash(column2) - - -@pytest.mark.parametrize( - ("column1", "column2"), - [ - (Column("a"), Column("b")), - (Column("a", [1, 2, 3]), Column("a", [1, 2])), - (Column("a", [1, 2, 3]), Column("a", ["1", "2", "3"])), + (Column("a"), Column("a"), True), + (Column("a", [1, 2, 3]), Column("a", [1, 2, 3]), True), + (Column("a"), Column("b"), False), + (Column("a", [1, 2, 3]), Column("a", [1, 2]), False), + (Column("a", [1, 2, 3]), Column("a", ["1", "2", "3"]), False), # We don't use the column values in the hash calculation ], ids=[ + "equal empty", + "equal non-empty", "different names", "different lengths", "different types", ], ) -def test_should_ideally_return_different_hash_for_unequal_columns(column1: Column, column2: Column) -> None: - assert hash(column1) != hash(column2) +def test_should_be_good_hash(column1: Column, column2: Column, expected: bool) -> None: + assert (hash(column1) == hash(column2)) == expected From 53aa165bb14e3aeab67b932e4aba3a984533f5a9 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 13:16:23 +0200 Subject: [PATCH 40/49] feat: `StringCell.index_of` --- .../tabular/containers/_lazy_string_cell.py | 3 ++ .../data/tabular/containers/_string_cell.py | 40 ++++++++++++++----- .../containers/_string_cell/test_index_of.py | 22 ++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_index_of.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index f8c015e20..042356f84 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -43,6 +43,9 @@ def length(self, optimize_for_ascii: bool = False) -> Cell[int]: def ends_with(self, suffix: str) -> Cell[bool]: return _LazyCell(self._expression.str.ends_with(suffix)) + def index_of(self, substring: str) -> Cell[int | None]: + return _LazyCell(self._expression.str.find(substring, literal=True)) + def starts_with(self, prefix: str) -> Cell[bool]: return _LazyCell(self._expression.str.starts_with(prefix)) diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 1d11bbb70..9fd94968e 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -67,6 +67,29 @@ def ends_with(self, suffix: str) -> Cell[bool]: 1 """ + @abstractmethod + def index_of(self, substring: str) -> Cell[int | None]: + """ + Get the index of the first occurrence of the substring in the string value in the cell. + + Parameters + ---------- + substring: + The substring to search for. + + Returns + ------- + index_of: + The index of the first occurrence of the substring. If the substring is not found, None is returned. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.transform(lambda cell: cell.string.index_of("b")) + [1, 0, None] + """ + @abstractmethod def length(self, *, optimize_for_ascii: bool = False) -> Cell[int]: """ @@ -204,13 +227,10 @@ def trim_start(self) -> Cell[str]: ["", "abc", "abc ", "abc "] """ - # indexOf - # lastIndexOf - # replace - # split - # substring - # toFloat - # toInt - # toDate - # toTime - # toDatetime + # replace -> replace/replace_many/replace_all + # substring -> slice + # toFloat -> to_decimal + # toInt -> to_integer + # toDate -> to_date + # toTime -> to_time + # toDatetime -> to_datetime diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py b/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py new file mode 100644 index 000000000..d59390f70 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py @@ -0,0 +1,22 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "substring", "expected"), + [ + ("", "a", None), + ("abc", "", 0), + ("abc", "b", 1), + ("abc", "d", None), + ], + ids=[ + "empty string", + "empty substring", + "contained", + "not contained", + ], +) +def test_should_return_index_of_first_occurrence_of_substring(string: str, substring: str, expected: bool) -> None: + assert_cell_operation_works(string, lambda cell: cell.string.index_of(substring), expected) From 3e38a9ec003b00ecba7f5ae7bb28c8e117858a68 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 15:34:13 +0200 Subject: [PATCH 41/49] feat: `StringCell.to_float` and `StringCell.to_int` --- .../tabular/containers/_lazy_string_cell.py | 8 ++++ .../data/tabular/containers/_string_cell.py | 47 ++++++++++++++++++- .../containers/_string_cell/test_to_float.py | 24 ++++++++++ .../containers/_string_cell/test_to_int.py | 26 ++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_to_float.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_to_int.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index 042356f84..0d6a82503 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -49,6 +49,14 @@ def index_of(self, substring: str) -> Cell[int | None]: def starts_with(self, prefix: str) -> Cell[bool]: return _LazyCell(self._expression.str.starts_with(prefix)) + def to_int(self, *, base: int = 10) -> Cell[int | None]: + return _LazyCell(self._expression.str.to_integer(base=base, strict=False)) + + def to_float(self) -> Cell[float | None]: + import polars as pl + + return _LazyCell(self._expression.cast(pl.Float64, strict=False)) + def to_lowercase(self) -> Cell[str]: return _LazyCell(self._expression.str.to_lowercase()) diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 9fd94968e..78519f982 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -137,6 +137,51 @@ def starts_with(self, prefix: str) -> Cell[bool]: 1 """ + @abstractmethod + def to_float(self) -> Cell[float | None]: + """ + Convert the string value in the cell to a float. + + Returns + ------- + float: + The float value. If the string cannot be converted to a float, None is returned. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["1", "3.4", "5.6", "abc"]) + >>> column.transform(lambda cell: cell.string.to_float()) + [1.0, 3.4, 5.6, None] + """ + + @abstractmethod + def to_int(self, *, base: int = 10) -> Cell[int | None]: + """ + Convert the string value in the cell to an integer. + + Parameters + ---------- + base: + The base of the integer. + + Returns + ------- + int: + The integer value. If the string cannot be converted to an integer, None is returned. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column1 = Column("example", ["1", "2", "3", "abc"]) + >>> column1.transform(lambda cell: cell.string.to_int()) + [1, 2, 3, None] + + >>> column2 = Column("example", ["1", "10", "11", "abc"]) + >>> column2.transform(lambda cell: cell.string.to_int(base=2)) + [1, 2, 3, None] + """ + @abstractmethod def to_lowercase(self) -> Cell[str]: """ @@ -229,8 +274,6 @@ def trim_start(self) -> Cell[str]: # replace -> replace/replace_many/replace_all # substring -> slice - # toFloat -> to_decimal - # toInt -> to_integer # toDate -> to_date # toTime -> to_time # toDatetime -> to_datetime diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py new file mode 100644 index 000000000..09a8f2477 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py @@ -0,0 +1,24 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", None), + ("11", 11), + ("11.5", 11.5), + ("10e-1", 1.0), + ("abc", None), + ], + ids=[ + "empty", + "integer", + "float", + "scientific notation", + "invalid string", + ], +) +def test_should_parse_float(string: str, expected: bool) -> None: + assert_cell_operation_works(string, lambda cell: cell.string.to_float(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py new file mode 100644 index 000000000..5f78ee4fa --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py @@ -0,0 +1,26 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "base", "expected"), + [ + ("", 10, None), + ("11", 10, 11), + ("11", 2, 3), + ("abc", 10, None), + ], + ids=[ + "empty", + "11 base 10", + "11 base 2", + "invalid string", + ], +) +def test_should_parse_integer(string: str, base: int, expected: bool) -> None: + assert_cell_operation_works( + string, + lambda cell: cell.string.to_int(base=base), + expected, + ) From f64d120c5714a5661f17b2641500429bd01d8b27 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 16:09:09 +0200 Subject: [PATCH 42/49] feat: `StringCell.to_date` and `StringCell.to_datetime` --- .../tabular/containers/_lazy_string_cell.py | 7 ++++ .../data/tabular/containers/_string_cell.py | 41 +++++++++++++++++-- .../containers/_string_cell/test_to_date.py | 22 ++++++++++ .../_string_cell/test_to_datetime.py | 22 ++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_to_date.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index 0d6a82503..aa5710950 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime from typing import TYPE_CHECKING from safeds._utils import _structural_hash @@ -49,6 +50,12 @@ def index_of(self, substring: str) -> Cell[int | None]: def starts_with(self, prefix: str) -> Cell[bool]: return _LazyCell(self._expression.str.starts_with(prefix)) + def to_date(self) -> Cell[datetime.date | None]: + return _LazyCell(self._expression.str.to_date(format="%F", strict=False)) + + def to_datetime(self) -> Cell[datetime.datetime | None]: + return _LazyCell(self._expression.str.to_datetime(format="%+", strict=False)) + def to_int(self, *, base: int = 10) -> Cell[int | None]: return _LazyCell(self._expression.str.to_integer(base=base, strict=False)) diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 78519f982..7f686fe4a 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: + import datetime + from safeds.data.tabular.containers import Cell @@ -137,6 +139,42 @@ def starts_with(self, prefix: str) -> Cell[bool]: 1 """ + @abstractmethod + def to_date(self) -> Cell[datetime.date | None]: + """ + Convert the string value in the cell to a date. Requires the string to be in the ISO 8601 format. + + Returns + ------- + date: + The date value. If the string cannot be converted to a date, None is returned. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["2021-01-01", "2021-02-01", "abc"]) + >>> column.transform(lambda cell: cell.string.to_date()) + ["2021-01-01", "2021-02-01", None] + """ + + @abstractmethod + def to_datetime(self) -> Cell[datetime.datetime | None]: + """ + Convert the string value in the cell to a datetime. Requires the string to be in the ISO 8601 format. + + Returns + ------- + datetime: + The datetime value. If the string cannot be converted to a datetime, None is returned. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["2021-01-01T00:00:00", "2021-02-01T00:00:00", "abc"]) + >>> column.transform(lambda cell: cell.string.to_datetime()) + ["2021-01-01 00:00:00", "2021-02-01 00:00:00", None] + """ + @abstractmethod def to_float(self) -> Cell[float | None]: """ @@ -274,6 +312,3 @@ def trim_start(self) -> Cell[str]: # replace -> replace/replace_many/replace_all # substring -> slice - # toDate -> to_date - # toTime -> to_time - # toDatetime -> to_datetime diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py new file mode 100644 index 000000000..ba5a4935c --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py @@ -0,0 +1,22 @@ +import datetime + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", None), + ("2022-01-09", datetime.date(2022, 1, 9)), + ("abc", None), + ], + ids=[ + "empty", + "ISO date", + "invalid string", + ], +) +def test_should_parse_date(string: str, expected: bool) -> None: + assert_cell_operation_works(string, lambda cell: cell.string.to_date(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py new file mode 100644 index 000000000..fef0df4fb --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py @@ -0,0 +1,22 @@ +import datetime + +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "expected"), + [ + ("", None), + ("2022-01-09T23:29:01Z", datetime.datetime(2022, 1, 9, 23, 29, 1, tzinfo=datetime.UTC)), + ("abc", None), + ], + ids=[ + "empty", + "ISO datetime", + "invalid string", + ], +) +def test_should_parse_datetimes(string: str, expected: bool) -> None: + assert_cell_operation_works(string, lambda cell: cell.string.to_datetime(), expected) From 4102a54bd74ec0d30702fe8ff751c907b72567c1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 16:16:45 +0200 Subject: [PATCH 43/49] test: `StringCell._equals`, `StringCell.__hash__`, `StringCell.__sizeof__` --- .../data/tabular/containers/_string_cell.py | 12 ++++++ .../containers/_string_cell/test_equals.py | 41 +++++++++++++++++++ .../containers/_string_cell/test_hash.py | 24 +++++++++++ .../containers/_string_cell/test_sizeof.py | 9 ++++ 4 files changed, 86 insertions(+) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_equals.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_hash.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_sizeof.py diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 7f686fe4a..958adf316 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -310,5 +310,17 @@ def trim_start(self) -> Cell[str]: ["", "abc", "abc ", "abc "] """ + # ------------------------------------------------------------------------------------------------------------------ + # Internal + # ------------------------------------------------------------------------------------------------------------------ + + @abstractmethod + def _equals(self, other: object) -> bool: + """ + Check if this cell is equal to another object. + + This method is needed because the `__eq__` method is used for element-wise comparisons. + """ + # replace -> replace/replace_many/replace_all # substring -> slice diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_equals.py b/tests/safeds/data/tabular/containers/_string_cell/test_equals.py new file mode 100644 index 000000000..2f0ad4ad1 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_equals.py @@ -0,0 +1,41 @@ +from typing import Any + +import polars as pl +import pytest +from safeds.data.tabular.containers import StringCell, Table +from safeds.data.tabular.containers._lazy_string_cell import _LazyStringCell + + +@pytest.mark.parametrize( + ("cell1", "cell2", "expected"), + [ + (_LazyStringCell(pl.col("a")), _LazyStringCell(pl.col("a")), True), + (_LazyStringCell(pl.col("a")), _LazyStringCell(pl.col("b")), False), + ], + ids=[ + "equal", + "different", + ], +) +def test_should_return_whether_two_cells_are_equal(cell1: StringCell, cell2: StringCell, expected: bool) -> None: + assert (cell1._equals(cell2)) == expected + + +def test_should_return_true_if_objects_are_identical() -> None: + cell = _LazyStringCell(pl.col("a")) + assert (cell._equals(cell)) is True + + +@pytest.mark.parametrize( + ("cell", "other"), + [ + (_LazyStringCell(pl.col("a")), None), + (_LazyStringCell(pl.col("a")), Table()), + ], + ids=[ + "Cell vs. None", + "Cell vs. Table", + ], +) +def test_should_return_not_implemented_if_other_is_not_cell(cell: StringCell, other: Any) -> None: + assert (cell._equals(other)) is NotImplemented diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_hash.py b/tests/safeds/data/tabular/containers/_string_cell/test_hash.py new file mode 100644 index 000000000..db9837178 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_hash.py @@ -0,0 +1,24 @@ +import polars as pl +import pytest +from safeds.data.tabular.containers import StringCell +from safeds.data.tabular.containers._lazy_string_cell import _LazyStringCell + + +def test_should_be_deterministic() -> None: + cell = _LazyStringCell(pl.col("a")) + assert hash(cell) == 7139977585477665635 + + +@pytest.mark.parametrize( + ("cell1", "cell2", "expected"), + [ + (_LazyStringCell(pl.col("a")), _LazyStringCell(pl.col("a")), True), + (_LazyStringCell(pl.col("a")), _LazyStringCell(pl.col("b")), False), + ], + ids=[ + "equal", + "different", + ], +) +def test_should_be_good_hash(cell1: StringCell, cell2: StringCell, expected: bool) -> None: + assert (hash(cell1) == hash(cell2)) == expected diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_sizeof.py b/tests/safeds/data/tabular/containers/_string_cell/test_sizeof.py new file mode 100644 index 000000000..43df6affb --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_sizeof.py @@ -0,0 +1,9 @@ +import sys + +import polars as pl +from safeds.data.tabular.containers._lazy_string_cell import _LazyStringCell + + +def test_should_return_size_greater_than_normal_object() -> None: + cell = _LazyStringCell(pl.col("a")) + assert sys.getsizeof(cell) > sys.getsizeof(object()) From d73aef2963a811e20411785a378517592f1fe11e Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 16:46:29 +0200 Subject: [PATCH 44/49] feat: `StringCell.replace`, `StringCell.substring` --- .../tabular/containers/_lazy_string_cell.py | 9 +++ .../data/tabular/containers/_string_cell.py | 59 ++++++++++++++++++- .../containers/_string_cell/test_replace.py | 24 ++++++++ .../containers/_string_cell/test_substring.py | 36 +++++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_replace.py create mode 100644 tests/safeds/data/tabular/containers/_string_cell/test_substring.py diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index aa5710950..a06aa0dea 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from safeds._utils import _structural_hash +from safeds._validation import _check_bounds, _ClosedBound from ._lazy_cell import _LazyCell from ._string_cell import StringCell @@ -47,9 +48,17 @@ def ends_with(self, suffix: str) -> Cell[bool]: def index_of(self, substring: str) -> Cell[int | None]: return _LazyCell(self._expression.str.find(substring, literal=True)) + def replace(self, old: str, new: str) -> Cell[str]: + return _LazyCell(self._expression.str.replace_all(old, new, literal=True)) + def starts_with(self, prefix: str) -> Cell[bool]: return _LazyCell(self._expression.str.starts_with(prefix)) + def substring(self, start: int = 0, length: int | None = None) -> Cell[str]: + _check_bounds("length", length, lower_bound=_ClosedBound(0)) + + return _LazyCell(self._expression.str.slice(start, length)) + def to_date(self) -> Cell[datetime.date | None]: return _LazyCell(self._expression.str.to_date(format="%F", strict=False)) diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 958adf316..f7202a619 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -116,6 +116,31 @@ def length(self, *, optimize_for_ascii: bool = False) -> Cell[int]: [0, 1, 3] """ + @abstractmethod + def replace(self, old: str, new: str) -> Cell[str]: + """ + Replace occurrences of the old substring with the new substring in the string value in the cell. + + Parameters + ---------- + old: + The substring to replace. + new: + The substring to replace with. + + Returns + ------- + replaced_string: + The string value with the occurrences replaced. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["ab", "bc", "cd"]) + >>> column.transform(lambda cell: cell.string.replace("b", "z")) + ["az", "zc", "cd"] + """ + @abstractmethod def starts_with(self, prefix: str) -> Cell[bool]: """ @@ -139,6 +164,37 @@ def starts_with(self, prefix: str) -> Cell[bool]: 1 """ + @abstractmethod + def substring(self, start: int = 0, length: int | None = None) -> Cell[str]: + """ + Get a substring of the string value in the cell. + + Parameters + ---------- + start: + The start index of the substring. + length: + The length of the substring. If None, the slice contains all rows starting from `start`. Must greater than + or equal to 0. + + Returns + ------- + substring: + The substring of the string value. + + Raises + ------ + OutOfBoundsError + If length is less than 0. + + Examples + -------- + >>> from safeds.data.tabular.containers import Column + >>> column = Column("example", ["abc", "def", "ghi"]) + >>> column.transform(lambda cell: cell.string.substring(1, 2)) + ["bc", "ef", "hi"] + """ + @abstractmethod def to_date(self) -> Cell[datetime.date | None]: """ @@ -321,6 +377,3 @@ def _equals(self, other: object) -> bool: This method is needed because the `__eq__` method is used for element-wise comparisons. """ - - # replace -> replace/replace_many/replace_all - # substring -> slice diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_replace.py b/tests/safeds/data/tabular/containers/_string_cell/test_replace.py new file mode 100644 index 000000000..514860afd --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_replace.py @@ -0,0 +1,24 @@ +import pytest + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "old", "new", "expected"), + [ + ("", "a", "b", ""), + ("abc", "", "d", "dadbdcd"), + ("abc", "a", "", "bc"), + ("abc", "d", "e", "abc"), + ("aba", "a", "d", "dbd"), + ], + ids=[ + "empty string", + "empty old", + "empty new", + "no occurrences", + "replace all occurrences", + ], +) +def test_should_replace_all_occurrences_of_old_with_new(string: str, old: str, new: str, expected: str) -> None: + assert_cell_operation_works(string, lambda cell: cell.string.replace(old, new), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_substring.py b/tests/safeds/data/tabular/containers/_string_cell/test_substring.py new file mode 100644 index 000000000..2cf6ff2d0 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_string_cell/test_substring.py @@ -0,0 +1,36 @@ +import pytest +from safeds.exceptions import OutOfBoundsError + +from tests.helpers import assert_cell_operation_works + + +@pytest.mark.parametrize( + ("string", "start", "length", "expected"), + [ + ("", 0, None, ""), + ("abc", 0, None, "abc"), + ("abc", 1, None, "bc"), + ("abc", 10, None, ""), + ("abc", -1, None, "c"), + ("abc", -10, None, "abc"), + ("abc", 0, 1, "a"), + ("abc", 0, 10, "abc"), + ], + ids=[ + "empty", + "full string", + "positive start in bounds", + "positive start out of bounds", + "negative start in bounds", + "negative start out of bounds", + "positive length in bounds", + "positive length out of bounds", + ], +) +def test_should_return_substring(string: str, start: int, length: int | None, expected: str) -> None: + assert_cell_operation_works(string, lambda cell: cell.string.substring(start, length), expected) + + +def test_should_raise_if_length_is_negative() -> None: + with pytest.raises(OutOfBoundsError): + assert_cell_operation_works("abc", lambda cell: cell.string.substring(length=-1), None) From 6c4a360c3895db9d6201b0ccf8b723f90e854e9c Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 16:49:55 +0200 Subject: [PATCH 45/49] feat: rename `string` to `str` Since it's needed often and is used in an expression context, it needs a short name. --- src/safeds/data/tabular/containers/_cell.py | 2 +- .../data/tabular/containers/_lazy_cell.py | 2 +- .../data/tabular/containers/_string_cell.py | 36 +++++++++---------- .../containers/_string_cell/test_contains.py | 2 +- .../containers/_string_cell/test_ends_with.py | 2 +- .../containers/_string_cell/test_index_of.py | 2 +- .../containers/_string_cell/test_length.py | 2 +- .../containers/_string_cell/test_replace.py | 2 +- .../_string_cell/test_starts_with.py | 2 +- .../containers/_string_cell/test_substring.py | 4 +-- .../containers/_string_cell/test_to_date.py | 2 +- .../_string_cell/test_to_datetime.py | 2 +- .../containers/_string_cell/test_to_float.py | 2 +- .../containers/_string_cell/test_to_int.py | 2 +- .../_string_cell/test_to_lowercase.py | 2 +- .../_string_cell/test_to_uppercase.py | 2 +- .../containers/_string_cell/test_trim.py | 2 +- .../containers/_string_cell/test_trim_end.py | 2 +- .../_string_cell/test_trim_start.py | 2 +- 19 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/safeds/data/tabular/containers/_cell.py b/src/safeds/data/tabular/containers/_cell.py index aa9543c7d..0201736b9 100644 --- a/src/safeds/data/tabular/containers/_cell.py +++ b/src/safeds/data/tabular/containers/_cell.py @@ -142,7 +142,7 @@ def __sizeof__(self) -> int: ... @property @abstractmethod - def string(self) -> StringCell: + def str(self) -> StringCell: """Namespace for operations on strings.""" # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_lazy_cell.py b/src/safeds/data/tabular/containers/_lazy_cell.py index 371b4e298..0eaf9ab11 100644 --- a/src/safeds/data/tabular/containers/_lazy_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_cell.py @@ -181,7 +181,7 @@ def __sizeof__(self) -> int: # ------------------------------------------------------------------------------------------------------------------ @property - def string(self) -> StringCell: + def str(self) -> StringCell: from ._lazy_string_cell import _LazyStringCell # circular import return _LazyStringCell(self._expression) diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index f7202a619..49fc8c613 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -19,7 +19,7 @@ class StringCell(ABC): -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.transform(lambda cell: cell.string.to_uppercase()) + >>> column.transform(lambda cell: cell.str.to_uppercase()) ["AB", "BC", "CD"] """ @@ -42,7 +42,7 @@ def contains(self, substring: str) -> Cell[bool]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.count_if(lambda cell: cell.string.contains("b")) + >>> column.count_if(lambda cell: cell.str.contains("b")) 2 """ @@ -65,7 +65,7 @@ def ends_with(self, suffix: str) -> Cell[bool]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.count_if(lambda cell: cell.string.ends_with("c")) + >>> column.count_if(lambda cell: cell.str.ends_with("c")) 1 """ @@ -88,7 +88,7 @@ def index_of(self, substring: str) -> Cell[int | None]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.transform(lambda cell: cell.string.index_of("b")) + >>> column.transform(lambda cell: cell.str.index_of("b")) [1, 0, None] """ @@ -112,7 +112,7 @@ def length(self, *, optimize_for_ascii: bool = False) -> Cell[int]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", "a", "abc"]) - >>> column.transform(lambda cell: cell.string.length()) + >>> column.transform(lambda cell: cell.str.length()) [0, 1, 3] """ @@ -137,7 +137,7 @@ def replace(self, old: str, new: str) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.transform(lambda cell: cell.string.replace("b", "z")) + >>> column.transform(lambda cell: cell.str.replace("b", "z")) ["az", "zc", "cd"] """ @@ -160,7 +160,7 @@ def starts_with(self, prefix: str) -> Cell[bool]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.count_if(lambda cell: cell.string.starts_with("a")) + >>> column.count_if(lambda cell: cell.str.starts_with("a")) 1 """ @@ -191,7 +191,7 @@ def substring(self, start: int = 0, length: int | None = None) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["abc", "def", "ghi"]) - >>> column.transform(lambda cell: cell.string.substring(1, 2)) + >>> column.transform(lambda cell: cell.str.substring(1, 2)) ["bc", "ef", "hi"] """ @@ -209,7 +209,7 @@ def to_date(self) -> Cell[datetime.date | None]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["2021-01-01", "2021-02-01", "abc"]) - >>> column.transform(lambda cell: cell.string.to_date()) + >>> column.transform(lambda cell: cell.str.to_date()) ["2021-01-01", "2021-02-01", None] """ @@ -227,7 +227,7 @@ def to_datetime(self) -> Cell[datetime.datetime | None]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["2021-01-01T00:00:00", "2021-02-01T00:00:00", "abc"]) - >>> column.transform(lambda cell: cell.string.to_datetime()) + >>> column.transform(lambda cell: cell.str.to_datetime()) ["2021-01-01 00:00:00", "2021-02-01 00:00:00", None] """ @@ -245,7 +245,7 @@ def to_float(self) -> Cell[float | None]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["1", "3.4", "5.6", "abc"]) - >>> column.transform(lambda cell: cell.string.to_float()) + >>> column.transform(lambda cell: cell.str.to_float()) [1.0, 3.4, 5.6, None] """ @@ -268,11 +268,11 @@ def to_int(self, *, base: int = 10) -> Cell[int | None]: -------- >>> from safeds.data.tabular.containers import Column >>> column1 = Column("example", ["1", "2", "3", "abc"]) - >>> column1.transform(lambda cell: cell.string.to_int()) + >>> column1.transform(lambda cell: cell.str.to_int()) [1, 2, 3, None] >>> column2 = Column("example", ["1", "10", "11", "abc"]) - >>> column2.transform(lambda cell: cell.string.to_int(base=2)) + >>> column2.transform(lambda cell: cell.str.to_int(base=2)) [1, 2, 3, None] """ @@ -290,7 +290,7 @@ def to_lowercase(self) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["AB", "BC", "CD"]) - >>> column.transform(lambda cell: cell.string.to_lowercase()) + >>> column.transform(lambda cell: cell.str.to_lowercase()) ["ab", "bc", "cd"] """ @@ -308,7 +308,7 @@ def to_uppercase(self) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) - >>> column.transform(lambda cell: cell.string.to_uppercase()) + >>> column.transform(lambda cell: cell.str.to_uppercase()) ["AB", "BC", "CD"] """ @@ -326,7 +326,7 @@ def trim(self) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", " abc", "abc ", " abc "]) - >>> column.transform(lambda cell: cell.string.trim()) + >>> column.transform(lambda cell: cell.str.trim()) ["", "abc", "abc", "abc"] """ @@ -344,7 +344,7 @@ def trim_end(self) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", " abc", "abc ", " abc "]) - >>> column.transform(lambda cell: cell.string.trim_end()) + >>> column.transform(lambda cell: cell.str.trim_end()) ["", " abc", "abc", " abc"] """ @@ -362,7 +362,7 @@ def trim_start(self) -> Cell[str]: -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", " abc", "abc ", " abc "]) - >>> column.transform(lambda cell: cell.string.trim_start()) + >>> column.transform(lambda cell: cell.str.trim_start()) ["", "abc", "abc ", "abc "] """ diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_contains.py b/tests/safeds/data/tabular/containers/_string_cell/test_contains.py index d826bf5c8..bac8e354a 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_contains.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_contains.py @@ -19,4 +19,4 @@ ], ) def test_should_check_whether_string_contains_substring(string: str, substring: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.contains(substring), expected) + assert_cell_operation_works(string, lambda cell: cell.str.contains(substring), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py b/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py index 02ef18d32..78102c900 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_ends_with.py @@ -19,4 +19,4 @@ ], ) def test_should_check_whether_string_ends_with_prefix(string: str, suffix: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.ends_with(suffix), expected) + assert_cell_operation_works(string, lambda cell: cell.str.ends_with(suffix), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py b/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py index d59390f70..84e79ad1b 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_index_of.py @@ -19,4 +19,4 @@ ], ) def test_should_return_index_of_first_occurrence_of_substring(string: str, substring: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.index_of(substring), expected) + assert_cell_operation_works(string, lambda cell: cell.str.index_of(substring), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_length.py b/tests/safeds/data/tabular/containers/_string_cell/test_length.py index 95b948685..5b7f0370b 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_length.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_length.py @@ -21,6 +21,6 @@ def test_should_return_number_of_characters(string: str, optimize_for_ascii: bool, expected: bool) -> None: assert_cell_operation_works( string, - lambda cell: cell.string.length(optimize_for_ascii=optimize_for_ascii), + lambda cell: cell.str.length(optimize_for_ascii=optimize_for_ascii), expected, ) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_replace.py b/tests/safeds/data/tabular/containers/_string_cell/test_replace.py index 514860afd..f1f32c07a 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_replace.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_replace.py @@ -21,4 +21,4 @@ ], ) def test_should_replace_all_occurrences_of_old_with_new(string: str, old: str, new: str, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.replace(old, new), expected) + assert_cell_operation_works(string, lambda cell: cell.str.replace(old, new), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py b/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py index ebc6ec4d6..7d402cd0b 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_starts_with.py @@ -19,4 +19,4 @@ ], ) def test_should_check_whether_string_start_with_prefix(string: str, prefix: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.starts_with(prefix), expected) + assert_cell_operation_works(string, lambda cell: cell.str.starts_with(prefix), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_substring.py b/tests/safeds/data/tabular/containers/_string_cell/test_substring.py index 2cf6ff2d0..1305e76b3 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_substring.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_substring.py @@ -28,9 +28,9 @@ ], ) def test_should_return_substring(string: str, start: int, length: int | None, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.substring(start, length), expected) + assert_cell_operation_works(string, lambda cell: cell.str.substring(start, length), expected) def test_should_raise_if_length_is_negative() -> None: with pytest.raises(OutOfBoundsError): - assert_cell_operation_works("abc", lambda cell: cell.string.substring(length=-1), None) + assert_cell_operation_works("abc", lambda cell: cell.str.substring(length=-1), None) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py index ba5a4935c..677438e0a 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_date.py @@ -19,4 +19,4 @@ ], ) def test_should_parse_date(string: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.to_date(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.to_date(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py index fef0df4fb..4c96d03d0 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_datetime.py @@ -19,4 +19,4 @@ ], ) def test_should_parse_datetimes(string: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.to_datetime(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.to_datetime(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py index 09a8f2477..f9ea7ef9c 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_float.py @@ -21,4 +21,4 @@ ], ) def test_should_parse_float(string: str, expected: bool) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.to_float(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.to_float(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py index 5f78ee4fa..b4b3256cc 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_int.py @@ -21,6 +21,6 @@ def test_should_parse_integer(string: str, base: int, expected: bool) -> None: assert_cell_operation_works( string, - lambda cell: cell.string.to_int(base=base), + lambda cell: cell.str.to_int(base=base), expected, ) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py index f1266a36a..f4c880761 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_lowercase.py @@ -15,4 +15,4 @@ ], ) def test_should_lowercase_a_string(string: str, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.to_lowercase(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.to_lowercase(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py b/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py index f7a7e85fc..cfb14c7d2 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_to_uppercase.py @@ -15,4 +15,4 @@ ], ) def test_should_uppercase_a_string(string: str, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.to_uppercase(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.to_uppercase(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim.py index cbd5c9ed6..2b2101e4e 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_trim.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim.py @@ -21,4 +21,4 @@ ], ) def test_should_remove_whitespace_prefix_and_suffix(string: str, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.trim(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.trim(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py index 0fe242079..af0cd88dc 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim_end.py @@ -21,4 +21,4 @@ ], ) def test_should_remove_whitespace_suffix(string: str, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.trim_end(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.trim_end(), expected) diff --git a/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py b/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py index 10477f8cb..6b487f6e7 100644 --- a/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py +++ b/tests/safeds/data/tabular/containers/_string_cell/test_trim_start.py @@ -21,4 +21,4 @@ ], ) def test_should_remove_whitespace_prefix(string: str, expected: str) -> None: - assert_cell_operation_works(string, lambda cell: cell.string.trim_start(), expected) + assert_cell_operation_works(string, lambda cell: cell.str.trim_start(), expected) From e35c755d5d5305c8f1c73ea4edd3ed1eff636563 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 16:56:08 +0200 Subject: [PATCH 46/49] docs: fix example output --- src/resources/from_json_file.json | 7 +- src/resources/from_json_file_2.json | 6 - src/resources/to_json_file.json | 2 +- src/resources/to_json_file_2.json | 6 - .../data/tabular/containers/_string_cell.py | 160 ++++++++++++++++-- src/safeds/data/tabular/containers/_table.py | 4 +- 6 files changed, 152 insertions(+), 33 deletions(-) delete mode 100644 src/resources/from_json_file_2.json delete mode 100644 src/resources/to_json_file_2.json diff --git a/src/resources/from_json_file.json b/src/resources/from_json_file.json index 5965ef149..5cd814134 100644 --- a/src/resources/from_json_file.json +++ b/src/resources/from_json_file.json @@ -1 +1,6 @@ -{ "a": { "0": 1, "1": 2, "2": 3 }, "b": { "0": 4, "1": 5, "2": 6 } } +{ + "columns": [ + { "name": "a", "datatype": "Int64", "bit_settings": "", "values": [1, 2, 3] }, + { "name": "b", "datatype": "Int64", "bit_settings": "", "values": [4, 5, 6] } + ] +} diff --git a/src/resources/from_json_file_2.json b/src/resources/from_json_file_2.json deleted file mode 100644 index 5cd814134..000000000 --- a/src/resources/from_json_file_2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "columns": [ - { "name": "a", "datatype": "Int64", "bit_settings": "", "values": [1, 2, 3] }, - { "name": "b", "datatype": "Int64", "bit_settings": "", "values": [4, 5, 6] } - ] -} diff --git a/src/resources/to_json_file.json b/src/resources/to_json_file.json index 5965ef149..29d04896f 100644 --- a/src/resources/to_json_file.json +++ b/src/resources/to_json_file.json @@ -1 +1 @@ -{ "a": { "0": 1, "1": 2, "2": 3 }, "b": { "0": 4, "1": 5, "2": 6 } } +{"columns":[{"name":"a","datatype":"Int64","bit_settings":"","values":[1,2,3]},{"name":"b","datatype":"Int64","bit_settings":"","values":[4,5,6]}]} \ No newline at end of file diff --git a/src/resources/to_json_file_2.json b/src/resources/to_json_file_2.json deleted file mode 100644 index 5cd814134..000000000 --- a/src/resources/to_json_file_2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "columns": [ - { "name": "a", "datatype": "Int64", "bit_settings": "", "values": [1, 2, 3] }, - { "name": "b", "datatype": "Int64", "bit_settings": "", "values": [4, 5, 6] } - ] -} diff --git a/src/safeds/data/tabular/containers/_string_cell.py b/src/safeds/data/tabular/containers/_string_cell.py index 49fc8c613..e47a41444 100644 --- a/src/safeds/data/tabular/containers/_string_cell.py +++ b/src/safeds/data/tabular/containers/_string_cell.py @@ -13,14 +13,22 @@ class StringCell(ABC): """ Namespace for operations on strings. - This class cannot be instantiated directly. It can only be accessed using the `string` attribute of a cell. + This class cannot be instantiated directly. It can only be accessed using the `str` attribute of a cell. Examples -------- >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) >>> column.transform(lambda cell: cell.str.to_uppercase()) - ["AB", "BC", "CD"] + +---------+ + | example | + | --- | + | str | + +=========+ + | AB | + | BC | + | CD | + +---------+ """ @abstractmethod @@ -89,7 +97,15 @@ def index_of(self, substring: str) -> Cell[int | None]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) >>> column.transform(lambda cell: cell.str.index_of("b")) - [1, 0, None] + +---------+ + | example | + | --- | + | u32 | + +=========+ + | 1 | + | 0 | + | null | + +---------+ """ @abstractmethod @@ -113,7 +129,15 @@ def length(self, *, optimize_for_ascii: bool = False) -> Cell[int]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", "a", "abc"]) >>> column.transform(lambda cell: cell.str.length()) - [0, 1, 3] + +---------+ + | example | + | --- | + | u32 | + +=========+ + | 0 | + | 1 | + | 3 | + +---------+ """ @abstractmethod @@ -138,7 +162,15 @@ def replace(self, old: str, new: str) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) >>> column.transform(lambda cell: cell.str.replace("b", "z")) - ["az", "zc", "cd"] + +---------+ + | example | + | --- | + | str | + +=========+ + | az | + | zc | + | cd | + +---------+ """ @abstractmethod @@ -192,7 +224,15 @@ def substring(self, start: int = 0, length: int | None = None) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["abc", "def", "ghi"]) >>> column.transform(lambda cell: cell.str.substring(1, 2)) - ["bc", "ef", "hi"] + +---------+ + | example | + | --- | + | str | + +=========+ + | bc | + | ef | + | hi | + +---------+ """ @abstractmethod @@ -210,7 +250,15 @@ def to_date(self) -> Cell[datetime.date | None]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["2021-01-01", "2021-02-01", "abc"]) >>> column.transform(lambda cell: cell.str.to_date()) - ["2021-01-01", "2021-02-01", None] + +------------+ + | example | + | --- | + | date | + +============+ + | 2021-01-01 | + | 2021-02-01 | + | null | + +------------+ """ @abstractmethod @@ -226,9 +274,17 @@ def to_datetime(self) -> Cell[datetime.datetime | None]: Examples -------- >>> from safeds.data.tabular.containers import Column - >>> column = Column("example", ["2021-01-01T00:00:00", "2021-02-01T00:00:00", "abc"]) + >>> column = Column("example", ["2021-01-01T00:00:00z", "2021-02-01T00:00:00z", "abc"]) >>> column.transform(lambda cell: cell.str.to_datetime()) - ["2021-01-01 00:00:00", "2021-02-01 00:00:00", None] + +-------------------------+ + | example | + | --- | + | datetime[μs, UTC] | + +=========================+ + | 2021-01-01 00:00:00 UTC | + | 2021-02-01 00:00:00 UTC | + | null | + +-------------------------+ """ @abstractmethod @@ -246,7 +302,16 @@ def to_float(self) -> Cell[float | None]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["1", "3.4", "5.6", "abc"]) >>> column.transform(lambda cell: cell.str.to_float()) - [1.0, 3.4, 5.6, None] + +---------+ + | example | + | --- | + | f64 | + +=========+ + | 1.00000 | + | 3.40000 | + | 5.60000 | + | null | + +---------+ """ @abstractmethod @@ -269,11 +334,29 @@ def to_int(self, *, base: int = 10) -> Cell[int | None]: >>> from safeds.data.tabular.containers import Column >>> column1 = Column("example", ["1", "2", "3", "abc"]) >>> column1.transform(lambda cell: cell.str.to_int()) - [1, 2, 3, None] + +---------+ + | example | + | --- | + | i64 | + +=========+ + | 1 | + | 2 | + | 3 | + | null | + +---------+ >>> column2 = Column("example", ["1", "10", "11", "abc"]) >>> column2.transform(lambda cell: cell.str.to_int(base=2)) - [1, 2, 3, None] + +---------+ + | example | + | --- | + | i64 | + +=========+ + | 1 | + | 2 | + | 3 | + | null | + +---------+ """ @abstractmethod @@ -291,7 +374,15 @@ def to_lowercase(self) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["AB", "BC", "CD"]) >>> column.transform(lambda cell: cell.str.to_lowercase()) - ["ab", "bc", "cd"] + +---------+ + | example | + | --- | + | str | + +=========+ + | ab | + | bc | + | cd | + +---------+ """ @abstractmethod @@ -309,7 +400,15 @@ def to_uppercase(self) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["ab", "bc", "cd"]) >>> column.transform(lambda cell: cell.str.to_uppercase()) - ["AB", "BC", "CD"] + +---------+ + | example | + | --- | + | str | + +=========+ + | AB | + | BC | + | CD | + +---------+ """ @abstractmethod @@ -327,7 +426,16 @@ def trim(self) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", " abc", "abc ", " abc "]) >>> column.transform(lambda cell: cell.str.trim()) - ["", "abc", "abc", "abc"] + +---------+ + | example | + | --- | + | str | + +=========+ + | | + | abc | + | abc | + | abc | + +---------+ """ @abstractmethod @@ -345,7 +453,16 @@ def trim_end(self) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", " abc", "abc ", " abc "]) >>> column.transform(lambda cell: cell.str.trim_end()) - ["", " abc", "abc", " abc"] + +---------+ + | example | + | --- | + | str | + +=========+ + | | + | abc | + | abc | + | abc | + +---------+ """ @abstractmethod @@ -363,7 +480,16 @@ def trim_start(self) -> Cell[str]: >>> from safeds.data.tabular.containers import Column >>> column = Column("example", ["", " abc", "abc ", " abc "]) >>> column.transform(lambda cell: cell.str.trim_start()) - ["", "abc", "abc ", "abc "] + +---------+ + | example | + | --- | + | str | + +=========+ + | | + | abc | + | abc | + | abc | + +---------+ """ # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index 476791915..80f7971de 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -225,7 +225,7 @@ def from_json_file(path: str | Path) -> Table: Examples -------- >>> from safeds.data.tabular.containers import Table - >>> Table.from_json_file("./src/resources/from_json_file_2.json") + >>> Table.from_json_file("./src/resources/from_json_file.json") +-----+-----+ | a | b | | --- | --- | @@ -1883,7 +1883,7 @@ def to_json_file( -------- >>> from safeds.data.tabular.containers import Table >>> table = Table({"a": [1, 2, 3], "b": [4, 5, 6]}) - >>> table.to_json_file("./src/resources/to_json_file_2.json") + >>> table.to_json_file("./src/resources/to_json_file.json") """ path = _normalize_and_check_file_path(path, ".json", [".json"]) path.parent.mkdir(parents=True, exist_ok=True) From 2cce126119dab6f0278f0b6f19eb2e4d54fcf401 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 16:59:24 +0200 Subject: [PATCH 47/49] style: fix ruff error --- src/safeds/data/tabular/containers/_lazy_string_cell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/safeds/data/tabular/containers/_lazy_string_cell.py b/src/safeds/data/tabular/containers/_lazy_string_cell.py index a06aa0dea..20f80e0fc 100644 --- a/src/safeds/data/tabular/containers/_lazy_string_cell.py +++ b/src/safeds/data/tabular/containers/_lazy_string_cell.py @@ -1,6 +1,5 @@ from __future__ import annotations -import datetime from typing import TYPE_CHECKING from safeds._utils import _structural_hash @@ -10,6 +9,8 @@ from ._string_cell import StringCell if TYPE_CHECKING: + import datetime + import polars as pl from ._cell import Cell From 3ec00969903fddf0711a6a0ac176e17e5d7c16b9 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 19 May 2024 17:07:18 +0200 Subject: [PATCH 48/49] style: fix mypy errors --- src/safeds/ml/metrics/_classification_metrics.py | 2 +- src/safeds/ml/metrics/_regression_metrics.py | 2 +- tests/safeds/data/tabular/containers/_cell/test_abs.py | 4 ++-- tests/safeds/data/tabular/containers/_cell/test_add.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_and.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_ceil.py | 4 ++-- tests/safeds/data/tabular/containers/_cell/test_div.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_eq.py | 8 ++++---- tests/safeds/data/tabular/containers/_cell/test_equals.py | 2 +- tests/safeds/data/tabular/containers/_cell/test_floor.py | 4 ++-- .../safeds/data/tabular/containers/_cell/test_floordiv.py | 4 ++-- tests/safeds/data/tabular/containers/_cell/test_ge.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_gt.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_hash.py | 4 +++- tests/safeds/data/tabular/containers/_cell/test_le.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_lt.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_mod.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_mul.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_ne.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_neg.py | 4 ++-- tests/safeds/data/tabular/containers/_cell/test_not.py | 4 ++-- tests/safeds/data/tabular/containers/_cell/test_or.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_pos.py | 2 +- tests/safeds/data/tabular/containers/_cell/test_pow.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_sizeof.py | 6 +++++- tests/safeds/data/tabular/containers/_cell/test_sub.py | 6 +++--- tests/safeds/data/tabular/containers/_cell/test_xor.py | 6 +++--- 27 files changed, 70 insertions(+), 64 deletions(-) diff --git a/src/safeds/ml/metrics/_classification_metrics.py b/src/safeds/ml/metrics/_classification_metrics.py index 5a06390c5..97ffbf7a5 100644 --- a/src/safeds/ml/metrics/_classification_metrics.py +++ b/src/safeds/ml/metrics/_classification_metrics.py @@ -15,7 +15,7 @@ class ClassificationMetrics(ABC): """A collection of classification metrics.""" @abstractmethod - def __init__(self): ... + def __init__(self) -> None: ... @staticmethod def summarize(predicted: Column | TabularDataset, expected: Column | TabularDataset, positive_class: Any) -> Table: diff --git a/src/safeds/ml/metrics/_regression_metrics.py b/src/safeds/ml/metrics/_regression_metrics.py index d0506b2e8..79cb2a872 100644 --- a/src/safeds/ml/metrics/_regression_metrics.py +++ b/src/safeds/ml/metrics/_regression_metrics.py @@ -11,7 +11,7 @@ class RegressionMetrics(ABC): """A collection of regression metrics.""" @abstractmethod - def __init__(self): ... + def __init__(self) -> None: ... @staticmethod def summarize(predicted: Column | TabularDataset, expected: Column | TabularDataset) -> Table: diff --git a/tests/safeds/data/tabular/containers/_cell/test_abs.py b/tests/safeds/data/tabular/containers/_cell/test_abs.py index de02f2eff..a01749e32 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_abs.py +++ b/tests/safeds/data/tabular/containers/_cell/test_abs.py @@ -23,8 +23,8 @@ ], ) class TestShouldReturnAbsoluteValueOfCell: - def test_dunder_method(self, value: float, expected: float): + def test_dunder_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: abs(cell), expected) - def test_named_method(self, value: float, expected: float): + def test_named_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: cell.abs(), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_add.py b/tests/safeds/data/tabular/containers/_cell/test_add.py index 4cf9a8ae3..5773ae9d3 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_add.py +++ b/tests/safeds/data/tabular/containers/_cell/test_add.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeAddition: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell + value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1 + cell, expected) - def test_named_method(self, value1: float, value2: float, expected: float): + def test_named_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell.add(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_and.py b/tests/safeds/data/tabular/containers/_cell/test_and.py index 6a5889fa2..cbae19fb5 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_and.py +++ b/tests/safeds/data/tabular/containers/_cell/test_and.py @@ -29,11 +29,11 @@ ], ) class TestShouldComputeConjunction: - def test_dunder_method(self, value1: Any, value2: bool, expected: bool): + def test_dunder_method(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell & value2, expected) - def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): + def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 & cell, expected) - def test_named_method(self, value1: Any, value2: bool, expected: bool): + def test_named_method(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.and_(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_ceil.py b/tests/safeds/data/tabular/containers/_cell/test_ceil.py index 647528f24..b1cd6bed0 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_ceil.py +++ b/tests/safeds/data/tabular/containers/_cell/test_ceil.py @@ -25,8 +25,8 @@ ], ) class TestShouldReturnCeilOfCell: - def test_dunder_method(self, value: float, expected: float): + def test_dunder_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: math.ceil(cell), expected) - def test_named_method(self, value: float, expected: float): + def test_named_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: cell.ceil(), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_div.py b/tests/safeds/data/tabular/containers/_cell/test_div.py index 03b97446e..e2ad00380 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_div.py +++ b/tests/safeds/data/tabular/containers/_cell/test_div.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeDivision: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell / value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1 / cell, expected) - def test_named_method(self, value1: float, value2: float, expected: float): + def test_named_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell.div(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_eq.py b/tests/safeds/data/tabular/containers/_cell/test_eq.py index 0ea7398aa..9347e1456 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_eq.py +++ b/tests/safeds/data/tabular/containers/_cell/test_eq.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeEquality: - def test_dunder_method(self, value1: float, value2: float, expected: bool): + def test_dunder_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell == value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value2, lambda cell: value1 == cell, expected) + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool) -> None: + assert_cell_operation_works(value2, lambda cell: value1 == cell, expected) # type: ignore[arg-type,return-value] - def test_named_method(self, value1: float, value2: float, expected: bool): + def test_named_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.eq(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_equals.py b/tests/safeds/data/tabular/containers/_cell/test_equals.py index c24a078e1..3a859c08a 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_equals.py +++ b/tests/safeds/data/tabular/containers/_cell/test_equals.py @@ -22,7 +22,7 @@ def test_should_return_whether_two_cells_are_equal(cell1: Cell, cell2: Cell, exp def test_should_return_true_if_objects_are_identical() -> None: - cell = _LazyCell(pl.col("a")) + cell: Cell[Any] = _LazyCell(pl.col("a")) assert (cell._equals(cell)) is True diff --git a/tests/safeds/data/tabular/containers/_cell/test_floor.py b/tests/safeds/data/tabular/containers/_cell/test_floor.py index e4ce81cb5..72590efa8 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_floor.py +++ b/tests/safeds/data/tabular/containers/_cell/test_floor.py @@ -25,8 +25,8 @@ ], ) class TestShouldReturnFloorOfCell: - def test_dunder_method(self, value: float, expected: float): + def test_dunder_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: math.floor(cell), expected) - def test_named_method(self, value: float, expected: float): + def test_named_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: cell.floor(), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_floordiv.py b/tests/safeds/data/tabular/containers/_cell/test_floordiv.py index d9394782f..495b129cc 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_floordiv.py +++ b/tests/safeds/data/tabular/containers/_cell/test_floordiv.py @@ -19,8 +19,8 @@ ], ) class TestShouldComputeDivision: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell // value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1 // cell, expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_ge.py b/tests/safeds/data/tabular/containers/_cell/test_ge.py index d0e8aaaaf..f285f4ae2 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_ge.py +++ b/tests/safeds/data/tabular/containers/_cell/test_ge.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeGreaterThanOrEqual: - def test_dunder_method(self, value1: float, value2: float, expected: bool): + def test_dunder_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell >= value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 >= cell, expected) - def test_named_method(self, value1: float, value2: float, expected: bool): + def test_named_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.ge(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_gt.py b/tests/safeds/data/tabular/containers/_cell/test_gt.py index b8f4bed35..a28704793 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_gt.py +++ b/tests/safeds/data/tabular/containers/_cell/test_gt.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeGreaterThan: - def test_dunder_method(self, value1: float, value2: float, expected: bool): + def test_dunder_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell > value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 > cell, expected) - def test_named_method(self, value1: float, value2: float, expected: bool): + def test_named_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.gt(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_hash.py b/tests/safeds/data/tabular/containers/_cell/test_hash.py index 5eb2019fb..cdea9c706 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_hash.py +++ b/tests/safeds/data/tabular/containers/_cell/test_hash.py @@ -1,3 +1,5 @@ +from typing import Any + import polars as pl import pytest from safeds.data.tabular.containers import Cell @@ -5,7 +7,7 @@ def test_should_be_deterministic() -> None: - cell = _LazyCell(pl.col("a")) + cell: Cell[Any] = _LazyCell(pl.col("a")) assert hash(cell) == 7139977585477665635 diff --git a/tests/safeds/data/tabular/containers/_cell/test_le.py b/tests/safeds/data/tabular/containers/_cell/test_le.py index b6aa09e1a..d2ea39816 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_le.py +++ b/tests/safeds/data/tabular/containers/_cell/test_le.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeLessThanOrEqual: - def test_dunder_method(self, value1: float, value2: float, expected: bool): + def test_dunder_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell <= value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 <= cell, expected) - def test_named_method(self, value1: float, value2: float, expected: bool): + def test_named_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.le(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_lt.py b/tests/safeds/data/tabular/containers/_cell/test_lt.py index b3a685ae8..2dc961031 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_lt.py +++ b/tests/safeds/data/tabular/containers/_cell/test_lt.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeLessThan: - def test_dunder_method(self, value1: float, value2: float, expected: bool): + def test_dunder_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell < value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 < cell, expected) - def test_named_method(self, value1: float, value2: float, expected: bool): + def test_named_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.lt(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_mod.py b/tests/safeds/data/tabular/containers/_cell/test_mod.py index 6873664a7..e71bc4642 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_mod.py +++ b/tests/safeds/data/tabular/containers/_cell/test_mod.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeModulus: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell % value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1 % cell, expected) - def test_named_method(self, value1: float, value2: float, expected: float): + def test_named_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell.mod(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_mul.py b/tests/safeds/data/tabular/containers/_cell/test_mul.py index 39719d96a..a9da48885 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_mul.py +++ b/tests/safeds/data/tabular/containers/_cell/test_mul.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeMultiplication: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell * value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1 * cell, expected) - def test_named_method(self, value1: float, value2: float, expected: float): + def test_named_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell.mul(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_ne.py b/tests/safeds/data/tabular/containers/_cell/test_ne.py index 93eacf824..e826c2b6e 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_ne.py +++ b/tests/safeds/data/tabular/containers/_cell/test_ne.py @@ -19,8 +19,8 @@ ], ) class TestShouldComputeNegatedEquality: - def test_dunder_method(self, value1: float, value2: float, expected: bool): + def test_dunder_method(self, value1: float, value2: float, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell != value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool): - assert_cell_operation_works(value1, lambda cell: value2 != cell, expected) + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: bool) -> None: + assert_cell_operation_works(value1, lambda cell: value2 != cell, expected) # type: ignore[arg-type,return-value] diff --git a/tests/safeds/data/tabular/containers/_cell/test_neg.py b/tests/safeds/data/tabular/containers/_cell/test_neg.py index c724c00db..306fdd530 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_neg.py +++ b/tests/safeds/data/tabular/containers/_cell/test_neg.py @@ -23,8 +23,8 @@ ], ) class TestShouldNegateValueOfCell: - def test_dunder_method(self, value: float, expected: float): + def test_dunder_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: -cell, expected) - def test_named_method(self, value: float, expected: float): + def test_named_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: cell.neg(), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_not.py b/tests/safeds/data/tabular/containers/_cell/test_not.py index 1e5f342fd..6381200b7 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_not.py +++ b/tests/safeds/data/tabular/containers/_cell/test_not.py @@ -21,8 +21,8 @@ ], ) class TestShouldInvertValueOfCell: - def test_dunder_method(self, value: Any, expected: bool): + def test_dunder_method(self, value: Any, expected: bool) -> None: assert_cell_operation_works(value, lambda cell: ~cell, expected) - def test_named_method(self, value: Any, expected: bool): + def test_named_method(self, value: Any, expected: bool) -> None: assert_cell_operation_works(value, lambda cell: cell.not_(), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_or.py b/tests/safeds/data/tabular/containers/_cell/test_or.py index 249907aed..edf5bc89c 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_or.py +++ b/tests/safeds/data/tabular/containers/_cell/test_or.py @@ -29,11 +29,11 @@ ], ) class TestShouldComputeDisjunction: - def test_dunder_method(self, value1: Any, value2: bool, expected: bool): + def test_dunder_method(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell | value2, expected) - def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): + def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 | cell, expected) - def test_named_method(self, value1: Any, value2: bool, expected: bool): + def test_named_method(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.or_(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_pos.py b/tests/safeds/data/tabular/containers/_cell/test_pos.py index c373c41c8..da37cc41f 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_pos.py +++ b/tests/safeds/data/tabular/containers/_cell/test_pos.py @@ -23,5 +23,5 @@ ], ) class TestShouldReturnValueOfCell: - def test_dunder_method(self, value: float, expected: float): + def test_dunder_method(self, value: float, expected: float) -> None: assert_cell_operation_works(value, lambda cell: +cell, expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_pow.py b/tests/safeds/data/tabular/containers/_cell/test_pow.py index 62400720d..40a2d5216 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_pow.py +++ b/tests/safeds/data/tabular/containers/_cell/test_pow.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputePower: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell**value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1**cell, expected) - def test_named_method(self, value1: float, value2: float, expected: float): + def test_named_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell.pow(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_sizeof.py b/tests/safeds/data/tabular/containers/_cell/test_sizeof.py index acf2a950c..1043ed5df 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_sizeof.py +++ b/tests/safeds/data/tabular/containers/_cell/test_sizeof.py @@ -1,9 +1,13 @@ import sys +from typing import TYPE_CHECKING, Any import polars as pl from safeds.data.tabular.containers._lazy_cell import _LazyCell +if TYPE_CHECKING: + from safeds.data.tabular.containers import Cell + def test_should_return_size_greater_than_normal_object() -> None: - cell = _LazyCell(pl.col("a")) + cell: Cell[Any] = _LazyCell(pl.col("a")) assert sys.getsizeof(cell) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/_cell/test_sub.py b/tests/safeds/data/tabular/containers/_cell/test_sub.py index 40eb65d98..f5dc9b885 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_sub.py +++ b/tests/safeds/data/tabular/containers/_cell/test_sub.py @@ -19,11 +19,11 @@ ], ) class TestShouldComputeSubtraction: - def test_dunder_method(self, value1: float, value2: float, expected: float): + def test_dunder_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell - value2, expected) - def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float): + def test_dunder_method_inverted_order(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value2, lambda cell: value1 - cell, expected) - def test_named_method(self, value1: float, value2: float, expected: float): + def test_named_method(self, value1: float, value2: float, expected: float) -> None: assert_cell_operation_works(value1, lambda cell: cell.sub(value2), expected) diff --git a/tests/safeds/data/tabular/containers/_cell/test_xor.py b/tests/safeds/data/tabular/containers/_cell/test_xor.py index c9396f1a6..256846491 100644 --- a/tests/safeds/data/tabular/containers/_cell/test_xor.py +++ b/tests/safeds/data/tabular/containers/_cell/test_xor.py @@ -29,11 +29,11 @@ ], ) class TestShouldComputeExclusiveOr: - def test_dunder_method(self, value1: Any, value2: bool, expected: bool): + def test_dunder_method(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell ^ value2, expected) - def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool): + def test_dunder_method_inverted_order(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value2, lambda cell: value1 ^ cell, expected) - def test_named_method(self, value1: Any, value2: bool, expected: bool): + def test_named_method(self, value1: Any, value2: bool, expected: bool) -> None: assert_cell_operation_works(value1, lambda cell: cell.xor(value2), expected) From c5479e90a19cec53fe3dc9aced02e2279eaaaed0 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Sun, 19 May 2024 15:12:01 +0000 Subject: [PATCH 49/49] style: apply automated linter fixes --- src/resources/to_json_file.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/resources/to_json_file.json b/src/resources/to_json_file.json index 29d04896f..5cd814134 100644 --- a/src/resources/to_json_file.json +++ b/src/resources/to_json_file.json @@ -1 +1,6 @@ -{"columns":[{"name":"a","datatype":"Int64","bit_settings":"","values":[1,2,3]},{"name":"b","datatype":"Int64","bit_settings":"","values":[4,5,6]}]} \ No newline at end of file +{ + "columns": [ + { "name": "a", "datatype": "Int64", "bit_settings": "", "values": [1, 2, 3] }, + { "name": "b", "datatype": "Int64", "bit_settings": "", "values": [4, 5, 6] } + ] +}