Skip to content

Commit

Permalink
Update plot patch
Browse files Browse the repository at this point in the history
  • Loading branch information
langdal committed Aug 10, 2023
1 parent 72b85bd commit e2360d3
Showing 1 changed file with 59 additions and 46 deletions.
105 changes: 59 additions & 46 deletions optimizerapi/plot_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator, FuncFormatter
from ProcessOptimizer.space import (
Categorical
)
from ProcessOptimizer.space import Categorical, Integer
from ProcessOptimizer import expected_minimum
from ProcessOptimizer.plots import (
partial,
dependence,
_cat_format,
_map_categories
)
from ProcessOptimizer.plots import partial, dependence, _cat_format, _map_categories


def plot_brownie_bee(
result,
Expand All @@ -21,41 +15,47 @@ def plot_brownie_bee(
size=2,
max_quality=5,
):
"""Single factor dependence plot of the model intended for use with the
"""Single factor dependence plot of the model intended for use with the
Brownie Bee user interface.
Each plot shows how quality depends on the dimension `i` when all other
factor values are locked to those of the expected minimum. A vertical line
Each plot shows how quality depends on the dimension `i` when all other
factor values are locked to those of the expected minimum. A vertical line
indicates the location of the expected minimum for each factor.
Parameters
----------
* `result` [`OptimizeResult`]
The result for which to create the plots.
* `n_points` [int, default=40]
Number of points at which to evaluate the partial dependence
along each dimension.
* `n_samples` [int, default=250]
Number of random samples to use for averaging the model function
at each of the `n_points`.
* `size` [float, default=2]
Height (in inches) of each returned figure.
* `max_quality` [int, default=5]
* `max_quality` [int, default=5]
The maximal quality obtainable in the setup of Brownie Bee. Quality is
assumed to be measured on a scale from 0 to this number, and the y-axis
of each plot is scaled to reflect this.
Returns
-------
* `plot_list`: [`Figures`]:
A list of individual matplotlib figure handles, one for each dimension
present in 'result' and a last one representing a histogram of samples
drawn at the expected minimum.
"""
# Here we define the value to highlight in each dimension. These
# same values will be used for evaluating the plots when calculating
# dependence. (Unless partial dependence is to be used instead).

space = result.space
# Check if we have any categorical dimensions, as this influences the plots
is_cat = [isinstance(dim, Categorical) for dim in space.dimensions]
# Check if we have any integer dimensions, as this influences the plots
is_int = [isinstance(dim, Integer) for dim in space.dimensions]
# Identify the location of the expected minimum, and its mean and std
x_eval, [res_mean, res_std] = expected_minimum(
result,
Expand Down Expand Up @@ -85,84 +85,79 @@ def plot_brownie_bee(
plots_data.append(row)

# Create the list to store figure handles
figure_list = []
figure_list = []

# Build all the plots in the figure
for n in range(space.n_dims):
# Prepare a figure
# Prepare a figure
fig, ax_ = plt.subplots(
figsize=(size, size),
dpi=200,
)
# Set the padding
fig.subplots_adjust(
left=0.18, right=0.95, bottom=0.2, top=0.95, hspace=0.0, wspace=0.0
left=0.12, right=0.93, bottom=0.2, top=0.95, hspace=0.0, wspace=0.0
)

# Get data to plot in this subplot
xi = plots_data[n][0]["xi"]
yi = plots_data[n][0]["yi"]
stddevs = plots_data[n][0]["std"]
stddevs = plots_data[n][0]["std"]

# Set y-axis limits
ax_.set_ylim(0, max_quality)
ax_.set_ylim(0, max_quality)

# Enter here when we plot a categoric factor
if is_cat[n]:
if is_cat[n]:
# Expand the x-axis for this factor so we can see the first
# and the last category
ax_.set_xlim(np.min(xi)-0.2, np.max(xi)+0.2)
ax_.set_xlim(np.min(xi) - 0.2, np.max(xi) + 0.2)

# Highlight the expected minimum
ax_.axvline(minimum[n], linestyle="--", color="k", lw=1)
# Create one uniformly colored bar for each category.
# Edgecolor ensures we can see the bar when plotting
# Edgecolor ensures we can see the bar when plotting
# at best obeservation, as stddev is often tiny there
ax_.bar(
xi,
2*1.96*stddevs,
2 * 1.96 * stddevs,
width=0.2,
bottom=(-yi-1.96*stddevs),
bottom=(-yi - 1.96 * stddevs),
alpha=0.5,
color="green",
edgecolor="green",
)
)
[labl.set_fontsize(6) for labl in ax_.get_xticklabels()]

# For non-categoric factors
else:
ax_.set_xlim(np.min(xi), np.max(xi))
# Highlight the expected minimum
ax_.axvline(minimum[n], linestyle="--", color="k", lw=1)
# Show the uncertainty
ax_.fill_between(
xi,
y1=-(yi - 1.96*stddevs),
y2=-(yi + 1.96*stddevs),
y1=-(yi - 1.96 * stddevs),
y2=-(yi + 1.96 * stddevs),
alpha=0.5,
color="green",
edgecolor="green",
linewidth=0.0,
)

# Highlight the expected minimum
ax_.axvline(minimum[n], linestyle="--", color="r", lw=2, zorder=6)
# Fix formatting of the y-axis with ticks from 0 to our max quality
ax_.yaxis.set_major_locator(MaxNLocator(5, integer=True))
ax_.tick_params(axis="y", direction="inout")
# Fix formatting of the x-axis
[labl.set_rotation(45) for labl in ax_.get_xticklabels()]

if space.dimensions[n].prior == "log-uniform":
ax_.set_xscale("log")
else:
ax_.xaxis.set_major_locator(
MaxNLocator(6, prune="both", integer=is_cat[n])
MaxNLocator(4, prune=None, integer=(is_cat[n] | is_int[n]))
)
if is_cat[n]:
# Axes for categorical dimensions are really integers;
# Axes for categorical dimensions are really integers;
# we have to label them with the category names
ax_.xaxis.set_major_formatter(
FuncFormatter(
partial(_cat_format, space.dimensions[n])
)
FuncFormatter(partial(_cat_format, space.dimensions[n]))
)

# Add the figure to the output list
Expand All @@ -177,12 +172,8 @@ def plot_brownie_bee(
fig.subplots_adjust(
left=0.05, right=0.95, bottom=0.2, top=0.95, hspace=0.0, wspace=0.0
)
# Plot out to 3 standard deviations from the mean but not outside the scale
xi = np.linspace(
max(-res_mean-3*res_std, 0),
min(-res_mean+3*res_std, max_quality),
100,
)
# Plot in the interval between 0 and our max quality
xi = np.linspace(0, max_quality, 250)
# Create histogram y-values
yi = norm.pdf(xi, -res_mean, res_std)
# Build the plot
Expand All @@ -197,8 +188,30 @@ def plot_brownie_bee(
)
# Cosmetics
ax_.get_yaxis().set_visible(False)
ax_.set_ylim(0, max(yi) * 1.05)
# Fix formatting of the x-axis with ticks from 0 to our max quality
ax_.set_xlim(0, max_quality)
ax_.xaxis.set_major_locator(MaxNLocator(5, prune=None, integer=True))

# Add the figure to the output list
figure_list.append(fig)

return figure_list


def _cat_format(dimension, x, _):
"""Categorical axis tick formatter function. Returns the name of category
`x` in `dimension`. Used with `matplotlib.ticker.FuncFormatter`."""
x = min(max(int(x), 0), len(dimension.categories) - 1)
label = str(dimension.categories[x])
# If longer than 10 characters, try to break on spaces
if len(label) > 10:
if " " in label:
# Break label at a space near the middle
spaces = [i for i in range(len(label)) if label[i] == " "]
middle_space = spaces[len(spaces) // 2]
label = label[:middle_space] + "\n" + label[middle_space + 1 :]
else:
# If no spaces, abbreviate to first 7 characters
label = label[:7] + "..."
return label

0 comments on commit e2360d3

Please sign in to comment.