Source code for smpl.plot.plot

from turtle import color
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import uncertainties
import uncertainties.unumpy as unp
import sympy
import matplotlib.pylab as pylab
import itertools
# local imports
from smpl import interpolate
from smpl import io
from smpl import util
from smpl import wrap
from smpl import doc
from smpl import fit as ffit


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",
    'xlabel': ["", "X axis label", ],
    'ylabel': ["", "Y axis label", ],
    'label': [None, "Legend name of plotted ``data``", ],
    'fmt': ['.', "Format for plotting fit function", ],
    '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", ],
    '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.", ],
    '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."]
}


[docs]@doc.append_doc(ffit.fit_kwargs) @doc.append_str("\t") @doc.append_str(doc.table(default, init=False)) @doc.append_str(doc.table({"plot_kwargs ": ["default", "description"]}, bottom=False)) def plot_kwargs(kwargs): """Set default 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
# @append_doc(default_kwargs)
[docs]def auto(datax, datay, 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. """ best_f, best_ff, lambda_f = ffit.auto(datax, datay, funcs, **kwargs) if not best_f is None: fit(datax, datay, best_f, **kwargs) return best_f, best_ff, lambda_f
# @append_doc(default_kwargs)
[docs]def fit(datax, datay, function, **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`` 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 """ kwargs = plot_kwargs(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']: 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 rfit
# @append_doc(default_kwargs)
[docs]def data(datax, datay, 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 not 'also_fit' in kwargs: kwargs['also_fit'] = False kwargs = plot_kwargs(kwargs) if kwargs['label'] == None and kwargs['lpos'] == 0: kwargs['lpos'] = -1 return fit(datax, datay, function, **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'] __function(func, xfit, **kargs) 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., linestyle=None): 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=0.4, 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("xmin", kwargs): 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 not "label" 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): def plt_residue(datax, datay, gfunction, rfit, fig, **kwargs): function = wrap.get_lambda(gfunction, kwargs['xvar']) fig.add_axes((.1, .1, .8, .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) 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) 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='mid', label=kwargs['label'], color=kwargs['data_color']) elif kwargs['fmt'] == "hist": ll, = plt.step(x, y, where='mid', 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='mid', 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=0.2, step='pre', color=ll.get_color()) else: plt.fill_between(x, y-yerr, y+yerr, label=kwargs['label'], alpha=0.2, 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 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 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) 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']) __function(fitted, kwargs['xspace'](np.min(xxfit), np.min(xfit), kwargs['steps']), util.get("extrapolate_fmt", kwargs, "--"), color=ll.get_color(), hatch=util.get("extrapolate_hatch", kwargs, r"||"), sigmas=kwargs['sigmas']) __function(fitted, kwargs['xspace'](np.max(xfit), np.max(xxfit), kwargs['steps']), util.get("extrapolate_fmt", kwargs, "--"), color=ll.get_color(), hatch=util.get("extrapolate_hatch", kwargs, r"||"), sigmas=kwargs['sigmas']) return ll.get_color(), xfit, fitted(xfit) 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"] # l = None so that no label return (inter, *plt_fit_or_interpolate(datax, datay, inter, l=None, c=icolor, **kargs, **kwargs)) 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)) 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((.1, .3, .8, .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 def save_plot(**kwargs): """ save plot """ 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'] == None: io.mkdirs(kwargs['save']) plt.savefig(kwargs['save'] + ".pdf") plt.grid(b=kwargs["grid"]) if 'show' in kwargs and kwargs['show']: show(**kwargs) def show(**kwargs): kwargs = plot_kwargs(kwargs) plt.grid(b=kwargs["grid"]) plt.show() if __name__ == "__main__": import doctest doctest.testmod()