From ee8acf762cef001a111c2d19c8f640ca6790de0c Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 17 May 2024 16:15:51 +0200 Subject: [PATCH] feat: outline around dots of scatterplot (#785) ### Summary of Changes Draw a white outline around the dots of a scatterplot to easily view overlapping dots. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> --- .../data/tabular/plotting/_table_plotter.py | 11 +++-- .../test_should_match_snapshot.png | Bin 12490 -> 0 bytes ...test_should_match_snapshot[functional].png | Bin 0 -> 12643 bytes ...est_should_match_snapshot[overlapping].png | Bin 0 -> 11230 bytes .../_table/test_plot_scatterplot.py | 39 ++++++++++++++++-- 5 files changed, 40 insertions(+), 10 deletions(-) delete mode 100644 tests/safeds/data/tabular/containers/_table/__snapshots__/test_plot_scatterplot/test_should_match_snapshot.png create mode 100644 tests/safeds/data/tabular/containers/_table/__snapshots__/test_plot_scatterplot/test_should_match_snapshot[functional].png create mode 100644 tests/safeds/data/tabular/containers/_table/__snapshots__/test_plot_scatterplot/test_should_match_snapshot[overlapping].png diff --git a/src/safeds/data/tabular/plotting/_table_plotter.py b/src/safeds/data/tabular/plotting/_table_plotter.py index a58b3c8f2..6be3bc095 100644 --- a/src/safeds/data/tabular/plotting/_table_plotter.py +++ b/src/safeds/data/tabular/plotting/_table_plotter.py @@ -5,6 +5,7 @@ from safeds._utils import _figure_to_image from safeds._validation import _check_columns_exist +from safeds._validation._check_columns_are_numeric import _check_columns_are_numeric from safeds.exceptions import NonNumericColumnError if TYPE_CHECKING: @@ -322,12 +323,7 @@ def scatter_plot(self, x_name: str, y_name: str) -> Image: >>> image = table.plot.scatter_plot("a", "b") """ _check_columns_exist(self._table, [x_name, y_name]) - - # TODO: pass list of columns names + extract validation - if not self._table.get_column(x_name).is_numeric: - raise NonNumericColumnError(x_name) - if not self._table.get_column(y_name).is_numeric: - raise NonNumericColumnError(y_name) + _check_columns_are_numeric(self._table, [x_name, y_name]) import matplotlib.pyplot as plt @@ -335,6 +331,9 @@ def scatter_plot(self, x_name: str, y_name: str) -> Image: ax.scatter( x=self._table.get_column(x_name)._series, y=self._table.get_column(y_name)._series, + s=64, # marker size + linewidth=1, + edgecolor="white", ) ax.set( xlabel=x_name, diff --git a/tests/safeds/data/tabular/containers/_table/__snapshots__/test_plot_scatterplot/test_should_match_snapshot.png b/tests/safeds/data/tabular/containers/_table/__snapshots__/test_plot_scatterplot/test_should_match_snapshot.png deleted file mode 100644 index c2dcdd67d9f6fbb790315dce2abbe52cca1679bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12490 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 NH None: - table = Table({"A": [1, 2, 3], "B": [2, 4, 7]}) - scatterplot = table.plot.scatter_plot("A", "B") +@pytest.mark.parametrize( + ("table", "x_name", "y_name"), + [ + (Table({"A": [1, 2, 3], "B": [2, 4, 7]}), "A", "B"), + ( + Table( + { + "A": [1, 0.99, 0.99, 2], + "B": [1, 0.99, 1.01, 2], + }, + ), + "A", + "B", + ), + ], + ids=[ + "functional", + "overlapping", + ], +) +def test_should_match_snapshot(table: Table, x_name: str, y_name: str, snapshot_png_image: SnapshotAssertion) -> None: + scatterplot = table.plot.scatter_plot(x_name, y_name) assert scatterplot == snapshot_png_image @@ -23,3 +42,15 @@ def test_should_match_snapshot(snapshot_png_image: SnapshotAssertion) -> None: def test_should_raise_if_column_does_not_exist(table: Table, col1: str, col2: str) -> None: with pytest.raises(ColumnNotFoundError): table.plot.scatter_plot(col1, col2) + + +@pytest.mark.parametrize( + ("table", "x_name", "y_name"), + [ + (Table({"A": ["a", "b", "c"], "B": [2, 4, 7]}), "A", "B"), + (Table({"A": [1, 2, 3], "B": ["a", "b", "c"]}), "A", "B"), + ], +) +def test_should_raise_if_columns_are_not_numeric(table: Table, x_name: str, y_name: str) -> None: + with pytest.raises(ColumnTypeError): + table.plot.scatter_plot(x_name, y_name)