"""Simplified plotting."""
import itertools
import matplotlib
import matplotlib.pylab as pylab
import matplotlib.pyplot as plt
import numpy as np
import sympy
import uncertainties
import uncertainties.unumpy as unp
from matplotlib.pyplot import *
# local imports
from smpl import doc
from smpl import fit as ffit
from smpl import interpolate, io, stat, util, wrap
[docs]def set_plot_style():
# fig_size = (8, 6)
# fig_legendsize = 14
# fig_labelsize = 12 # ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’.
params = {
"legend.fontsize": "x-large",
"figure.figsize": (8, 6),
"axes.labelsize": "x-large",
"axes.titlesize": "x-large",
"xtick.labelsize": "x-large",
"ytick.labelsize": "x-large",
}
pylab.rcParams.update(params)
matplotlib.rcParams.update(params)
# matplotlib.rcParams.update({'font.size': fig_labelsize})
# colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
unv = unp.nominal_values
usd = unp.std_devs
default = {
# 'params' :[None ,"Initial fit parameters",
"title": [None, "Plot title"],
"xlabel": [
"",
"X axis label",
],
"ylabel": [
"",
"Y axis label",
],
"label": [
None,
"Legend name of plotted ``data``",
],
"fmt": [
".",
"Format for plotting fit function (could be a line style, marker or a combination of both or 'step')",
],
"where": [
"mid",
"Where to place the ticks. Possible values are 'pre', 'post', 'mid', 'edge'. Might be incompatible with uncertainties.",
],
"units": [
None,
"Units of the fit parameters as strings. Displayed in the Legend",
],
"save": [
None,
" File to save the plot",
],
"lpos": [
0,
"Legend position",
],
"tight": [
True,
"tight_layout",
],
# 'frange' :[None ,"Limit the fit to given range. First integer is the lowest and second the highest index.",],
"prange": [
None,
"Limit the plot of the fit to given range",
],
"sigmas": [
0,
"Color the array of given ``sigma`` times uncertainty. Only works if the fit function is coded with ``unp``",
],
"data_sigmas": [
1,
"Color the array of given ``sigma`` times uncertainty. Only works if the data has uncertainties",
],
"init": [False, "Initialize a new plot"],
"ss": [
True,
"save, add legends and grid to the plot",
],
"also_data": [True, " also plot the data"],
"also_fit": [
True,
"also plot the fit",
],
"auto_fit": [
False,
"automatically fit",
],
"logy": [
False,
"logarithmic x axis",
],
"logx": [
False,
"logarithmic y axis",
],
"function_color": [
None,
"Color of the function plot",
],
"data_color": [
None,
"Color of the data plot",
],
"fit_color": [
None,
"Color of the fit plot",
],
"fit_fmt": [
"-",
"Format of the fit plot",
],
"residue": [
False,
"Display difference between fit and data in a second plot",
],
"residue_err": [
True,
"Differences between fit and data will have errorbars",
],
"show": [
False,
"Call plt.show()",
],
"size": [
None,
"Size of the plot as a tuple (x,y). Only has an effect if ``init`` is True",
],
"number_format": [
io.gf(4),
"Format to display numbers.",
],
# , 'selector' :[ None ,"Function that takes ``x`` and ``y`` as parameters and returns an array mask in order to limit the data points for fitting. Alternatively a mask for selecting elements from datax and datay.",],
# , 'fixed_params' :[ True ,"Enable fixing parameters by choosing the same-named variables from ``kwargs``.",],
# , 'sortbyx' :[ True , "Enable sorting the x and y data so that x is sorted.",],
"interpolate": [False, "Enable interpolation of the data."],
"interpolate_fmt": [
"-",
"Either format string or linestyle tuple.",
],
"interpolate_label": [
None,
"Label for the interpolation.",
],
"extrapolate": [
True,
"Enable extrapolation of whole data if fit range is limited by ``frange`` or ``fselector``.",
],
"extrapolate_min": [
None,
"Lower extrapolation bound",
],
"extrapolate_max": [
None,
"Higher extrapolation bound",
],
"extrapolate_fmt": [
"--",
"Format of the extrapolation line",
],
"extrapolate_hatch": [
r"||",
"Extrapolation shape/hatch for filled area in case of ``sigmas``>0. See https://matplotlib.org/stable/gallery/shapes_and_collections/hatch_style_reference.html",
],
"bbox_to_anchor": [
None,
"Position in a tuple (x,y),Shift position of the legend out of the main pane. ",
],
"ncol": [
None,
"Columns in the legend if used with ``bbox_to_anchor``.",
],
"steps": [
1000,
"resolution of the plotted function",
],
"fitinline": [
False,
"No newlines for each fit parameter",
],
"grid": [
True,
"Enable grid for the plot",
],
"hist": [
False,
"Enable histogram plot",
],
"stairs": [
False,
"Enable stair plot",
],
"capsize": [5, "size of cap on error bar plot"],
"axes": [None, "set current axis"],
"linestyle": [None, "linestyle, only active if `fmt`=None"],
"xspace": [
np.linspace,
"xspace gets called with xspace(xmin,xmax,steps) in :func:`function` to get the points of the function that will be drawn.",
],
"alpha": [0.2, "alpha value for the fill_between plot"],
}
[docs]@doc.append_doc(ffit.fit_kwargs)
@doc.append_str("\t")
@doc.append_str(doc.array_table(default, init=False))
@doc.append_str(
doc.array_table({"plot_kwargs ": ["default", "description"]}, bottom=False)
)
@doc.append_str("\n\n")
def plot_kwargs(kwargs):
"""Set default :func:`plot_kwargs` if not set."""
kwargs = ffit.fit_kwargs(kwargs)
for k, v in default.items():
if not k in kwargs:
kwargs[k] = v[0]
return kwargs
# TODO optimize to minimize number of calls to function
# @append_doc(default_kwargs)
[docs]def fit(func, *adata, **kwargs):
"""
Fit and plot function to datax and datay.
Parameters
----------
datax : array_like
X data either as ``unp.uarray`` or ``np.array`` or ``list``
datay : array_like
Y data either as ``unp.uarray`` or ``np.array`` or ``list``
function : func
Fit function with parameters: ``x``, ``params``
**kwargs : optional
see :func:`plot_kwargs`.
Fit parameters can be fixed via ``kwargs`` eg. ``a=5``.
Returns
-------
array_like
Optimized fit parameters of ``function`` to ``datax`` and ``datay``.
If ``datay`` is complex, both the real and imaginary part are returned.
Examples
--------
.. plot::
:include-source:
>>> from smpl import functions as f
>>> from smpl import plot
>>> param = plot.fit([0,1,2],[0,1,2],f.line)
>>> plot.unv(param).round()[0]
1.0
"""
function = func
if "function" in kwargs:
function = kwargs["function"]
del kwargs["function"]
adata = [func, *adata]
# Fix parameter order if necessary
elif isinstance(function, (list, tuple, np.ndarray)):
adata = [adata[-1], function, *adata[:-1]]
function = adata[0]
adata = adata[1:]
if util.true("bins", kwargs):
# yvalue will be overwritten
ndata = [*adata, *adata]
for i, o in enumerate(adata):
ndata[2 * i] = o
ndata[2 * i + 1] = o * 0
adata = ndata
assert len(adata) % 2 == 0, "data must be pairs of x and y data"
if len(adata) == 2:
datax, datay = adata
else:
rs = []
for i in range(0, len(adata), 2):
datax, datay = adata[i], adata[i + 1]
if util.true("bins", kwargs):
rs.append(fit(function, datax, **kwargs))
else:
rs.append(fit(function, datax, datay, **kwargs))
return rs
kwargs = plot_kwargs(kwargs)
if np.any(np.iscomplex(datay)):
label = util.get("label", kwargs, "")
kwargs["label"] = label + "(real)"
r = fit(datax, datay.real, function=function, **kwargs)
kwargs["label"] = label + "(imag)"
i = fit(datax, datay.imag, function=function, **kwargs)
return r, i
if kwargs["auto_fit"]:
best_f, best_ff, lambda_f = ffit.auto(datax, datay, function, **kwargs)
if best_f is not None:
del kwargs["auto_fit"]
fit(datax, datay, best_f, **kwargs)
return best_f, best_ff, lambda_f
if kwargs["also_fit"] == False and kwargs["label"] is None and kwargs["lpos"] == 0:
kwargs["lpos"] = -1
return _fit_impl(datax, datay, function, **kwargs)
def _fit_impl(datax, datay, function, **kwargs):
x = None
y = None
rfit = None
ifit = None
fig = None
fig = init_plot(kwargs)
ll = None
if kwargs["also_data"]:
ll = plt_data(datax, datay, **kwargs).get_color()
if kwargs["interpolate"]:
ifit, _, x, y = plt_interpolate(datax, datay, icolor=ll, **kwargs)
if kwargs["also_fit"]:
assert function is not None, "function must be given"
rfit, kwargs["fit_color"], _, _ = plt_fit(datax, datay, function, **kwargs)
if kwargs["ss"]:
kwargs["oldshow"] = kwargs["show"]
kwargs["show"] = kwargs["show"] and not kwargs["residue"]
save_plot(**kwargs)
kwargs["show"] = kwargs["oldshow"]
if kwargs["residue"] and fig is not None:
plt_residue(datax, datay, function, rfit, fig, **kwargs)
if not kwargs["also_fit"] and kwargs["interpolate"]:
return (ifit, x, y)
# return ifit
return rfit
# @append_doc(default_kwargs)
[docs]def data(*data, function=None, **kwargs):
"""
Plot datay against datax via :func:`fit`
Parameters
----------
datax : array_like
X data either as ``unp.uarray`` or ``np.array`` or ``list``
datay : array_like
Y data either as ``unp.uarray`` or ``np.array`` or ``list``
function : func,optional
Fit function with parameters: ``x``, ``params``
**kwargs : optional
see :func:`plot_kwargs`.
Returns
-------
array_like
Optimized fit parameters of ``function`` to ``datax`` and ``datay``
"""
if "also_fit" not in kwargs:
kwargs["also_fit"] = False
kwargs = plot_kwargs(kwargs)
return fit(function=function, *data, **kwargs)
# @append_doc(default_kwargs)
[docs]def auto(*adata, funcs=None, **kwargs):
"""
Automatically loop over functions and fit the best one.
Parameters
----------
funcs : function array
functions to consider as fit. Default all ``smpl.functions``.
**kwargs : optional
see :func:`plot_kwargs`.
Returns
-------
The best fit function and it's parameters. Also a lambda function where the parameters are already applied.
"""
if "auto_fit" not in kwargs:
kwargs["auto_fit"] = True
kwargs = plot_kwargs(kwargs)
return fit(function=funcs, *adata, **kwargs)
def _function(func, xfit, **kwargs):
kargs = {}
if util.has("fmt", kwargs):
kargs["fmt"] = kwargs["fmt"]
if util.has("label", kwargs) and kwargs["label"] != "":
kargs["label"] = kwargs["label"]
if util.has("function_color", kwargs) and kwargs["function_color"] != "":
kargs["color"] = kwargs["function_color"]
if util.has("sigmas", kwargs) and kwargs["sigmas"] != "":
kargs["sigmas"] = kwargs["sigmas"]
if util.has("alpha", kwargs) and kwargs["alpha"] != "":
kargs["alpha"] = kwargs["alpha"]
__function(func, xfit, **kargs)
[docs]def plt_plt(x, y, fmt, color, label, linestyle):
if linestyle is None and fmt is not None:
return plt.plot(x, y, fmt, label=label, color=color)
elif linestyle is not None and fmt is None:
return plt.plot(x, y, label=label, color=color, linestyle=linestyle)
elif linestyle is None and fmt is None:
return plt.plot(x, y, label=label, color=color)
def __function(
gfunc,
xlinspace,
fmt="-",
label=None,
color=None,
hatch=None,
sigmas=0.0,
linestyle=None,
alpha=0.4,
):
func = gfunc
x = xlinspace
l = label
if isinstance(func(x)[0], uncertainties.UFloat):
if sigmas > 0:
(ll,) = plt_plt(
x, unv(func(x)), fmt, label=None, color=color, linestyle=linestyle
)
y = func(x)
plt.fill_between(
x,
unv(y) - sigmas * usd(y),
unv(y) + sigmas * usd(y),
alpha=alpha,
label=l,
color=ll.get_color(),
hatch=hatch,
)
else:
(ll,) = plt_plt(
x, unv(func(x)), fmt, label=l, color=color, linestyle=linestyle
)
else:
(ll,) = plt_plt(x, func(x), fmt, label=l, color=color, linestyle=linestyle)
return ll
[docs]def function(func, *args, **kwargs):
"""
Plot function ``func`` between ``xmin`` and ``xmax``
Parameters
----------
func : function
Function to be plotted between ``xmin`` and ``xmax``, only taking `array_like` ``x`` as parameter
*args : optional
arguments for ``func``
**kwargs : optional
see :func:`plot_kwargs`.
"""
if not util.has("xmin", kwargs) or not util.has("xmax", kwargs):
kwargs["xmin"], kwargs["xmax"] = stat.get_interesting_domain(func)
# raise Exception("xmin or xmax missing.")
# if not util.has('lpos', kwargs) and not util.has('label', kwargs):
# kwargs['lpos'] = -1
if not util.has("fmt", kwargs):
kwargs["fmt"] = "-"
if "label" not in kwargs:
kwargs = plot_kwargs(kwargs)
kwargs["label"] = get_fnc_legend(func, args, **kwargs)
else:
kwargs = plot_kwargs(kwargs)
xlin = kwargs["xspace"](kwargs["xmin"], kwargs["xmax"], kwargs["steps"])
init_plot(kwargs)
# kwargs['lpos'] = 0
# _plot(xfit, func(xfit, *args), **kwargs)
_function(wrap.get_lambda_argd(func, kwargs["xvar"], *args), xlin, **kwargs)
if kwargs["ss"]:
save_plot(**kwargs)
# xaxis="",yaxis="",fit_color=None,save = None,residue_err=True,show=False):
[docs]def plt_residue(datax, datay, gfunction, rfit, fig, **kwargs):
function = wrap.get_lambda(gfunction, kwargs["xvar"])
fig.add_axes((0.1, 0.1, 0.8, 0.2))
kwargs["yaxis"] = "$\\Delta$" + kwargs["yaxis"]
kwargs["data_color"] = kwargs["fit_color"]
if kwargs["residue_err"]:
plt_data(datax, datay - function(datax, *rfit), **kwargs)
else:
plt_data(unv(datax), unv(datay - function(datax, *rfit)), **kwargs)
kwargs["lpos"] = -1
save_plot(**kwargs)
[docs]def data_split(datax, datay, **kwargs):
return ffit.data_split(datax, datay, **kwargs)
def _fit(datax, datay, function, **kwargs):
"""
Returns a fit like :func:`fit` but does no plotting.
"""
return ffit.fit(datax, datay, function, **kwargs)
[docs]def plt_data(datax, datay, **kwargs):
"""
Plot datay vs datax
"""
x, y, xerr, yerr = data_split(datax, datay, **kwargs)
if xerr is not None:
xerr = xerr * kwargs["data_sigmas"]
if yerr is not None:
yerr = yerr * kwargs["data_sigmas"]
ll = None
if xerr is None and yerr is None:
if kwargs["fmt"] is None:
if kwargs["linestyle"] is None:
(ll,) = plt.plot(
x, y, label=kwargs["label"], color=kwargs["data_color"]
)
else:
(ll,) = plt.plot(
x,
y,
label=kwargs["label"],
color=kwargs["data_color"],
linestyle=kwargs["linestyle"],
)
elif kwargs["fmt"] == "step":
(ll,) = plt.step(
x,
y,
where=kwargs["where"],
label=kwargs["label"],
color=kwargs["data_color"],
)
elif kwargs["fmt"] == "hist":
(ll,) = plt.step(
x,
y,
where=kwargs["where"],
label=kwargs["label"],
color=kwargs["data_color"],
)
plt.fill_between(x, y, step="mid", color=ll.get_color())
else:
(ll,) = plt.plot(
x, y, kwargs["fmt"], label=kwargs["label"], color=kwargs["data_color"]
)
else:
if kwargs["fmt"] is None:
if kwargs["linestyle"] is None:
(
ll,
_,
_,
) = plt.errorbar(
x,
y,
yerr=yerr,
xerr=xerr,
fmt=" ",
capsize=kwargs["capsize"],
label=kwargs["label"],
color=kwargs["data_color"],
)
else:
(
ll,
_,
_,
) = plt.errorbar(
x,
y,
yerr=yerr,
xerr=xerr,
fmt=" ",
capsize=kwargs["capsize"],
label=kwargs["label"],
color=kwargs["data_color"],
linestyle=kwargs["linestyle"],
)
elif kwargs["fmt"] == "step":
(ll,) = plt.step(x, y, where=kwargs["where"], color=kwargs["data_color"])
if xerr is not None:
for ix, xv in enumerate(x):
dx = xerr[ix]
tx = [xv - dx, xv + dx]
plt.fill_between(
tx,
y[ix] - yerr[ix],
y[ix] + yerr[ix],
label=kwargs["label"] if ix == 1 else None,
alpha=kwargs["alpha"],
step="pre",
color=ll.get_color(),
)
else:
plt.fill_between(
x,
y - yerr,
y + yerr,
label=kwargs["label"],
alpha=kwargs["alpha"],
step="mid",
color=ll.get_color(),
)
elif kwargs["fmt"] == "hist":
(
ll,
_,
_,
) = plt.errorbar(
x,
y,
yerr=yerr,
xerr=xerr,
fmt=" ",
capsize=kwargs["capsize"],
color="black",
)
plt.fill_between(
x, y, step="mid", label=kwargs["label"], color=ll.get_color()
)
else:
(
ll,
_,
_,
) = plt.errorbar(
x,
y,
yerr=yerr,
xerr=xerr,
fmt=kwargs["fmt"],
capsize=kwargs["capsize"],
label=kwargs["label"],
color=kwargs["data_color"],
)
return ll
[docs]def get_fnc_legend(function, rfit, **kwargs):
l = wrap.get_latex(function)
vnames = wrap.get_varnames(function, kwargs["xvar"])
for i in range(1, len(vnames)):
l = l + ("\n" if not kwargs["fitinline"] or i == 1 else " ")
l = l + "$" + sympy.latex(sympy.symbols(str(vnames[i]))) + "$="
if kwargs["units"] is not None and usd(rfit[i - 1]) > 0:
l = l + "("
if "number_format" in kwargs:
l = l + kwargs["number_format"].format(rfit[i - 1])
else:
l = l + "%s" % (rfit[i - 1])
if kwargs["units"] is not None and usd(rfit[i - 1]) > 0:
l = l + ")"
if kwargs["units"] is not None:
l = l + " " + kwargs["units"][i - 1]
return l
[docs]def plt_fit_or_interpolate(
datax, datay, fitted, l=None, c=None, f=None, ls=None, **kwargs
):
if kwargs["prange"] is None:
x, _, _, _ = ffit.fit_split(datax, datay, **kwargs)
xfit = kwargs["xspace"](np.min(unv(x)), np.max(unv(x)), kwargs["steps"])
else:
xfit = kwargs["xspace"](
kwargs["prange"][0], kwargs["prange"][1], kwargs["steps"]
)
ll = __function(
fitted,
xfit,
kwargs["fit_fmt"] if f is not None and ls is None else f,
label=l,
color=kwargs["fit_color"] if c is None else c,
sigmas=kwargs["sigmas"],
linestyle=ls,
alpha=kwargs["alpha"],
)
if (
(kwargs["frange"] is not None or kwargs["fselector"] is not None)
and util.true("extrapolate", kwargs)
or util.has("extrapolate_max", kwargs)
or util.has("extrapolate_min", kwargs)
):
xxfit = kwargs["xspace"](
util.get("extrapolate_min", kwargs, np.min(unv(datax))),
util.get("extrapolate_max", kwargs, np.max(unv(datax))),
kwargs["steps"],
)
for pmin, pmax in [
(np.min(xxfit), np.min(xfit)),
(np.max(xfit), np.max(xxfit)),
]:
__function(
fitted,
kwargs["xspace"](pmin, pmax, kwargs["steps"]),
util.get("extrapolate_fmt", kwargs, "--"),
color=ll.get_color(),
hatch=util.get("extrapolate_hatch", kwargs, r"||"),
sigmas=kwargs["sigmas"],
alpha=kwargs["alpha"],
)
return ll.get_color(), xfit, fitted(xfit)
[docs]def plt_interpolate(datax, datay, icolor=None, **kwargs):
"""
Interpolate and Plot that Interpolation.
"""
inter = interpolate.interpolate(datax, datay, **kwargs)
kargs = {}
if isinstance(kwargs["interpolate_fmt"], tuple):
kargs["ls"] = kwargs["interpolate_fmt"]
else:
kargs["f"] = kwargs["interpolate_fmt"]
if kwargs["interpolate_label"] is not None:
kargs["l"] = kwargs["interpolate_label"]
# l = None so that no label
return (
inter,
*plt_fit_or_interpolate(datax, datay, inter, c=icolor, **kargs, **kwargs),
)
[docs]def plt_fit(datax, datay, gfunction, **kwargs):
"""
Fit and Plot that Fit.
"""
func = wrap.get_lambda(gfunction, kwargs["xvar"])
rfit = _fit(datax, datay, gfunction, **kwargs)
def fitted(x):
return func(x, *rfit)
l = get_fnc_legend(gfunction, rfit, **kwargs)
return (rfit, *plt_fit_or_interpolate(datax, datay, fitted, l, **kwargs))
[docs]def init_plot(kwargs):
fig = None
if util.has("axes", kwargs) and kwargs["axes"] is not None:
plt.sca(kwargs["axes"])
fig = kwargs["axes"].get_figure()
if kwargs["init"] or util.true("residue", kwargs):
if kwargs["size"] is None:
fig = plt.figure()
else:
fig = plt.figure(figsize=kwargs["size"])
if kwargs["residue"]:
fig.add_axes((0.1, 0.3, 0.8, 0.6))
if util.has("xlabel", kwargs) and kwargs["xlabel"] != "":
plt.xlabel(kwargs["xlabel"])
if util.has("ylabel", kwargs) and kwargs["ylabel"] != "":
plt.ylabel(kwargs["ylabel"])
if util.has("xaxis", kwargs) and kwargs["xaxis"] != "":
plt.xlabel(kwargs["xaxis"])
if util.has("yaxis", kwargs) and kwargs["yaxis"] != "":
plt.ylabel(kwargs["yaxis"])
if util.has("next_color", kwargs) and not kwargs["next_color"]:
it1, it2 = itertools.tee(iter(plt.gca()._get_lines.prop_cycler))
plt.gca()._get_lines.prop_cycler = it2
tmp_color = next(it1)["color"]
if kwargs["data_color"] is None:
kwargs["data_color"] = tmp_color
if kwargs["fit_color"] is None:
kwargs["fit_color"] = tmp_color
if kwargs["function_color"] is None:
kwargs["function_color"] = tmp_color
return fig
[docs]def save_plot(**kwargs):
"""
save plot
"""
if "title" in kwargs and kwargs["title"] is not None:
plt.title(kwargs["title"])
if "logy" in kwargs and kwargs["logy"]:
plt.gca().set_yscale("log")
if "logx" in kwargs and kwargs["logx"]:
plt.gca().set_xscale("log")
if "tight" in kwargs and kwargs["tight"]:
plt.tight_layout()
if "lpos" in kwargs and kwargs["lpos"] >= 0:
if util.has("bbox_to_anchor", kwargs):
if util.has("ncol", kwargs):
plt.legend(
loc=kwargs["lpos"],
bbox_to_anchor=kwargs["bbox_to_anchor"],
ncol=kwargs["ncol"],
borderaxespad=0,
)
else:
plt.legend(loc=kwargs["lpos"], bbox_to_anchor=kwargs["bbox_to_anchor"])
else:
plt.legend(loc=kwargs["lpos"])
# plt.gca().set_xlim([kwargs['xmin'],kwargs['xmax']])
# plt.gca().set_ylim([kwargs['ymin'],kwargs['ymax']])
if "save" in kwargs and not kwargs["save"] is None:
io.mkdirs(kwargs["save"])
plt.savefig(kwargs["save"] + ".pdf")
plt.grid(kwargs["grid"])
if "show" in kwargs and kwargs["show"]:
show(**kwargs)
[docs]def show(**kwargs):
kwargs = plot_kwargs(kwargs)
plt.grid(kwargs["grid"])
plt.show()
from smpl.plot2d import plot2d as _plot2d
from smpl.plot2d import plot2d_kwargs as _plot2d_kwargs
[docs]def plot2d(*args, **kwargs):
_plot2d(*args, **kwargs)
[docs]def plot2d_kwargs(*args, **kwargs):
_plot2d_kwargs(*args, **kwargs)
if __name__ == "__main__":
import doctest
doctest.testmod()