Skip to content

Commit

Permalink
feat: Add copy method for tables (#405)
Browse files Browse the repository at this point in the history
Closes #275 

### Summary of Changes
Added copy method for Table, TaggedTable, Row, Column and looked through
code to change usage to this method.

Co-authored-by: philipgutberlet
<92990487+philipgutberlet@users.noreply.github.com>

---------

Co-authored-by: PhilipGutberlet <92990487+PhilipGutberlet@users.noreply.github.com>
Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
Co-authored-by: Alexander <47296670+Marsmaennchen221@users.noreply.github.com>
  • Loading branch information
4 people committed Jun 30, 2023
1 parent cb77790 commit 72e87f0
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 4 deletions.
17 changes: 17 additions & 0 deletions src/safeds/data/tabular/containers/_column.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import copy
import io
from collections.abc import Sequence
from numbers import Number
Expand Down Expand Up @@ -1031,3 +1032,19 @@ def _count_missing_values(self) -> int:
2
"""
return self._data.isna().sum()

# ------------------------------------------------------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------------------------------------------------------

def _copy(self) -> Column:
"""
Return a copy of this column.
Returns
-------
column : Column
The copy of this column.
"""
return copy.deepcopy(self)
18 changes: 17 additions & 1 deletion src/safeds/data/tabular/containers/_row.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import copy
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any

Expand Down Expand Up @@ -263,7 +264,7 @@ def __repr__(self) -> str:
>>> repr(row)
"Row({'a': 1})"
"""
return f"Row({str(self)})"
return f"Row({self!s})"

def __str__(self) -> str:
"""
Expand Down Expand Up @@ -493,3 +494,18 @@ def _repr_html_(self) -> str:
The generated HTML.
"""
return self._data.to_html(max_rows=1, max_cols=self._data.shape[1], notebook=True)

# ------------------------------------------------------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------------------------------------------------------

def _copy(self) -> Row:
"""
Return a copy of this row.
Returns
-------
copy : Row
The copy of this row.
"""
return copy.deepcopy(self)
21 changes: 18 additions & 3 deletions src/safeds/data/tabular/containers/_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ def add_row(self, row: Row) -> Table:
1 3 4
"""
int_columns = []
result = self.remove_columns([]) # clone
result = self._copy()
if result.number_of_rows == 0:
int_columns = list(filter(lambda name: isinstance(row[name], int | np.int64), row.column_names))
if result.number_of_columns == 0:
Expand Down Expand Up @@ -974,7 +974,7 @@ def add_rows(self, rows: list[Row] | Table) -> Table:
if isinstance(rows, Table):
rows = rows.to_rows()
int_columns = []
result = self.remove_columns([]) # clone
result = self._copy()
for row in rows:
if result.number_of_rows == 0:
int_columns = list(filter(lambda name: isinstance(row[name], int | np.int64), row.column_names))
Expand Down Expand Up @@ -1094,7 +1094,7 @@ def keep_only_columns(self, column_names: list[str]) -> Table:
if len(invalid_columns) != 0:
raise UnknownColumnNameError(invalid_columns)

clone = copy.deepcopy(self)
clone = self._copy()
clone = clone.remove_columns(list(set(self.column_names) - set(column_names)))
return clone

Expand Down Expand Up @@ -2223,3 +2223,18 @@ def __dataframe__(self, nan_as_null: bool = False, allow_copy: bool = True): #
data_copy = self._data.copy()
data_copy.columns = self.column_names
return data_copy.__dataframe__(nan_as_null, allow_copy)

# ------------------------------------------------------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------------------------------------------------------

def _copy(self) -> Table:
"""
Return a copy of this table.
Returns
-------
table : Table
The copy of this table.
"""
return copy.deepcopy(self)
16 changes: 16 additions & 0 deletions src/safeds/data/tabular/containers/_tagged_table.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import copy
from typing import TYPE_CHECKING

from safeds.data.tabular.containers import Column, Table
Expand Down Expand Up @@ -169,3 +170,18 @@ def features(self) -> Table:
@property
def target(self) -> Column:
return self._target

# ------------------------------------------------------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------------------------------------------------------

def _copy(self) -> TaggedTable:
"""
Return a copy of this tagged table.
Returns
-------
table : TaggedTable
The copy of this tagged table.
"""
return copy.deepcopy(self)
13 changes: 13 additions & 0 deletions tests/safeds/data/tabular/containers/_column/test_copy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest
from safeds.data.tabular.containers import Column


@pytest.mark.parametrize(
"column",
[Column("a"), Column("a", ["a", 3, 0.1])],
ids=["empty", "normal"],
)
def test_should_copy_table(column: Column) -> None:
copied = column._copy()
assert copied == column
assert copied is not column
13 changes: 13 additions & 0 deletions tests/safeds/data/tabular/containers/_table/test_copy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest
from safeds.data.tabular.containers import Table


@pytest.mark.parametrize(
"table",
[Table(), Table({"a": [], "b": []}), Table({"a": ["a", 3, 0.1], "b": [True, False, None]})],
ids=["empty", "empty-rows", "normal"],
)
def test_should_copy_table(table: Table) -> None:
copied = table._copy()
assert copied == table
assert copied is not table
12 changes: 12 additions & 0 deletions tests/safeds/data/tabular/containers/test_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,3 +511,15 @@ def test_should_contain_th_element_for_each_column_name(self, row: Row) -> None:
def test_should_contain_td_element_for_each_value(self, row: Row) -> None:
for value in row.values():
assert f"<td>{value}</td>" in row._repr_html_()


class TestCopy:
@pytest.mark.parametrize(
"row",
[Row(), Row({"a": [3, 0.1]})],
ids=["empty", "normal"],
)
def test_should_copy_table(self, row: Row) -> None:
copied = row._copy()
assert copied == row
assert copied is not row
15 changes: 15 additions & 0 deletions tests/safeds/data/tabular/containers/test_tagged_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,18 @@ def test_should_return_features(self, tagged_table: TaggedTable) -> None:
class TestTarget:
def test_should_return_target(self, tagged_table: TaggedTable) -> None:
assert tagged_table.target == Column("T", [0, 1])


class TestCopy:
@pytest.mark.parametrize(
"tagged_table",
[
TaggedTable({"a": [], "b": []}, target_name="b", feature_names=["a"]),
TaggedTable({"a": ["a", 3, 0.1], "b": [True, False, None]}, target_name="b", feature_names=["a"]),
],
ids=["empty-rows", "normal"],
)
def test_should_copy_tagged_table(self, tagged_table: TaggedTable) -> None:
copied = tagged_table._copy()
assert copied == tagged_table
assert copied is not tagged_table

0 comments on commit 72e87f0

Please sign in to comment.