# pylint: disable=unused-argument
"""None plotting backend.
Interface to no plotting backend, returned processed data without interfacing to any plotting
backend for actual drawing.
:term:`plots` are lists which are extended with kwarg dictionaries every time an visual
would be added to them.
"""
import warnings
import numpy as np
from .legend import legend
ALLOW_KWARGS = True
class UnsetDefault:
"""Specific class to indicate an aesthetic hasn't been set."""
unset = UnsetDefault()
def get_background_color():
"""Get the background color."""
return "#ffffff"
# generation of default values for aesthetics
def get_default_aes(aes_key, n, kwargs=None):
"""Generate `n` default values for a given aesthetics keyword.
Parameters
----------
aes_key : str
The key for which default values should be generated.
Ideally part of {ref}`common interface arguments <backend_interface_arguments>`.
n : int
Number of values to generate.
kwargs : mapping of {str : array_like}, optional
Mapping with aesthetic keywords as keys and its correponding values as values.
If `aes_key` is present, the provided values will be used, repeating them
with :func:`numpy.tile` if necessary.
Returns
-------
ndarray of shape (n,)
The requested `n` default values for `aes_key`. They might not be unique.
"""
if kwargs is None:
kwargs = {}
if aes_key not in kwargs:
if aes_key in {"x", "y"}:
return np.arange(n)
if aes_key == "alpha":
return np.linspace(0.2, 0.7, n)
return np.array([f"{aes_key}_{i}" for i in range(n)])
aes_vals = kwargs[aes_key]
n_aes_vals = len(aes_vals)
if n_aes_vals >= n:
return aes_vals[:n]
return np.tile(aes_vals, (n // n_aes_vals) + 1)[:n]
def scale_fig_size(figsize, rows=1, cols=1, figsize_units=None):
"""Scale figure properties according to figsize, rows and cols.
Provide a default figure size given `rows` and `cols`.
Parameters
----------
figsize : tuple of (float, float) or None
Size of figure in `figsize_units`
rows : int, default 1
Number of rows
cols : int, default 1
Number of columns
figsize_units : {"inches", "dots"}, optional
Ignored if `figsize` is ``None``
Returns
-------
figsize : tuple of (float, float) or None
Size of figure in dots
"""
if figsize_units is None:
figsize_units = "dots"
if figsize is None:
width = cols * (400 if cols < 4 else 250)
height = 100 * (rows + 1) ** 1.1
figsize_units = "dots"
else:
width, height = figsize
if figsize_units == "inches":
warnings.warn(
f"Assuming dpi=100. Use figsize_units='dots' and figsize={figsize} "
"to stop seeing this warning"
)
width *= 100
height *= 100
elif figsize_units != "dots":
raise ValueError(f"figsize_units must be 'dots' or 'inches', but got {figsize_units}")
return (width, height)
# object creation and i/o
[docs]
def show(figure):
"""Show this :term:`figure`.
Parameters
----------
figure : figure_type
"""
raise TypeError("'none' backend objects can't be shown.")
def savefig(figure, path, **kwargs):
"""Show this :term:`figure`.
Parameters
----------
figure : figure_type
The figure to save.
path : pathlib.Path
The path to save the figure to.
**kwargs : dict, optional
Additional keyword arguments.
"""
raise TypeError("'none' backend figures can't be saved.")
def get_figsize(plot_collection):
"""Get the size of the :term:`figure` element and its units."""
figure_element = plot_collection.viz["figure"].item()
return figure_element["figsize"], figure_element["figsize_units"]
[docs]
def create_plotting_grid(
number,
rows=1,
cols=1,
*,
figsize=None,
figsize_units="inches",
squeeze=True,
sharex=False,
sharey=False,
polar=False,
width_ratios=None,
height_ratios=None,
plot_hspace=None,
subplot_kws=None,
**kwargs,
):
"""Create a :term:`figure` with a grid of :term:`plots` in it.
Parameters
----------
number : int
Number of plots required
rows, cols : int, default 1
Number of rows and columns.
figsize : tuple of (float, float), optional
Size of the figure in `figsize_units`.
figsize_units : {"inches", "dots"}, default "inches"
Units in which `figsize` is given.
squeeze : bool, default True
Delete dimensions of size 1 in the resulting array of :term:`plots`
sharex, sharey : bool, default False
Flags that indicate the axis limits between the different plots should
be shared.
polar : bool, default False
width_ratios : array_like of shape (cols,), optional
plot_hspace : float, optional
subplot_kws, **kwargs : mapping, optional
Arguments passed downstream to the plotting backend.
Returns
-------
figure : False
plots : [] or ndarray of []
"""
plots = np.empty((rows, cols), dtype=object)
for i, idx in enumerate(np.ndindex((rows, cols))):
plots[idx] = None if i + 1 > number else []
if squeeze:
plots = plots.squeeze()
if not ALLOW_KWARGS:
if subplot_kws:
raise ValueError("'subplot_kws' is not empty")
if kwargs:
raise ValueError("kwargs are not empty")
figure_element = {
"figsize": figsize,
"figsize_units": figsize_units,
"sharex": sharex,
"sharey": sharey,
"polar": polar,
"width_ratios": width_ratios,
"height_ratios": height_ratios,
"plot_hspace": plot_hspace,
"subplot_kws": subplot_kws,
**kwargs,
}
return np.array(figure_element, dtype=object), plots
def _filter_kwargs(kwargs, artist_kws):
"""Filter a dictionary to remove all keys whose values are ``unset``."""
kwargs = {key: value for key, value in kwargs.items() if value is not unset}
return {**artist_kws, **kwargs}
def hist(
y,
l_e,
r_e,
target,
*,
bottom=0,
color=unset,
facecolor=unset,
edgecolor=unset,
alpha=unset,
**artist_kws,
):
"""Interface to matplotlib for a histogram bar plot."""
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
if color is not unset:
if facecolor is unset:
facecolor = color
if edgecolor is unset:
edgecolor = color
kwargs = {"bottom": bottom, "facecolor": facecolor, "edgecolor": edgecolor}
artist_element = {
"function": "hist",
"l_e": np.atleast_1d(l_e),
"r_e": np.atleast_1d(r_e),
"y": np.atleast_1d(y),
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
# "geoms"
[docs]
def line(x, y, target, *, color=unset, alpha=unset, width=unset, linestyle=unset, **artist_kws):
"""Interface to a line plot."""
kwargs = {"color": color, "alpha": alpha, "width": width, "linestyle": linestyle}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "line",
"x": np.atleast_1d(x),
"y": np.atleast_1d(y),
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
[docs]
def multiple_lines(
x, y, target, *, color=unset, alpha=unset, width=unset, linestyle=unset, **artist_kws
):
"""Interface to multiple lines.
Parameters
----------
x : (N,) array-like
Shared data for the x axis
y : (N, M) array-like
Data for the y values of the multiple lines
target : list[Any]
color, alpha, width, linestyle : Any, optional
See {ref}`backend_interface_arguments` for their description
**artist_kws
Extra keyword arguments. For the none backend they are stored as is
to allow for rendering of the plot later on.
Returns
-------
dict
dictionary with the provided arguments and a "function" key to identify
the backend function.
"""
kwargs = {"color": color, "alpha": alpha, "width": width, "linestyle": linestyle}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "multiple_lines",
"x": x,
"y": y,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
[docs]
def scatter(
x,
y,
target,
*,
size=unset,
marker=unset,
alpha=unset,
color=unset,
facecolor=unset,
edgecolor=unset,
width=unset,
**artist_kws,
):
"""Interface to a scatter plot."""
if color is not unset:
if facecolor is unset and edgecolor is unset:
facecolor = color
edgecolor = color
elif facecolor is unset:
facecolor = color
elif edgecolor is unset:
edgecolor = color
kwargs = {
"size": size,
"marker": marker,
"alpha": alpha,
"facecolor": facecolor,
"edgecolor": edgecolor,
"width": width,
}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "scatter",
"x": np.atleast_1d(x),
"y": np.atleast_1d(y),
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def step(
x,
y,
target,
*,
color=unset,
alpha=unset,
width=unset,
linestyle=unset,
step_mode=unset,
**artist_kws,
):
"""Interface to a step line."""
kwargs = {
"color": color,
"alpha": alpha,
"width": width,
"linestyle": linestyle,
"step_mode": step_mode,
}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "step",
"x": np.atleast_1d(x),
"y": np.atleast_1d(y),
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
[docs]
def text(
x,
y,
string,
target,
*,
size=unset,
alpha=unset,
color=unset,
vertical_align=unset,
horizontal_align=unset,
**artist_kws,
):
"""Interface to text annotation inside a plot."""
kwargs = {
"size": size,
"alpha": alpha,
"color": color,
"vertical_align": vertical_align,
"horizontal_align": horizontal_align,
}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "text",
"x": np.atleast_1d(x),
"y": np.atleast_1d(y),
"string": string,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def fill_between_y(x, y_bottom, y_top, target, *, color=unset, alpha=unset, **artist_kws):
"""Fill the region between y_bottom and y_top."""
x = np.atleast_1d(x)
y_bottom = np.atleast_1d(y_bottom)
if y_bottom.size == 1:
y_bottom = y_bottom.item()
y_top = np.atleast_1d(y_top)
if y_top.size == 1:
y_top = y_top.item()
kwargs = {"color": color, "alpha": alpha}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "fill_between_y",
"x": x,
"y_bottom": y_bottom,
"y_top": y_top,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def vline(x, target, *, color=unset, alpha=unset, width=unset, linestyle=unset, **artist_kws):
"""Interface to a vertical line spanning the whole axes."""
kwargs = {"color": color, "alpha": alpha, "width": width, "linestyle": linestyle}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "vline",
"x": x,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def hline(y, target, *, color=unset, alpha=unset, width=unset, linestyle=unset, **artist_kws):
"""Interface to a horizontal line spanning the whole axes."""
kwargs = {"color": color, "alpha": alpha, "width": width, "linestyle": linestyle}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "vline",
"y": y,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def vspan(xmin, xmax, target, *, color=unset, alpha=unset, **artist_kws):
"""Interface to a vertical shaded region spanning the whole axes."""
kwargs = {"color": color, "alpha": alpha}
if not ALLOW_KWARGS and artist_kws:
raise ValueError("artist_kws not empty")
artist_element = {
"function": "vspan",
"xmin": xmin,
"xmax": xmax,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def hspan(ymin, ymax, target, *, color=unset, alpha=unset, **artist_kws):
"""Interface to a horizontal shaded region spanning the whole axes."""
kwargs = {"color": color, "alpha": alpha}
if not ALLOW_KWARGS and artist_kws:
raise ValueError("artist_kws not empty")
artist_element = {
"function": "hspan",
"ymin": ymin,
"ymax": ymax,
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
def ciliney(
x,
y_bottom,
y_top,
target,
*,
color=unset,
alpha=unset,
width=unset,
linestyle=unset,
**artist_kws,
):
"""Interface to a line from y_bottom to y_top at given value of x."""
kwargs = {"color": color, "alpha": alpha, "width": width, "linestyle": linestyle}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "ciliney",
"x": np.atleast_1d(x),
"y_bottom": np.atleast_1d(y_bottom),
"y_top": np.atleast_1d(y_top),
**_filter_kwargs(kwargs, artist_kws),
}
target.append(artist_element)
return artist_element
# general plot appeareance
[docs]
def title(string, target, *, size=unset, color=unset, **artist_kws):
"""Interface to adding a title to a plot."""
kwargs = {"color": color, "size": size}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {"function": "title", "string": string, **_filter_kwargs(kwargs, artist_kws)}
target.append(artist_element)
return artist_element
[docs]
def ylabel(string, target, *, size=unset, color=unset, **artist_kws):
"""Interface to adding a label to a plot's y axis."""
kwargs = {"color": color, "size": size}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {"function": "ylabel", "string": string, **_filter_kwargs(kwargs, artist_kws)}
target.append(artist_element)
return artist_element
[docs]
def xlabel(string, target, *, size=unset, color=unset, **artist_kws):
"""Interface to adding a label to a plot's x axis."""
kwargs = {"color": color, "size": size}
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {"function": "xlabel", "string": string, **_filter_kwargs(kwargs, artist_kws)}
target.append(artist_element)
return artist_element
[docs]
def xticks(ticks, labels, target, *, rotation=unset, **artist_kws):
"""Interface to setting ticks and tick labels of the x axis."""
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "xticks",
"ticks": ticks,
"labels": labels,
"rotation": rotation,
**artist_kws,
}
target.append(artist_element)
return artist_element
[docs]
def yticks(ticks, labels, target, *, rotation=unset, **artist_kws):
"""Interface to setting ticks and tick labels of the y axis."""
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {
"function": "yticks",
"ticks": ticks,
"labels": labels,
"rotation": rotation,
**artist_kws,
}
target.append(artist_element)
return artist_element
def set_ticklabel_visibility(target, *, axis="both", visible=True):
"""Interface to setting visibility of tick labels."""
artist_element = {
"function": "set_ticklabel_visibility",
"axis": axis,
"visible": visible,
}
target.append(artist_element)
return artist_element
def xlim(lims, target, **artist_kws):
"""Interface to setting limits for the x axis."""
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {"function": "xlim", "lims": lims, **artist_kws}
target.append(artist_element)
return artist_element
def ylim(lims, target, **artist_kws):
"""Interface to setting limits for the y axis."""
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
artist_element = {"function": "ylim", "lims": lims, **artist_kws}
target.append(artist_element)
return artist_element
[docs]
def ticklabel_props(target, *, axis="both", size=unset, color=unset, **artist_kws):
"""Interface to setting size of tick labels."""
if not ALLOW_KWARGS and artist_kws:
raise ValueError(f"artist_kws not empty: {artist_kws}")
kwargs = {"axis": axis, "size": size, "color": color}
artist_element = {"function": "ticklabel_props", **_filter_kwargs(kwargs, artist_kws)}
target.append(artist_element)
return artist_element
[docs]
def remove_ticks(target, *, axis="y"):
"""Interface to removing ticks from a plot."""
artist_element = {
"function": "remove_ticks",
"axis": axis,
}
target.append(artist_element)
return artist_element
[docs]
def remove_axis(target, axis="y"):
"""Interface to removing axis from a plot."""
artist_element = {
"function": "remove_axis",
"axis": axis,
}
target.append(artist_element)
return artist_element
def set_y_scale(target, scale):
"""Interface to setting the y scale of a plot."""
artist_element = {
"function": "set_y_scale",
"scale": scale,
}
target.append(artist_element)
return artist_element
def grid(target, axis, color):
"""Interface to setting a grid in any axis."""
artist_element = {
"function": "grid",
"axis": axis,
"color": color,
}
target.append(artist_element)
return artist_element