Skip to content

Commit

Permalink
feat: dark mode for plots (#911)
Browse files Browse the repository at this point in the history
Closes #798 

### Summary of Changes

Implemented Dark Mode for Class Tableplotter and Columnplotter.

---------

Co-authored-by: Tobias Plücker <[email protected]>
Co-authored-by: megalinter-bot <[email protected]>
Co-authored-by: Tobias Plücker <[email protected]>
Co-authored-by: Lars Reimann <[email protected]>
  • Loading branch information
5 people authored Jul 14, 2024
1 parent 1cbc147 commit 5447551
Show file tree
Hide file tree
Showing 43 changed files with 756 additions and 265 deletions.
160 changes: 126 additions & 34 deletions src/safeds/data/tabular/plotting/_column_plotter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal

from safeds._utils import _figure_to_image
from safeds._validation._check_columns_are_numeric import _check_column_is_numeric
Expand Down Expand Up @@ -29,10 +29,15 @@ class ColumnPlotter:
def __init__(self, column: Column):
self._column: Column = column

def box_plot(self) -> Image:
def box_plot(self, *, theme: Literal["dark", "light"] = "light") -> Image:
"""
Create a box plot for the values in the column. This is only possible for numeric columns.
Parameters
----------
theme:
The color theme of the plot. Default is "light".
Returns
-------
plot:
Expand All @@ -54,27 +59,84 @@ def box_plot(self) -> Image:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.boxplot(
self._column._series.drop_nulls(),
patch_artist=True,
)

ax.set(title=self._column.name)
ax.set_xticks([])
ax.yaxis.grid(visible=True)
fig.tight_layout()

return _figure_to_image(fig)

def histogram(self, *, max_bin_count: int = 10) -> Image:
def _set_boxplot_colors(box: dict, theme: str) -> None:
if theme == "dark":
for median in box["medians"]:
median.set(color="orange", linewidth=1.5)

for box_part in box["boxes"]:
box_part.set(color="white", linewidth=1.5, facecolor="cyan")

for whisker in box["whiskers"]:
whisker.set(color="white", linewidth=1.5)

for cap in box["caps"]:
cap.set(color="white", linewidth=1.5)

for flier in box["fliers"]:
flier.set(marker="o", color="white", alpha=0.5)
else:
for median in box["medians"]:
median.set(color="orange", linewidth=1.5)

for box_part in box["boxes"]:
box_part.set(color="black", linewidth=1.5, facecolor="blue")

for whisker in box["whiskers"]:
whisker.set(color="black", linewidth=1.5)

for cap in box["caps"]:
cap.set(color="black", linewidth=1.5)

for flier in box["fliers"]:
flier.set(marker="o", color="black", alpha=0.5)

style = "dark_background" if theme == "dark" else "default"
with plt.style.context(style):
if theme == "dark":
plt.rcParams.update(
{
"text.color": "white",
"axes.labelcolor": "white",
"axes.edgecolor": "white",
"xtick.color": "white",
"ytick.color": "white",
"grid.color": "gray",
"grid.linewidth": 0.5,
},
)
else:
plt.rcParams.update(
{
"grid.linewidth": 0.5,
},
)

fig, ax = plt.subplots()
box = ax.boxplot(
self._column._series.drop_nulls(),
patch_artist=True,
)

_set_boxplot_colors(box, theme)

ax.set(title=self._column.name)
ax.set_xticks([])
ax.yaxis.grid(visible=True)
fig.tight_layout()

return _figure_to_image(fig)

def histogram(self, *, max_bin_count: int = 10, theme: Literal["dark", "light"] = "light") -> Image:
"""
Create a histogram for the values in the column.
Parameters
----------
max_bin_count:
The maximum number of bins to use in the histogram. Default is 10.
theme:
The color theme of the plot. Default is "light".
Returns
-------
Expand All @@ -87,16 +149,33 @@ def histogram(self, *, max_bin_count: int = 10) -> Image:
>>> column = Column("test", [1, 2, 3])
>>> histogram = column.plot.histogram()
"""
return self._column.to_table().plot.histograms(max_bin_count=max_bin_count)
import matplotlib.pyplot as plt

def lag_plot(self, lag: int) -> Image:
style = "dark_background" if theme == "dark" else "default"
with plt.style.context(style):
if theme == "dark":
plt.rcParams.update(
{
"text.color": "white",
"axes.labelcolor": "white",
"axes.edgecolor": "white",
"xtick.color": "white",
"ytick.color": "white",
},
)

return self._column.to_table().plot.histograms(max_bin_count=max_bin_count)

def lag_plot(self, lag: int, *, theme: Literal["dark", "light"] = "light") -> Image:
"""
Create a lag plot for the values in the column.
Parameters
----------
lag:
The amount of lag.
theme:
The color theme of the plot. Default is "light".
Returns
-------
Expand All @@ -114,21 +193,34 @@ def lag_plot(self, lag: int) -> Image:
>>> column = Column("values", [1, 2, 3, 4])
>>> image = column.plot.lag_plot(2)
"""
if self._column.row_count > 0:
_check_column_is_numeric(self._column, operation="create a lag plot")

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
series = self._column._series
ax.scatter(
x=series.slice(0, max(len(self._column) - lag, 0)),
y=series.slice(lag),
)
ax.set(
xlabel="y(t)",
ylabel=f"y(t + {lag})",
)
fig.tight_layout()

return _figure_to_image(fig)
style = "dark_background" if theme == "dark" else "default"
with plt.style.context(style):
if theme == "dark":
plt.rcParams.update(
{
"text.color": "white",
"axes.labelcolor": "white",
"axes.edgecolor": "white",
"xtick.color": "white",
"ytick.color": "white",
},
)

if self._column.row_count > 0:
_check_column_is_numeric(self._column, operation="create a lag plot")

fig, ax = plt.subplots()
series = self._column._series
ax.scatter(
x=series.slice(0, max(len(self._column) - lag, 0)),
y=series.slice(lag),
)
ax.set(
xlabel="y(t)",
ylabel=f"y(t + {lag})",
)
fig.tight_layout()

return _figure_to_image(fig)
Loading

0 comments on commit 5447551

Please sign in to comment.