Skip to content

Commit

Permalink
Add box-plots to standard deviation plot
Browse files Browse the repository at this point in the history
- test for stddev plot
- Update baseline image for test_that_all_snake_oil_visualisations_matches_snapshot
- Remove redudant image files

Co-authored by Feda Curic <[email protected]>
  • Loading branch information
dafeda authored and xjules committed Oct 3, 2024
1 parent 7c8fcc8 commit 6e715d8
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 20 deletions.
78 changes: 63 additions & 15 deletions src/ert/gui/plottery/plots/std_dev.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, List
from typing import Any, Dict

import matplotlib.pyplot as plt
import numpy as np
Expand Down Expand Up @@ -28,40 +28,88 @@ def plot(
if layer is not None:
vmin: float = np.inf
vmax: float = -np.inf
axes = []
images: List[npt.NDArray[np.float32]] = []
heatmaps = []
boxplot_axes = []

# Adjust height_ratios to reduce space between plots
figure.set_layout_engine("constrained")
gridspec = figure.add_gridspec(2, ensemble_count, hspace=0.2)

for i, ensemble in enumerate(plot_context.ensembles(), start=1):
ax = figure.add_subplot(1, ensemble_count, i)
axes.append(ax)
ax_heat = figure.add_subplot(gridspec[0, i - 1])
ax_box = figure.add_subplot(gridspec[1, i - 1])
data = std_dev_data[ensemble.name]
if data.size == 0:
ax.set_axis_off()
ax.text(
ax_heat.set_axis_off()
ax_box.set_axis_off()
ax_heat.text(
0.5,
0.5,
f"No data for {ensemble.experiment_name} : {ensemble.name}",
ha="center",
va="center",
)
else:
images.append(data)
vmin = min(vmin, float(np.min(data)))
vmax = max(vmax, float(np.max(data)))
ax.set_title(

im = ax_heat.imshow(data, cmap="viridis", aspect="equal")
heatmaps.append(im)

ax_box.boxplot(data.flatten(), vert=True, widths=0.5)
boxplot_axes.append(ax_box)

min_value = np.min(data)
mean_value = np.mean(data)
max_value = np.max(data)

annotation_text = f"Min: {min_value:.2f}\nMean: {mean_value:.2f}\nMax: {max_value:.2f}"
ax_box.annotate(
annotation_text,
xy=(1, 1), # Changed from (0, 1) to (1, 1)
xycoords="axes fraction",
ha="right", # Changed from 'left' to 'right'
va="top",
fontsize=8,
fontweight="bold",
bbox={
"facecolor": "white",
"edgecolor": "black",
"boxstyle": "round,pad=0.2",
},
)

ax_box.spines["top"].set_visible(False)
ax_box.spines["right"].set_visible(False)
ax_box.spines["bottom"].set_visible(False)
ax_box.spines["left"].set_visible(True)

ax_box.set_xticks([])
ax_box.set_xticklabels([])

ax_heat.set_ylabel("")
ax_box.set_ylabel(
"Standard Deviation", fontsize=8
) # Reduced font size

self._colorbar(im)

ax_heat.set_title(
f"{ensemble.experiment_name} : {ensemble.name} layer={layer}",
wrap=True,
fontsize=10, # Reduced font size
)

norm = plt.Normalize(vmin, vmax)
for ax, data in zip(axes, images):
if data is not None:
im = ax.imshow(data, norm=norm, cmap="viridis")
self._colorbar(im)
figure.tight_layout()
for im in heatmaps:
im.set_norm(norm)

padding = 0.05 * (vmax - vmin)
for ax_box in boxplot_axes:
ax_box.set_ylim(vmin - padding, vmax + padding)

@staticmethod
def _colorbar(mappable: Any) -> Any:
# https://joseph-long.com/writing/colorbars/
last_axes = plt.gca()
ax = mappable.axes
assert ax is not None
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
16 changes: 11 additions & 5 deletions tests/ert/ui_tests/gui/test_plotting_of_snake_oil.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
from ert.services import StorageService
from ert.storage import open_storage

from .conftest import (
get_child,
wait_for_child,
)
from .conftest import get_child, wait_for_child


# Use a fixture for the figure in order for the lifetime
# of the c++ gui element to not go out before mpl_image_compare
# of the c++ gui element to not go out before mpl_image_compare.
# Note that the data is copied from test-data and all the existing storages
# there will be copied too! They need to be removed!
# Once the storage is created it its cached in .pytest_cache.
@pytest.fixture(
params=[
("FOPR", STATISTICS, "snake_oil"),
Expand Down Expand Up @@ -97,6 +97,12 @@ def plot_figure(qtbot, heat_equation_storage, snake_oil_case_storage, request):
selected_key.dimensionality
== tab._plotter.dimensionality
)
if plot_name == STD_DEV:
# we need a better resolution for box plots
tab._figure.set_size_inches(
2000 / tab._figure.get_dpi(),
1000 / tab._figure.get_dpi(),
)
yield tab._figure.figure
else:
assert (
Expand Down
49 changes: 49 additions & 0 deletions tests/ert/unit_tests/gui/plottery/test_stddev_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from unittest.mock import Mock

import matplotlib.pyplot as plt
import numpy as np
import pytest
from matplotlib.figure import Figure

from ert.gui.plottery import PlotConfig, PlotContext
from ert.gui.plottery.plots.std_dev import StdDevPlot
from ert.gui.tools.plot.plot_api import EnsembleObject


@pytest.fixture()
def plot_context(request):
context = Mock(spec=PlotContext)
context.ensembles.return_value = [
EnsembleObject("ensemble_1", "id", False, "experiment_1")
]
context.history_data = None
context.layer = 0
context.plotConfig.return_value = PlotConfig(title="StdDev Plot")
return context


def test_stddev_plot_shows_boxplot(plot_context: PlotContext):
rng = np.random.default_rng()
figure = Figure()
std_dev_data = rng.random((5, 5))
StdDevPlot().plot(
figure,
plot_context,
{},
{},
{"ensemble_1": std_dev_data},
)
ax = figure.axes
assert ax[0].get_title() == "experiment_1 : ensemble_1 layer=0"
assert ax[1].get_ylabel() == "Standard Deviation"
annotation = [
child for child in ax[1].get_children() if isinstance(child, plt.Annotation)
]
assert len(annotation) == 1
min_value = np.min(std_dev_data)
mean_value = np.mean(std_dev_data)
max_value = np.max(std_dev_data)
assert (
annotation[0].get_text()
== f"Min: {min_value:.2f}\nMean: {mean_value:.2f}\nMax: {max_value:.2f}"
)

0 comments on commit 6e715d8

Please sign in to comment.